VNPY源码学习系列文章:
VNPY源码(一)CTP封装及K线合成
VNPY源码(二)API获取行情和script_trader
VNPY源码(三)主引擎MainEngine
VNPY源码(四)DataRecorder
VNPY源码(五)CtaEngine实盘引擎
VNPY源码(六)BacktesterEngine回测引擎
VNPY源码(七)限价单与停止单
VNPY源码(八)VNPY的数据流
这是VNPY中的一个难点,特别是停止单,相信很多人学了vnpy很久,还是没有搞懂。
一、什么是限价单?什么是停止单?
我们先来看看官方的回答吧:
问:请问backtesting里的cross_limit_order和cross_stop_order什么意思?是干什么用的?
答:撮合限价单委托,撮合本地停止单(条件单)委托。讲最新的行情K线或者TICK和策略之前下达的所有委托进行检查,如果能够撮合成交,则返回并记录数据。
请问你看得懂吗?反正我是看不懂。下面我试着来解释一下:
举个例子,以做多为例,一般我们是想要价格跌到多少的时候就买进,比如Long170:表示如果价格低于170,我们就买入,这就是限价单,
如果我们是想要价格涨到多少的时候就买进,比如Long170:表示如果价格高于170,我们就买入,这就是停止单。
限价单是优价成交,比如你设定170元买入,他买入的价格肯定<=170元。
停止单是劣价成交,比如你设定170元买入,他买入的价格肯定>=170元。
而且这个停止单的叫法本来就有问题,停人摸不着头脑,很难理解,其实其英文名为Stop Order,其实就是我们常说的止损单。你从止损的角度去理解就好理解很多了。
1.限价单
在限价单下:
Long170:表示如果价格低于170,我们就买入。
Long70:表示如果价格低于70,我们就买入。
2.停止单:
在停止单下:
Long170:表示如果价格高于170,我们就买入(突破买入)。
Long70:表示如果价格涨过了70,我们就买入。
3.Long 和Short
但是也要注意,并不是说Long就代表买入,
你持有空仓,平仓是Long, 你多头建仓,也是Long.
你持有多仓,平仓是short,你空头建仓,也是short。
举个例子:short170的意思,是我们打算在170的价格卖出。

来看个停止单和固定止损的例子吧!

二、图形理解


三、cross_limit_order
通过判断限价与当前价,调用策略的on_order、on_trade函数,并更新仓位。
1 | def cross_limit_order(self): |
3 | Cross limit order with last bar/tick data. |
5 | if self.mode == BacktestingMode.BAR: |
6 | long_cross_price = self.bar.low_price |
7 | short_cross_price = self.bar.high_price |
8 | long_best_price = self.bar.open_price |
9 | short_best_price = self.bar.open_price |
11 | long_cross_price = self.tick.ask_price_1 |
12 | short_cross_price = self.tick.bid_price_1 |
13 | long_best_price = long_cross_price |
14 | short_best_price = short_cross_price |
16 | for order in list(self.active_limit_orders.values()): |
17 | # Push order update with status "not traded" (pending). |
18 | if order.status == Status.SUBMITTING: |
19 | order.status = Status.NOTTRADED |
20 | self.strategy.on_order(order) |
22 | # Check whether limit orders can be filled.这里判断是否成交。 |
24 | order.direction == Direction.LONG |
25 | and order.price >= long_cross_price #目的是要低于限价买入,long_cross_price 等于bar的最低价,这行代码反过来看,就是bar的最低价小于我们设定的限价,所以要买入。 |
26 | and long_cross_price > 0 |
30 | order.direction == Direction.SHORT |
31 | and order.price <= short_cross_price #目的是高于限价卖出,反过来看,bar的最高价(short_cross_price)大于我们的限价了。 |
32 | and short_cross_price > 0 |
35 | if not long_cross and not short_cross: |
38 | # Push order udpate with status "all traded" (filled). |
39 | order.traded = order.volume |
40 | order.status = Status.ALLTRADED |
41 | self.strategy.on_order(order) #调用策略的on_order |
43 | self.active_limit_orders.pop(order.vt_orderid) |
49 | trade_price = min(order.price, long_best_price) #目的是要低于限价买入,如果开盘价比限价低,当然是按开盘价买入,如果开盘价比限价高,但是那个bar的最低价是小于我们的限价的,所以价格会走到我们的限价的价位,理论成交价格也是限价。 |
50 | pos_change = order.volume |
52 | trade_price = max(order.price, short_best_price) |
53 | pos_change = -order.volume |
57 | exchange=order.exchange, |
58 | orderid=order.orderid, |
59 | tradeid=str(self.trade_count), |
60 | direction=order.direction, |
64 | time=self.datetime. strftime ( "%H:%M:%S" ), |
65 | gateway_name=self.gateway_name, |
67 | trade.datetime = self.datetime |
69 | self.strategy.pos += pos_change |
70 | self.strategy.on_trade(trade) |
72 | self.trades[trade.vt_tradeid] = trade |
为了便于理解,我们只看Bar部分吧。
1.active_limit_orders
它的数据是在send_limit_order这个函数中生成的。
9 | self.limit_order_count += 1 |
13 | exchange=self.exchange, |
14 | orderid=str(self.limit_order_count), |
19 | status=Status.SUBMITTING, |
20 | gateway_name=self.gateway_name, |
22 | order.datetime = self.datetime |
24 | self.active_limit_orders[order.vt_orderid] = order #存入active_limit_orders这个字典 |
25 | self.limit_orders[order.vt_orderid] = order #存入limit_orders这个字典 |
27 | return order.vt_orderid |
四、cross_stop_order
通过判断限价与当前价,调用策略的on_stop_order、on_order、on_trade函数。
这里就和上面的不一样:
1 | def cross_stop_order(self): |
3 | Cross stop order with last bar/tick data. |
5 | if self.mode == BacktestingMode.BAR: |
6 | long_cross_price = self.bar.high_price |
7 | short_cross_price = self.bar.low_price |
8 | long_best_price = self.bar.open_price |
9 | short_best_price = self.bar.open_price |
11 | long_cross_price = self.tick.last_price |
12 | short_cross_price = self.tick.last_price |
13 | long_best_price = long_cross_price |
14 | short_best_price = short_cross_price |
16 | for stop_order in list(self.active_stop_orders.values()): |
17 | # Check whether stop order can be triggered. |
19 | stop_order.direction == Direction.LONG |
20 | and stop_order.price <= long_cross_price #目的是要高于限价买入,这里相当于bar的最高价(long_cross_price)大于我们的限价了 |
24 | stop_order.direction == Direction.SHORT |
25 | and stop_order.price >= short_cross_price #目的是要低于限价卖出(止损),这里相当于bar的最低价(short_cross_price)小于我们的限价了 |
28 | if not long_cross and not short_cross: |
32 | self.limit_order_count += 1 |
36 | exchange=self.exchange, |
37 | orderid=str(self.limit_order_count), |
38 | direction=stop_order.direction, |
39 | offset=stop_order.offset, |
40 | price=stop_order.price, |
41 | volume=stop_order.volume, |
42 | status=Status.ALLTRADED, |
43 | gateway_name=self.gateway_name, |
45 | order.datetime = self.datetime |
47 | self.limit_orders[order.vt_orderid] = order #将order存入了limit_orders字典 |
52 | trade_price = max(stop_order.price, long_best_price) #如果开盘价比限价高,当然是按开盘价买入,如果低,当然按限价买入。 |
53 | pos_change = order.volume |
55 | trade_price = min(stop_order.price, short_best_price) |
56 | pos_change = -order.volume |
可以 讲的很好