Skip to content

Commit

Permalink
[Mod] complete test of rollover tool
Browse files Browse the repository at this point in the history
  • Loading branch information
vnpy committed Apr 27, 2021
1 parent fef378e commit fd8d1e2
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 28 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

## 新增
1. 新增DataManager在导入CSV文件时,对于时间戳时区的选择功能
2. 新增CtaStrategy模块的策略移仓助手功能,实现一键式期货换月移仓支持


# 2.2.0版本

Expand Down
2 changes: 1 addition & 1 deletion vnpy/app/chart_wizard/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,4 @@ def _query_history(
)

event = Event(EVENT_CHART_HISTORY, data)
self.event_engine.put(event)
self.event_engine.put(event)
3 changes: 2 additions & 1 deletion vnpy/app/cta_strategy/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ def remove_strategy(self, strategy_name: str):
# Remove from strategies
self.strategies.pop(strategy_name)

self.write_log(f"策略{strategy.strategy_name}移除移除成功")
return True

def load_strategy_class(self):
Expand Down Expand Up @@ -937,7 +938,7 @@ def write_log(self, msg: str, strategy: CtaTemplate = None):
Create cta engine log event.
"""
if strategy:
msg = f"{strategy.strategy_name}: {msg}"
msg = f"[{strategy.strategy_name}] {msg}"

log = LogData(msg=msg, gateway_name=APP_NAME)
event = Event(type=EVENT_CTA_LOG, data=log)
Expand Down
90 changes: 68 additions & 22 deletions vnpy/app/cta_strategy/ui/rollover.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,44 @@
from datetime import datetime
from time import sleep
from typing import TYPE_CHECKING

from vnpy.trader.engine import MainEngine
from vnpy.trader.constant import OrderType
from vnpy.trader.object import ContractData, OrderRequest, TickData
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 PositionHolding
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_engine: CtaEngine) -> None:
def __init__(self, cta_manager: "CtaManager") -> None:
""""""
super().__init__()

self.cta_engine = cta_engine
self.main_engine: MainEngine = cta_engine.main_engine
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(
self.cta_engine.symbol_strategy_map.keys()
)
self.old_symbol_combo.addItems(old_symbols)

self.new_symbol_line = QtWidgets.QLineEdit()

Expand All @@ -39,37 +47,69 @@ def init_ui(self) -> None:

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(button)
form.addRow(self.log_edit)

self.setLayout(form)
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)
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

# Roll position first
self.roll_position(old_symbol, new_symbol, payup)

strategies = self.cta_engine.symbol_strategy_map[old_symbol]
# Then roll strategy
for strategy in strategies:
self.roll_strategy(strategy, new_symbol, payup)
self.roll_strategy(strategy, new_symbol)

# Disable self
self.setEnabled(False)

def roll_position(self, old_symbol: str, new_symbol: str, payup: int) -> None:
""""""
Expand All @@ -78,53 +118,58 @@ def roll_position(self, old_symbol: str, new_symbol: str, payup: int) -> None:

# Roll long position
if holding.long_pos:
volume = holding.long_pos

self.send_order(
old_symbol,
Direction.SHORT,
Offset.CLOSE,
payup,
holding.long_pos
volume
)

self.send_order(
new_symbol,
Direction.LONG,
Offset.OPEN,
payup,
holding.long_pos
volume
)

# Roll short postiion
if holding.short_pos:
volume = holding.short_pos

self.send_order(
old_symbol,
Direction.LONG,
Offset.CLOSE,
payup,
holding.short_pos
volume
)

self.send_order(
new_symbol,
Direction.SHORT,
Offset.OPEN,
payup,
holding.short_pos
volume
)

def roll_strategy(self, strategy: CtaTemplate, vt_symbol: str) -> None:
""""""
if not strategy.inited:
self.write_log(f"无法执行移仓,请先初始化策略{strategy.strategy_name}")
return
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
self.cta_engine.remove_strategy(name)
result = self.cta_engine.remove_strategy(name)
if result:
self.cta_manager.remove_strategy(name)

self.write_log(f"移除老策略{name}[{strategy.vt_symbol}]")

Expand Down Expand Up @@ -160,6 +205,7 @@ def send_order(
"""
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
Expand All @@ -177,7 +223,7 @@ def send_order(
reference=f"{APP_NAME}_Rollover"
)

req_list = self.offset_converter.convert_order_request(original_req, False, False)
req_list = offset_converter.convert_order_request(original_req, False, False)

vt_orderids = []
for req in req_list:
Expand All @@ -186,7 +232,7 @@ def send_order(
continue

vt_orderids.append(vt_orderid)
self.offset_converter.update_order_request(req, vt_orderid)
offset_converter.update_order_request(req, vt_orderid)

msg = f"发出委托{vt_symbol}{direction.value} {offset.value}{volume}@{price}"
self.write_log(msg)
2 changes: 1 addition & 1 deletion vnpy/app/cta_strategy/ui/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ def show(self):

def roll(self):
""""""
dialog = RolloverTool(self.cta_engine)
dialog = RolloverTool(self)
dialog.exec_()


Expand Down
2 changes: 1 addition & 1 deletion vnpy/app/market_radar/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def remove_signal(self, signal_id: int) -> None:
signal = self.signals.pop(signal_id)
signal.active = False

self.rule_signal_map[signal.rule_name].remove(signal)
self.rule_signal_map[signal.rule_name].remove(signal)
self.put_event(EVENT_RADAR_SIGNAL, signal)

def check_signal(self, rule_name: str, rule_value: float) -> None:
Expand Down
4 changes: 2 additions & 2 deletions vnpy/gateway/binances/binances_gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ def query_history(self, req: HistoryRequest) -> List[BarData]:
if req.end:
end_time = int(datetime.timestamp(req.end))
params["endTime"] = end_time * 1000 # convert to millisecond

else:
params["endTime"] = end_time * 1000
path = "/dapi/v1/klines"
Expand Down Expand Up @@ -760,7 +760,7 @@ def query_history(self, req: HistoryRequest) -> List[BarData]:
else:
end_dt = begin - TIMEDELTA_MAP[req.interval]
end_time = int(datetime.timestamp(end_dt))

if not self.usdt_base:
history = list(reversed(history))
return history
Expand Down

0 comments on commit fd8d1e2

Please sign in to comment.