Skip to content

Commit 9edf475

Browse files
committed
Add algos
1 parent edbb448 commit 9edf475

File tree

9 files changed

+1029
-0
lines changed

9 files changed

+1029
-0
lines changed

vnpy_algotrading/algos/__init__.py

Whitespace-only changes.
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
from vnpy.trader.constant import Direction
2+
from vnpy.trader.object import TradeData, OrderData
3+
from vnpy.trader.engine import BaseEngine
4+
5+
from ..template import AlgoTemplate
6+
7+
8+
class ArbitrageAlgo(AlgoTemplate):
9+
""""""
10+
11+
display_name = "Arbitrage 套利"
12+
13+
default_setting = {
14+
"active_vt_symbol": "",
15+
"passive_vt_symbol": "",
16+
"spread_up": 0.0,
17+
"spread_down": 0.0,
18+
"max_pos": 0,
19+
"interval": 0,
20+
}
21+
22+
variables = [
23+
"timer_count",
24+
"active_vt_orderid",
25+
"passive_vt_orderid",
26+
"active_pos",
27+
"passive_pos"
28+
]
29+
30+
def __init__(
31+
self,
32+
algo_engine: BaseEngine,
33+
algo_name: str,
34+
setting: dict
35+
):
36+
""""""
37+
super().__init__(algo_engine, algo_name, setting)
38+
39+
# 参数
40+
self.active_vt_symbol = setting["active_vt_symbol"]
41+
self.passive_vt_symbol = setting["passive_vt_symbol"]
42+
self.spread_up = setting["spread_up"]
43+
self.spread_down = setting["spread_down"]
44+
self.max_pos = setting["max_pos"]
45+
self.interval = setting["interval"]
46+
47+
# 变量
48+
self.active_vt_orderid = ""
49+
self.passive_vt_orderid = ""
50+
self.active_pos = 0
51+
self.passive_pos = 0
52+
self.timer_count = 0
53+
54+
self.subscribe(self.active_vt_symbol)
55+
self.subscribe(self.passive_vt_symbol)
56+
57+
self.put_parameters_event()
58+
self.put_variables_event()
59+
60+
def on_stop(self):
61+
""""""
62+
self.write_log("停止算法")
63+
64+
def on_order(self, order: OrderData):
65+
""""""
66+
if order.vt_symbol == self.active_vt_symbol:
67+
if not order.is_active():
68+
self.active_vt_orderid = ""
69+
elif order.vt_symbol == self.passive_vt_symbol:
70+
if not order.is_active():
71+
self.passive_vt_orderid = ""
72+
self.put_variables_event()
73+
74+
def on_trade(self, trade: TradeData):
75+
""""""
76+
# 更新持仓
77+
if trade.direction == Direction.LONG:
78+
if trade.vt_symbol == self.active_vt_symbol:
79+
self.active_pos += trade.volume
80+
else:
81+
self.passive_pos += trade.volume
82+
else:
83+
if trade.vt_symbol == self.active_vt_symbol:
84+
self.active_pos -= trade.volume
85+
else:
86+
self.passive_pos -= trade.volume
87+
88+
# 主动腿成交,执行对冲
89+
if trade.vt_symbol == self.active_vt_symbol:
90+
self.write_log("收到主动腿成交回报,执行对冲")
91+
self.hedge()
92+
93+
self.put_variables_event()
94+
95+
def on_timer(self):
96+
""""""
97+
# 定时执行算法
98+
self.timer_count += 1
99+
if self.timer_count < self.interval:
100+
self.put_variables_event()
101+
return
102+
self.timer_count = 0
103+
104+
# 继续运行前撤销所有活动中订单
105+
if self.active_vt_orderid or self.passive_vt_orderid:
106+
self.write_log("有未成交委托,执行撤单")
107+
self.cancel_all()
108+
return
109+
110+
# 确保主动腿和被动腿完全对冲
111+
if (self.active_pos + self.passive_pos) != 0:
112+
self.write_log("主动腿和被动腿数量不一致,执行对冲")
113+
self.hedge()
114+
return
115+
116+
# 确保两条套利腿的行情数据都通畅
117+
active_tick = self.get_tick(self.active_vt_symbol)
118+
passive_tick = self.get_tick(self.passive_vt_symbol)
119+
if not active_tick or not passive_tick:
120+
self.write_log("获取某条套利腿的行情失败,无法交易")
121+
return
122+
123+
# 计算价差
124+
spread_bid_price = active_tick.bid_price_1 - passive_tick.ask_price_1
125+
spread_ask_price = active_tick.ask_price_1 - passive_tick.bid_price_1
126+
127+
spread_bid_volume = min(active_tick.bid_volume_1,
128+
passive_tick.ask_volume_1)
129+
spread_ask_volume = min(active_tick.ask_volume_1,
130+
passive_tick.bid_volume_1)
131+
132+
msg = f"价差盘口,买:{spread_bid_price} ({spread_bid_volume}),卖:{spread_ask_price} ({spread_ask_volume})"
133+
self.write_log(msg)
134+
135+
# 做空条件
136+
if spread_bid_price > self.spread_up:
137+
self.write_log("套利价差超过上限,满足做空条件")
138+
139+
if self.active_pos > -self.max_pos:
140+
self.write_log("当前持仓小于最大持仓限制,执行卖出操作")
141+
142+
volume = min(spread_bid_volume,
143+
self.active_pos + self.max_pos)
144+
145+
self.active_vt_orderid = self.sell(
146+
self.active_vt_symbol,
147+
active_tick.bid_price_1,
148+
volume
149+
)
150+
151+
# 做多条件
152+
elif spread_ask_price < -self.spread_down:
153+
self.write_log("套利价差超过下限,满足做多条件")
154+
155+
if self.active_pos < self.max_pos:
156+
self.write_log("当前持仓小于最大持仓限制,执行买入操作")
157+
158+
volume = min(spread_ask_volume,
159+
self.max_pos - self.active_pos)
160+
161+
self.active_vt_orderid = self.buy(
162+
self.active_vt_symbol,
163+
active_tick.ask_price_1,
164+
volume
165+
)
166+
167+
self.put_variables_event()
168+
169+
def hedge(self):
170+
"""对冲"""
171+
tick = self.get_tick(self.passive_vt_symbol)
172+
volume = -self.active_pos - self.passive_pos
173+
174+
if volume > 0:
175+
self.passive_vt_orderid = self.buy(
176+
self.passive_vt_symbol,
177+
tick.ask_price_1,
178+
volume
179+
)
180+
elif volume < 0:
181+
self.passive_vt_orderid = self.sell(
182+
self.passive_vt_symbol,
183+
tick.bid_price_1,
184+
abs(volume)
185+
)
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
from random import uniform
2+
3+
from vnpy.trader.constant import Offset, Direction
4+
from vnpy.trader.object import TradeData, OrderData, TickData
5+
from vnpy.trader.engine import BaseEngine
6+
from vnpy.trader.utility import round_to
7+
8+
from ..template import AlgoTemplate
9+
10+
11+
class BestLimitAlgo(AlgoTemplate):
12+
""""""
13+
14+
display_name = "BestLimit 最优限价"
15+
16+
default_setting = {
17+
"vt_symbol": "",
18+
"direction": [Direction.LONG.value, Direction.SHORT.value],
19+
"volume": 0.0,
20+
"min_volume": 0.0,
21+
"max_volume": 0.0,
22+
"volume_change": [
23+
"1",
24+
"0.1",
25+
"0.01",
26+
"0.001",
27+
"0.0001",
28+
"0.00001"
29+
],
30+
"offset": [
31+
Offset.NONE.value,
32+
Offset.OPEN.value,
33+
Offset.CLOSE.value,
34+
Offset.CLOSETODAY.value,
35+
Offset.CLOSEYESTERDAY.value
36+
]
37+
}
38+
39+
variables = [
40+
"traded",
41+
"vt_orderid",
42+
"order_price",
43+
"last_tick",
44+
]
45+
46+
def __init__(
47+
self,
48+
algo_engine: BaseEngine,
49+
algo_name: str,
50+
setting: dict
51+
):
52+
""""""
53+
super().__init__(algo_engine, algo_name, setting)
54+
55+
# 参数
56+
self.vt_symbol = setting["vt_symbol"]
57+
self.direction = Direction(setting["direction"])
58+
self.volume = setting["volume"]
59+
self.offset = Offset(setting["offset"])
60+
61+
self.min_volume = setting["min_volume"]
62+
self.max_volume = setting["max_volume"]
63+
64+
if "." in setting["volume_change"]:
65+
self.volume_change = float(setting["volume_change"])
66+
else:
67+
self.volume_change = int(setting["volume_change"])
68+
69+
# 变量
70+
self.vt_orderid = ""
71+
self.traded = 0
72+
self.last_tick = None
73+
self.order_price = 0
74+
75+
self.put_parameters_event()
76+
self.put_variables_event()
77+
78+
# 检查最大/最小挂单量
79+
if self.min_volume <= 0:
80+
self.write_log("最小挂单量必须大于0,算法启动失败")
81+
self.stop()
82+
return
83+
84+
if self.max_volume < self.min_volume:
85+
self.write_log("最大挂单量必须不小于最小委托量,算法启动失败")
86+
self.stop()
87+
return
88+
89+
self.subscribe(self.vt_symbol)
90+
91+
def on_tick(self, tick: TickData):
92+
""""""
93+
self.last_tick = tick
94+
95+
if self.direction == Direction.LONG:
96+
if not self.vt_orderid:
97+
self.buy_best_limit()
98+
elif self.order_price != self.last_tick.bid_price_1:
99+
self.cancel_all()
100+
else:
101+
if not self.vt_orderid:
102+
self.sell_best_limit()
103+
elif self.order_price != self.last_tick.ask_price_1:
104+
self.cancel_all()
105+
106+
self.put_variables_event()
107+
108+
def on_trade(self, trade: TradeData):
109+
""""""
110+
self.traded += trade.volume
111+
112+
if self.traded >= self.volume:
113+
self.write_log(f"已交易数量:{self.traded},总数量:{self.volume}")
114+
self.stop()
115+
else:
116+
self.put_variables_event()
117+
118+
def on_order(self, order: OrderData):
119+
""""""
120+
if not order.is_active():
121+
self.vt_orderid = ""
122+
self.order_price = 0
123+
self.put_variables_event()
124+
125+
def buy_best_limit(self):
126+
""""""
127+
volume_left = self.volume - self.traded
128+
129+
rand_volume = self.generate_rand_volume()
130+
order_volume = min(rand_volume, volume_left)
131+
132+
self.order_price = self.last_tick.bid_price_1
133+
self.vt_orderid = self.buy(
134+
self.vt_symbol,
135+
self.order_price,
136+
order_volume,
137+
offset=self.offset
138+
)
139+
140+
def sell_best_limit(self):
141+
""""""
142+
volume_left = self.volume - self.traded
143+
144+
rand_volume = self.generate_rand_volume()
145+
order_volume = min(rand_volume, volume_left)
146+
147+
self.order_price = self.last_tick.ask_price_1
148+
self.vt_orderid = self.sell(
149+
self.vt_symbol,
150+
self.order_price,
151+
order_volume,
152+
offset=self.offset
153+
)
154+
155+
def generate_rand_volume(self):
156+
""""""
157+
rand_volume = uniform(self.min_volume, self.max_volume)
158+
rand_volume = round_to(rand_volume, self.volume_change)
159+
160+
if self.volume_change == 1:
161+
rand_volume = int(rand_volume)
162+
163+
return rand_volume

0 commit comments

Comments
 (0)