-
Notifications
You must be signed in to change notification settings - Fork 103
/
rollover.py
259 lines (206 loc) · 8.02 KB
/
rollover.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
from datetime import datetime
from time import sleep
from typing import TYPE_CHECKING
from copy import copy
from vnpy.trader.engine import MainEngine
from vnpy.trader.constant import OrderType
from vnpy.trader.object import ContractData, OrderRequest, SubscribeRequest, TickData
from vnpy.trader.object import Direction, Offset
from vnpy.trader.ui import QtWidgets
from vnpy.trader.converter import OffsetConverter, PositionHolding
from ..engine import CtaEngine, APP_NAME
from ..template import CtaTemplate
if TYPE_CHECKING:
from .widget import CtaManager
class RolloverTool(QtWidgets.QDialog):
""""""
def __init__(self, cta_manager: "CtaManager") -> None:
""""""
super().__init__()
self.cta_manager: "CtaManager" = cta_manager
self.cta_engine: CtaEngine = cta_manager.cta_engine
self.main_engine: MainEngine = cta_manager.main_engine
self.init_ui()
def init_ui(self) -> None:
""""""
self.setWindowTitle("移仓助手")
old_symbols = []
for vt_symbol, strategies in self.cta_engine.symbol_strategy_map.items():
if strategies:
old_symbols.append(vt_symbol)
self.old_symbol_combo = QtWidgets.QComboBox()
self.old_symbol_combo.addItems(old_symbols)
self.new_symbol_line = QtWidgets.QLineEdit()
self.payup_spin = QtWidgets.QSpinBox()
self.payup_spin.setMinimum(5)
self.max_volume_spin = QtWidgets.QSpinBox()
self.max_volume_spin.setMinimum(1)
self.max_volume_spin.setMaximum(10000)
self.max_volume_spin.setValue(100)
self.log_edit = QtWidgets.QTextEdit()
self.log_edit.setReadOnly(True)
self.log_edit.setMinimumWidth(500)
button = QtWidgets.QPushButton("移仓")
button.clicked.connect(self.roll_all)
button.setFixedHeight(button.sizeHint().height() * 2)
form = QtWidgets.QFormLayout()
form.addRow("移仓合约", self.old_symbol_combo)
form.addRow("目标合约", self.new_symbol_line)
form.addRow("委托超价", self.payup_spin)
form.addRow("单笔上限", self.max_volume_spin)
form.addRow(button)
hbox = QtWidgets.QHBoxLayout()
hbox.addLayout(form)
hbox.addWidget(self.log_edit)
self.setLayout(hbox)
def write_log(self, text: str) -> None:
""""""
now = datetime.now()
text = now.strftime("%H:%M:%S\t") + text
self.log_edit.append(text)
def subscribe(self, vt_symbol: str) -> None:
""""""
contract = self.main_engine.get_contract(vt_symbol)
if not contract:
return
req = SubscribeRequest(contract.symbol, contract.exchange)
self.main_engine.subscribe(req, contract.gateway_name)
def roll_all(self) -> None:
""""""
old_symbol = self.old_symbol_combo.currentText()
new_symbol = self.new_symbol_line.text()
self.subscribe(new_symbol)
sleep(1)
new_tick = self.main_engine.get_tick(new_symbol)
if not new_tick:
self.write_log(f"无法获取目标合约{new_symbol}的盘口数据,请先订阅行情")
return
payup = self.payup_spin.value()
# Check all strategies inited (pos data loaded from disk json file) and not trading
strategies = self.cta_engine.symbol_strategy_map[old_symbol]
for strategy in strategies:
if not strategy.inited:
self.write_log(f"策略{strategy.strategy_name}尚未初始化,无法执行移仓")
return
if strategy.trading:
self.write_log(f"策略{strategy.strategy_name}正在运行中,无法执行移仓")
return
# Roll position first
self.roll_position(old_symbol, new_symbol, payup)
# Then roll strategy
for strategy in copy(strategies):
self.roll_strategy(strategy, new_symbol)
# Disable self
self.setEnabled(False)
def roll_position(self, old_symbol: str, new_symbol: str, payup: int) -> None:
""""""
converter = self.cta_engine.offset_converter
holding: PositionHolding = converter.get_position_holding(old_symbol)
# Roll long position
if holding.long_pos:
volume = holding.long_pos
self.send_order(
old_symbol,
Direction.SHORT,
Offset.CLOSE,
payup,
volume
)
self.send_order(
new_symbol,
Direction.LONG,
Offset.OPEN,
payup,
volume
)
# Roll short postiion
if holding.short_pos:
volume = holding.short_pos
self.send_order(
old_symbol,
Direction.LONG,
Offset.CLOSE,
payup,
volume
)
self.send_order(
new_symbol,
Direction.SHORT,
Offset.OPEN,
payup,
volume
)
def roll_strategy(self, strategy: CtaTemplate, vt_symbol: str) -> None:
""""""
if not strategy.inited:
self.cta_engine._init_strategy(strategy.strategy_name)
# Save data of old strategy
pos = strategy.pos
name = strategy.strategy_name
parameters = strategy.get_parameters()
# Remove old strategy
result = self.cta_engine.remove_strategy(name)
if result:
self.cta_manager.remove_strategy(name)
self.write_log(f"移除老策略{name}[{strategy.vt_symbol}]")
# Add new strategy
self.cta_engine.add_strategy(
strategy.__class__.__name__,
name,
vt_symbol,
parameters
)
self.write_log(f"创建策略{name}[{vt_symbol}]")
# Init new strategy
self.cta_engine.init_strategy(name)
self.write_log(f"初始化策略{name}[{vt_symbol}]")
# Update pos to new strategy
new_strategy: CtaTemplate = self.cta_engine.strategies[name]
new_strategy.pos = pos
new_strategy.sync_data()
self.write_log(f"更新策略仓位{name}[{vt_symbol}]")
def send_order(
self,
vt_symbol: str,
direction: Direction,
offset: Offset,
payup: int,
volume: float,
):
"""
Send a new order to server.
"""
max_volume: int = self.max_volume_spin.value()
contract: ContractData = self.main_engine.get_contract(vt_symbol)
tick: TickData = self.main_engine.get_tick(vt_symbol)
offset_converter: OffsetConverter = self.cta_engine.offset_converter
if direction == Direction.LONG:
price = tick.ask_price_1 + contract.pricetick * payup
else:
price = tick.bid_price_1 - contract.pricetick * payup
while True:
order_volume: int = min(volume, max_volume)
original_req: OrderRequest = OrderRequest(
symbol=contract.symbol,
exchange=contract.exchange,
direction=direction,
offset=offset,
type=OrderType.LIMIT,
price=price,
volume=order_volume,
reference=f"{APP_NAME}_Rollover"
)
req_list = offset_converter.convert_order_request(original_req, False, False)
vt_orderids = []
for req in req_list:
vt_orderid = self.main_engine.send_order(req, contract.gateway_name)
if not vt_orderid:
continue
vt_orderids.append(vt_orderid)
offset_converter.update_order_request(req, vt_orderid)
msg = f"发出委托{vt_symbol},{direction.value} {offset.value},{volume}@{price}"
self.write_log(msg)
# Check whether all volume sent
volume = volume - order_volume
if not volume:
break