diff --git a/docs/algo_trader.md b/docs/algo_trader.md index c411c451..736d2c19 100644 --- a/docs/algo_trader.md +++ b/docs/algo_trader.md @@ -9,7 +9,7 @@ - engine:定义了算法引擎,其中包括:引擎初始化、保存/移除/加载算法配置、启动算法、停止算法、订阅行情、挂撤单等。 - template:定义了交易算法模板,具体的算法示例,如冰山算法,都需要继承于该模板。 -- algos:官方提供的交易算法示例,包括:冰山算法、狙击手算法、时间加权平均算法、条件委托、最优限价。 +- algos:具体的交易算法示例。用户基于算法模板和官方提供是算法示例,可以自己搭建新的算法。 - ui:基于PyQt5的GUI图形应用。 ![](https://vnpy-community.oss-cn-shanghai.aliyuncs.com/forum_experience/yazhang/algo_trader/algo_trader_document.png) @@ -83,6 +83,37 @@ ## 算法示例 + +### 直接委托算法 + +直接发出新的委托(限价单、停止单、市价单) + +``` + def on_tick(self, tick: TickData): + """""" + if not self.vt_orderid: + if self.direction == Direction.LONG: + self.vt_orderid = self.buy( + self.vt_symbol, + self.price, + self.volume, + self.order_type, + self.offset + ) + + else: + self.vt_orderid = self.sell( + self.vt_symbol, + self.price, + self.volume, + self.order_type, + self.offset + ) + self.put_variables_event() +``` + +  + ### 时间加权平均算法 - 将委托数量平均分布在某个时间区域内; @@ -318,4 +349,161 @@ order_volume, offset=self.offset ) +``` + +  + +### 网格算法 + +- 每隔一段时间检查委托情况,若有委托则先清空。 +- 基于用户设置的价格步进(即网格)计算目标距离,目标距离=(目标价格- 当前价格)/价格步进,故当前价格低于目标价格,目标距离为正,方向为买入;当前价格高于目标价格,目标距离为负,方向为卖出。(高抛低吸概念) +- 计算目标仓位,目标仓位= 取整后的目标距离 * 委托数量步进。注意卖卖方向取整的方式是不同的:买入方向要向下取整math.floor(),如目标距离为1.6,取1;卖出方向要向上取整,如目标距离为-1.6,取-1。 +- 计算具体委托仓位:若目标买入仓位大于当前仓位,执行买入操作;若目标卖出仓位低于当前仓位,执行卖出操作。 +- 为了能够快速成交,买入情况是基于ask price计算,卖出情况是基于bid price计算。 + + +``` + def on_timer(self): + """""" + if not self.last_tick: + return + + self.timer_count += 1 + if self.timer_count < self.interval: + self.put_variables_event() + return + self.timer_count = 0 + + if self.vt_orderid: + self.cancel_all() + + # Calculate target volume to buy + target_buy_distance = (self.price - self.last_tick.ask_price_1) / self.step_price + target_buy_position = math.floor(target_buy_distance) * self.step_volume + target_buy_volume = target_buy_position - self.last_pos + + # Buy when price dropping + if target_buy_volume > 0: + self.vt_orderid = self.buy( + self.vt_symbol, + self.last_tick.ask_price_1, + min(target_buy_volume, self.last_tick.ask_volume_1) + ) + + # Calculate target volume to sell + target_sell_distance = (self.price - self.last_tick.bid_price_1) / self.step_price + target_sell_position = math.ceil(target_sell_distance) * self.step_volume + target_sell_volume = self.last_pos - target_sell_position + + # Sell when price rising + if target_sell_volume > 0: + self.vt_orderid = self.sell( + self.vt_symbol, + self.last_tick.bid_price_1, + min(target_sell_volume, self.last_tick.bid_volume_1) + ) +``` + +  + +### 套利算法 + +- 每隔一段时间检查委托情况,若有委托则先清空;若主动腿还持有净持仓,通过被动腿成交来对冲。 +- 计算价差spread_bid_price 和 spread_ask_price, 以及对应的委托数量 +- 卖出情况:主动腿价格相对被动腿上涨,其价差spread_bid_price大于spread_up时,触发买入信号 +- 买入情况:主动腿价格相对被动腿下跌,其价差spread_ask_price小于 - spread_down(spread_down默认设置为正数)时,触发卖出信号 +- 在买卖信号判断加入最大持仓的限制,其作用是避免持仓过多导致保证金不足或者直接被交易所惩罚性强平;而且随着价差持续波动,主动腿持仓可以从0 -> 10 -> 0 -> -10 -> 0,从而实现平仓获利离场。 + + +``` + def on_timer(self): + """""" + self.timer_count += 1 + if self.timer_count < self.interval: + self.put_variables_event() + return + self.timer_count = 0 + + if self.active_vt_orderid or self.passive_vt_orderid: + self.cancel_all() + return + + if self.net_pos: + self.hedge() + return + + active_tick = self.get_tick(self.active_vt_symbol) + passive_tick = self.get_tick(self.passive_vt_symbol) + if not active_tick or not passive_tick: + return + + # Calculate spread + spread_bid_price = active_tick.bid_price_1 - passive_tick.ask_price_1 + spread_ask_price = active_tick.ask_price_1 - passive_tick.bid_price_1 + + spread_bid_volume = min(active_tick.bid_volume_1, passive_tick.ask_volume_1) + spread_ask_volume = min(active_tick.ask_volume_1, passive_tick.bid_volume_1) + + # Sell condition + if spread_bid_price > self.spread_up: + if self.acum_pos <= -self.max_pos: + return + else: + self.active_vt_orderid = self.sell( + self.active_vt_symbol, + active_tick.bid_price_1, + spread_bid_volume + ) + + # Buy condition + elif spread_ask_price < -self.spread_down: + if self.acum_pos >= self.max_pos: + return + else: + self.active_vt_orderid = self.buy( + self.active_vt_symbol, + active_tick.ask_price_1, + spread_ask_volume + ) + self.put_variables_event() + + def hedge(self): + """""" + tick = self.get_tick(self.passive_vt_symbol) + volume = abs(self.net_pos) + + if self.net_pos > 0: + self.passive_vt_orderid = self.sell( + self.passive_vt_symbol, + tick.bid_price_5, + volume + ) + elif self.net_pos < 0: + self.passive_vt_orderid = self.buy( + self.passive_vt_symbol, + tick.ask_price_5, + volume + ) + + def on_trade(self, trade: TradeData): + """""" + # Update net position volume + if trade.direction == Direction.LONG: + self.net_pos += trade.volume + else: + self.net_pos -= trade.volume + + # Update active symbol position + if trade.vt_symbol == self.active_vt_symbol: + if trade.direction == Direction.LONG: + self.acum_pos += trade.volume + else: + self.acum_pos -= trade.volume + + # Hedge if active symbol traded + if trade.vt_symbol == self.active_vt_symbol: + self.hedge() + + self.put_variables_event() + ``` \ No newline at end of file