Merge pull request #4 from vnpy/DEV

Dev
This commit is contained in:
RobinLiu 2019-04-08 10:42:40 +08:00 committed by GitHub
commit 3cd9165497
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1328 additions and 157 deletions

View File

@ -15,6 +15,7 @@ from vnpy.gateway.huobi import HuobiGateway
from vnpy.app.cta_strategy import CtaStrategyApp
from vnpy.app.csv_loader import CsvLoaderApp
from vnpy.app.algo_trading import AlgoTradingApp
def main():
@ -35,6 +36,7 @@ def main():
main_engine.add_app(CtaStrategyApp)
main_engine.add_app(CsvLoaderApp)
main_engine.add_app(AlgoTradingApp)
main_window = MainWindow(main_engine, event_engine)
main_window.showMaximized()

View File

@ -3,9 +3,10 @@ from pathlib import Path
from vnpy.trader.app import BaseApp
from .engine import AlgoEngine, APP_NAME
from .template import AlgoTemplate
class CtaStrategyApp(BaseApp):
class AlgoTradingApp(BaseApp):
""""""
app_name = APP_NAME

View File

@ -1,59 +0,0 @@
# encoding: UTF-8
'''
动态载入所有的策略类
'''
from __future__ import print_function
import os
import importlib
import traceback
# 用来保存算法类和控件类的字典
ALGO_DICT = {}
WIDGET_DICT = {}
#----------------------------------------------------------------------
def loadAlgoModule(path, prefix):
"""使用importlib动态载入算法"""
for root, subdirs, files in os.walk(path):
for name in files:
# 只有文件名以Algo.py结尾的才是算法文件
if len(name)>7 and name[-7:] == 'Algo.py':
try:
# 模块名称需要模块路径前缀
moduleName = prefix + name.replace('.py', '')
module = importlib.import_module(moduleName)
# 获取算法类和控件类
algo = None
widget = None
for k in dir(module):
# 以Algo结尾的类是算法
if k[-4:] == 'Algo':
algo = module.__getattribute__(k)
# 以Widget结尾的类是控件
if k[-6:] == 'Widget':
widget = module.__getattribute__(k)
# 保存到字典中
if algo and widget:
ALGO_DICT[algo.templateName] = algo
WIDGET_DICT[algo.templateName] = widget
except:
print ('-' * 20)
print ('Failed to import strategy file %s:' %moduleName)
traceback.print_exc()
# 遍历algo目录下的文件
path1 = os.path.abspath(os.path.dirname(__file__))
loadAlgoModule(path1, 'vnpy.trader.app.algoTrading.algo.')
# 遍历工作目录下的文件
path2 = os.getcwd()
loadAlgoModule(path2, '')

View File

@ -0,0 +1,137 @@
from vnpy.trader.constant import Offset, Direction
from vnpy.trader.object import TradeData, OrderData, TickData
from vnpy.trader.engine import BaseEngine
from vnpy.app.algo_trading import AlgoTemplate
class IcebergAlgo(AlgoTemplate):
""""""
display_name = "Iceberg 冰山"
default_setting = {
"vt_symbol": "",
"direction": [Direction.LONG.value, Direction.SHORT.value],
"price": 0.0,
"volume": 0.0,
"display_volume": 0.0,
"interval": 0,
"offset": [
Offset.NONE.value,
Offset.OPEN.value,
Offset.CLOSE.value,
Offset.CLOSETODAY.value,
Offset.CLOSEYESTERDAY.value
]
}
variables = [
"traded",
"timer_count",
"vt_orderid"
]
def __init__(
self,
algo_engine: BaseEngine,
algo_name: str,
setting: dict
):
""""""
super().__init__(algo_engine, algo_name, setting)
# Parameters
self.vt_symbol = setting["vt_symbol"]
self.direction = Direction(setting["direction"])
self.price = setting["price"]
self.volume = setting["volume"]
self.display_volume = setting["display_volume"]
self.interval = setting["interval"]
self.offset = Offset(setting["offset"])
# Variables
self.timer_count = 0
self.vt_orderid = ""
self.traded = 0
self.last_tick = None
self.subscribe(self.vt_symbol)
self.put_parameters_event()
self.put_variables_event()
def on_stop(self):
""""""
self.write_log("停止算法")
def on_tick(self, tick: TickData):
""""""
self.last_tick = tick
def on_order(self, order: OrderData):
""""""
msg = f"委托号:{order.vt_orderid},委托状态:{order.status.value}"
self.write_log(msg)
if not order.is_active():
self.vt_orderid = ""
self.put_variables_event()
def on_trade(self, trade: TradeData):
""""""
self.traded += trade.volume
if self.traded >= self.volume:
self.write_log(f"已交易数量:{self.traded},总数量:{self.volume}")
self.stop()
else:
self.put_variables_event()
def on_timer(self):
""""""
self.timer_count += 1
if self.timer_count < self.interval:
self.put_variables_event()
return
self.timer_count = 0
contract = self.get_contract(self.vt_symbol)
if not contract:
return
# If order already finished, just send new order
if not self.vt_orderid:
order_volume = self.volume - self.traded
order_volume = min(order_volume, self.display_volume)
if self.direction == Direction.LONG:
self.vt_orderid = self.buy(
self.vt_symbol,
self.price,
order_volume,
offset=self.offset
)
else:
self.vt_orderid = self.sell(
self.vt_symbol,
self.price,
order_volume,
offset=self.offset
)
# Otherwise check for cancel
else:
if self.direction == Direction.LONG:
if self.last_tick.ask_price_1 <= self.price:
self.cancel_order(self.vt_orderid)
self.vt_orderid = ""
self.write_log(u"最新Tick卖一价低于买入委托价格之前委托可能丢失强制撤单")
else:
if self.last_tick.bid_price_1 >= self.price:
self.cancel_order(self.vt_orderid)
self.vt_orderid = ""
self.write_log(u"最新Tick买一价高于卖出委托价格之前委托可能丢失强制撤单")
self.put_variables_event()

View File

@ -0,0 +1,101 @@
from vnpy.trader.constant import Offset, Direction
from vnpy.trader.object import TradeData, OrderData, TickData
from vnpy.trader.engine import BaseEngine
from vnpy.app.algo_trading import AlgoTemplate
class SniperAlgo(AlgoTemplate):
""""""
display_name = "Sniper 狙击手"
default_setting = {
"vt_symbol": "",
"direction": [Direction.LONG.value, Direction.SHORT.value],
"price": 0.0,
"volume": 0.0,
"offset": [
Offset.NONE.value,
Offset.OPEN.value,
Offset.CLOSE.value,
Offset.CLOSETODAY.value,
Offset.CLOSEYESTERDAY.value
]
}
variables = [
"traded",
"vt_orderid"
]
def __init__(
self,
algo_engine: BaseEngine,
algo_name: str,
setting: dict
):
""""""
super().__init__(algo_engine, algo_name, setting)
# Parameters
self.vt_symbol = setting["vt_symbol"]
self.direction = Direction(setting["direction"])
self.price = setting["price"]
self.volume = setting["volume"]
self.offset = Offset(setting["offset"])
# Variables
self.vt_orderid = ""
self.traded = 0
self.subscribe(self.vt_symbol)
self.put_parameters_event()
self.put_variables_event()
def on_tick(self, tick: TickData):
""""""
if self.vt_orderid:
self.cancel_all()
return
if self.direction == Direction.LONG:
if tick.ask_price_1 <= self.price:
order_volume = self.volume - self.traded
order_volume = min(order_volume, tick.ask_volume_1)
self.vt_orderid = self.buy(
self.vt_symbol,
self.price,
order_volume,
offset=self.offset
)
else:
if tick.bid_price_1 >= self.price:
order_volume = self.volume - self.traded
order_volume = min(order_volume, tick.bid_volume_1)
self.vt_orderid = self.sell(
self.vt_symbol,
self.price,
order_volume,
offset=self.offset
)
self.put_variables_event()
def on_order(self, order: OrderData):
""""""
if not order.is_active():
self.vt_orderid = ""
self.put_variables_event()
def on_trade(self, trade: TradeData):
""""""
self.traded += trade.volume
if self.traded >= self.volume:
self.write_log(f"已交易数量:{self.traded},总数量:{self.volume}")
self.stop()
else:
self.put_variables_event()

View File

@ -0,0 +1,105 @@
from vnpy.trader.constant import Offset, Direction
from vnpy.trader.object import TradeData
from vnpy.trader.engine import BaseEngine
from vnpy.app.algo_trading import AlgoTemplate
class TwapAlgo(AlgoTemplate):
""""""
display_name = "TWAP 时间加权平均"
default_setting = {
"vt_symbol": "",
"direction": [Direction.LONG.value, Direction.SHORT.value],
"price": 0.0,
"volume": 0.0,
"time": 600,
"interval": 60,
"offset": [
Offset.NONE.value,
Offset.OPEN.value,
Offset.CLOSE.value,
Offset.CLOSETODAY.value,
Offset.CLOSEYESTERDAY.value
]
}
variables = [
"traded",
"order_volume",
"timer_count",
"total_count"
]
def __init__(
self,
algo_engine: BaseEngine,
algo_name: str,
setting: dict
):
""""""
super().__init__(algo_engine, algo_name, setting)
# Parameters
self.vt_symbol = setting["vt_symbol"]
self.direction = Direction(setting["direction"])
self.price = setting["price"]
self.volume = setting["volume"]
self.time = setting["time"]
self.interval = setting["interval"]
self.offset = Offset(setting["offset"])
# Variables
self.order_volume = self.volume / (self.time / self.interval)
self.timer_count = 0
self.total_count = 0
self.traded = 0
self.subscribe(self.vt_symbol)
self.put_parameters_event()
self.put_variables_event()
def on_trade(self, trade: TradeData):
""""""
self.traded += trade.volume
if self.traded >= self.volume:
self.write_log(f"已交易数量:{self.traded},总数量:{self.volume}")
self.stop()
else:
self.put_variables_event()
def on_timer(self):
""""""
self.timer_count += 1
self.total_count += 1
self.put_variables_event()
if self.total_count >= self.time:
self.write_log("执行时间已结束,停止算法")
self.stop()
return
if self.timer_count < self.interval:
return
self.timer_count = 0
tick = self.get_tick(self.vt_symbol)
if not tick:
return
self.cancel_all()
left_volume = self.volume - self.traded
order_volume = min(self.order_volume, left_volume)
if self.direction == Direction.LONG:
if tick.ask_price_1 <= self.price:
self.buy(self.vt_symbol, self.price,
order_volume, offset=self.offset)
else:
if tick.bid_price_1 >= self.price:
self.sell(self.vt_symbol, self.price,
order_volume, offset=self.offset)

View File

@ -1,65 +1,265 @@
from vnpy.event import EventEngine
from vnpy.event import EventEngine, Event
from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.event import (EVENT_TICK, EVENT_TIMER, EVENT_ORDER, EVENT_TRADE)
from vnpy.trader.event import (
EVENT_TICK, EVENT_TIMER, EVENT_ORDER, EVENT_TRADE)
from vnpy.trader.constant import (Direction, Offset, OrderType)
from vnpy.trader.object import (SubscribeRequest, OrderRequest)
from vnpy.trader.utility import load_json, save_json
from .template import AlgoTemplate
APP_NAME = "AlgoTrading"
EVENT_ALGO_LOG = "eAlgoLog"
EVENT_ALGO_SETTING = "eAlgoSetting"
EVENT_ALGO_VARIABLES = "eAlgoVariables"
EVENT_ALGO_PARAMETERS = "eAlgoParameters"
class AlgoEngine(BaseEngine):
""""""
setting_filename = "algo_trading_setting.json"
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
"""Constructor"""
super().__init__(main_engine, event_engine)
super().__init__(main_engine, event_engine, APP_NAME)
self.algos = {}
self.symbol_algo_map = {}
self.orderid_algo_map = {}
self.algo_templates = {}
self.algo_settings = {}
self.load_algo_template()
self.register_event()
def init_engine(self):
""""""
self.write_log("算法交易引擎启动")
self.load_algo_setting()
def load_algo_template(self):
""""""
from .algos.twap_algo import TwapAlgo
from .algos.iceberg_algo import IcebergAlgo
from .algos.sniper_algo import SniperAlgo
self.add_algo_template(TwapAlgo)
self.add_algo_template(IcebergAlgo)
self.add_algo_template(SniperAlgo)
def add_algo_template(self, template: AlgoTemplate):
""""""
self.algo_templates[template.__name__] = template
def load_algo_setting(self):
""""""
self.algo_settings = load_json(self.setting_filename)
for setting_name, setting in self.algo_settings.items():
self.put_setting_event(setting_name, setting)
self.write_log("算法配置载入成功")
def save_algo_setting(self):
""""""
save_json(self.setting_filename, self.algo_settings)
def register_event(self):
""""""
self.event_engine.register(EVENT_TICK, self.process_tick_event)
self.event_engine.register(EVENT_TIMER, self.process_timer_event)
self.event_engine.register(EVENT_ORDER, self.process_order_event)
self.event_engine.register(EVENT_TRADE, self.process_trade_event)
def process_tick_event(self):
def process_tick_event(self, event: Event):
""""""
pass
def process_timer_event(self):
tick = event.data
algos = self.symbol_algo_map.get(tick.vt_symbol, None)
if algos:
for algo in algos:
algo.update_tick(tick)
def process_timer_event(self, event: Event):
""""""
pass
def process_trade_event(self):
for algo in self.algos.values():
algo.update_timer()
def process_trade_event(self, event: Event):
""""""
pass
def process_order_event(self):
trade = event.data
algo = self.orderid_algo_map.get(trade.vt_orderid, None)
if algo:
algo.update_trade(trade)
def process_order_event(self, event: Event):
""""""
pass
order = event.data
algo = self.orderid_algo_map.get(order.vt_orderid, None)
if algo:
algo.update_order(order)
def start_algo(self, setting: dict):
""""""
pass
def stop_algo(self, algo_name: dict):
template_name = setting["template_name"]
algo_template = self.algo_templates[template_name]
algo = algo_template.new(self, setting)
algo.start()
self.algos[algo.algo_name] = algo
return algo.algo_name
def stop_algo(self, algo_name: str):
""""""
pass
algo = self.algos.get(algo_name, None)
if algo:
algo.stop()
self.algos.pop(algo_name)
def stop_all(self):
""""""
pass
def subscribe(self, algo, vt_symbol):
for algo_name in list(self.algos.keys()):
self.stop_algo(algo_name)
def subscribe(self, algo: AlgoTemplate, vt_symbol: str):
""""""
pass
contract = self.main_engine.get_contract(vt_symbol)
if not contract:
self.write_log(f'订阅行情失败,找不到合约:{vt_symbol}', algo)
return
algos = self.symbol_algo_map.setdefault(vt_symbol, set())
if not algos:
req = SubscribeRequest(
symbol=contract.symbol,
exchange=contract.exchange
)
self.main_engine.subscribe(req, contract.gateway_name)
algos.add(algo)
def send_order(
self,
algo,
vt_symbol
self,
algo: AlgoTemplate,
vt_symbol: str,
direction: Direction,
price: float,
volume: float,
order_type: OrderType,
offset: Offset
):
""""""
pass
contract = self.main_engine.get_contract(vt_symbol)
if not contract:
self.write_log(f'委托下单失败,找不到合约:{vt_symbol}', algo)
return
req = OrderRequest(
symbol=contract.symbol,
exchange=contract.exchange,
direction=direction,
type=order_type,
volume=volume,
price=price,
offset=offset
)
vt_orderid = self.main_engine.send_order(req, contract.gateway_name)
self.orderid_algo_map[vt_orderid] = algo
return vt_orderid
def cancel_order(self, algo: AlgoTemplate, vt_orderid: str):
""""""
order = self.main_engine.get_order(vt_orderid)
if not order:
self.write_log(f"委托撤单失败,找不到委托:{vt_orderid}", algo)
return
req = order.create_cancel_request()
self.main_engine.cancel_order(req, order.gateway_name)
def get_tick(self, algo: AlgoTemplate, vt_symbol: str):
""""""
tick = self.main_engine.get_tick(vt_symbol)
if not tick:
self.write_log(f"查询行情失败,找不到行情:{vt_symbol}", algo)
return tick
def get_contract(self, algo: AlgoTemplate, vt_symbol: str):
""""""
contract = self.main_engine.get_contract(vt_symbol)
if not contract:
self.write_log(f"查询合约失败,找不到合约:{vt_symbol}", algo)
return contract
def write_log(self, msg: str, algo: AlgoTemplate = None):
""""""
if algo:
msg = f"{algo.algo_name}{msg}"
event = Event(EVENT_ALGO_LOG)
event.data = msg
self.event_engine.put(event)
def put_setting_event(self, setting_name: str, setting: dict):
""""""
event = Event(EVENT_ALGO_SETTING)
event.data = {
"setting_name": setting_name,
"setting": setting
}
self.event_engine.put(event)
def update_algo_setting(self, setting_name: str, setting: dict):
""""""
self.algo_settings[setting_name] = setting
self.save_algo_setting()
self.put_setting_event(setting_name, setting)
def remove_algo_setting(self, setting_name: str):
""""""
if setting_name not in self.algo_settings:
return
self.algo_settings.pop(setting_name)
event = Event(EVENT_ALGO_SETTING)
event.data = {
"setting_name": setting_name,
"setting": None
}
self.event_engine.put(event)
self.save_algo_setting()
def put_parameters_event(self, algo: AlgoTemplate, parameters: dict):
""""""
event = Event(EVENT_ALGO_PARAMETERS)
event.data = {
"algo_name": algo.algo_name,
"parameters": parameters
}
self.event_engine.put(event)
def put_variables_event(self, algo: AlgoTemplate, variables: dict):
""""""
event = Event(EVENT_ALGO_VARIABLES)
event.data = {
"algo_name": algo.algo_name,
"variables": variables
}
self.event_engine.put(event)

View File

@ -1,30 +1,38 @@
from vnpy.trader.engine import BaseEngine
from vnpy.trader.object import TickData, OrderData, TradeData
from vnpy.trader.constant import OrderType, Offset
from vnpy.trader.constant import OrderType, Offset, Direction
class AlgoTemplate:
""""""
count = 0
_count = 0
display_name = ""
default_setting = {}
variables = []
def __init__(
self,
algo_engine: BaseEngine,
self,
algo_engine: BaseEngine,
algo_name: str,
setting: dict
):
"""Constructor"""
self.algo_engine = algo_engine
self.algo_name = algo_name
self.active = False
self.active_orders = {} # vt_orderid:order
@staticmethod
def new(cls, algo_engine:BaseEngine, setting: dict):
self.active = False
self.active_orders = {} # vt_orderid:order
self.variables.insert(0, "active")
@classmethod
def new(cls, algo_engine: BaseEngine, setting: dict):
"""Create new algo instance"""
cls.count += 1
algo_name = f"{cls.__name__}_{cls.count}"
cls._count += 1
algo_name = f"{cls.__name__}_{cls._count}"
algo = cls(algo_engine, algo_name, setting)
return algo
def update_tick(self, tick: TickData):
""""""
@ -38,27 +46,27 @@ class AlgoTemplate:
self.active_orders[order.vt_orderid] = order
elif order.vt_orderid in self.active_orders:
self.active_orders.pop(order.vt_orderid)
self.on_order(order)
def update_trade(self, trade: TradeData):
""""""
if self.active:
self.on_trade(trade)
def update_timer(self):
""""""
if self.active:
self.on_timer()
def on_start(self):
""""""
pass
def on_stop(self):
""""""
pass
def on_tick(self, tick: TickData):
""""""
pass
@ -66,58 +74,114 @@ class AlgoTemplate:
def on_order(self, order: OrderData):
""""""
pass
def on_trade(self, trade: TradeData):
""""""
pass
def on_timer(self):
""""""
pass
pass
def start(self):
""""""
pass
self.active = True
self.on_start()
self.put_variables_event()
def stop(self):
""""""
pass
self.active = False
self.cancel_all()
self.on_stop()
self.put_variables_event()
self.write_log("停止算法")
def subscribe(self, vt_symbol):
""""""
self.algo_engine.subscribe(self, vt_symbol)
def buy(
self,
vt_symbol,
price,
volume,
self,
vt_symbol,
price,
volume,
order_type: OrderType = OrderType.LIMIT,
offset: Offset = Offset.NONE
):
""""""
return self.algo_engine.buy(
msg = f"委托买入{vt_symbol}{volume}@{price}"
self.write_log(msg)
return self.algo_engine.send_order(
self,
vt_symbol,
Direction.LONG,
price,
volume,
order_type,
offset
)
def sell(
self,
vt_symbol,
price,
volume,
self,
vt_symbol,
price,
volume,
order_type: OrderType = OrderType.LIMIT,
offset: Offset = Offset.NONE
):
""""""
return self.algo_engine.buy(
msg = f"委托卖出{vt_symbol}{volume}@{price}"
self.write_log(msg)
return self.algo_engine.send_order(
self,
vt_symbol,
Direction.SHORT,
price,
volume,
order_type,
offset
)
)
def cancel_order(self, vt_orderid: str):
""""""
self.algo_engine.cancel_order(self, vt_orderid)
def cancel_all(self):
""""""
if not self.active_orders:
return
for vt_orderid in self.active_orders.keys():
self.cancel_order(vt_orderid)
def get_tick(self, vt_symbol: str):
""""""
return self.algo_engine.get_tick(self, vt_symbol)
def get_contract(self, vt_symbol: str):
""""""
return self.algo_engine.get_contract(self, vt_symbol)
def write_log(self, msg: str):
""""""
self.algo_engine.write_log(msg, self)
def put_parameters_event(self):
""""""
parameters = {}
for name in self.default_setting.keys():
parameters[name] = getattr(self, name)
self.algo_engine.put_parameters_event(self, parameters)
def put_variables_event(self):
""""""
variables = {}
for name in self.variables:
variables[name] = getattr(self, name)
self.algo_engine.put_variables_event(self, variables)

View File

@ -0,0 +1 @@
from .widget import AlgoManager

View File

@ -0,0 +1,16 @@
NAME_DISPLAY_MAP = {
"vt_symbol": "本地代码",
"direction": "方向",
"price": "价格",
"volume": "数量",
"time": "执行时间(秒)",
"interval": "每轮间隔(秒)",
"offset": "开平",
"active": "算法状态",
"traded": "成交数量",
"order_volume": "单笔委托",
"timer_count": "本轮读秒",
"total_count": "累计读秒",
"template_name": "算法模板",
"display_volume": "挂出数量"
}

View File

@ -0,0 +1,575 @@
"""
Widget for algo trading.
"""
from functools import partial
from datetime import datetime
from vnpy.event import EventEngine, Event
from vnpy.trader.engine import MainEngine
from vnpy.trader.ui import QtWidgets, QtCore
from ..engine import (
AlgoEngine,
AlgoTemplate,
APP_NAME,
EVENT_ALGO_LOG,
EVENT_ALGO_PARAMETERS,
EVENT_ALGO_VARIABLES,
EVENT_ALGO_SETTING
)
from .display import NAME_DISPLAY_MAP
class AlgoWidget(QtWidgets.QWidget):
"""
Start connection of a certain gateway.
"""
def __init__(
self,
algo_engine: AlgoEngine,
algo_template: AlgoTemplate
):
""""""
super().__init__()
self.algo_engine = algo_engine
self.template_name = algo_template.__name__
self.default_setting = algo_template.default_setting
self.widgets = {}
self.init_ui()
def init_ui(self):
"""
Initialize line edits and form layout based on setting.
"""
self.setMaximumWidth(400)
form = QtWidgets.QFormLayout()
for field_name, field_value in self.default_setting.items():
field_type = type(field_value)
if field_type == list:
widget = QtWidgets.QComboBox()
widget.addItems(field_value)
else:
widget = QtWidgets.QLineEdit()
display_name = NAME_DISPLAY_MAP.get(field_name, field_name)
form.addRow(display_name, widget)
self.widgets[field_name] = (widget, field_type)
start_algo_button = QtWidgets.QPushButton("启动算法")
start_algo_button.clicked.connect(self.start_algo)
form.addRow(start_algo_button)
form.addRow(QtWidgets.QLabel(""))
self.setting_name_line = QtWidgets.QLineEdit()
form.addRow("配置名称", self.setting_name_line)
save_setting_button = QtWidgets.QPushButton("保存配置")
save_setting_button.clicked.connect(self.save_setting)
form.addRow(save_setting_button)
self.setLayout(form)
def get_setting(self):
"""
Get setting value from line edits.
"""
setting = {"template_name": self.template_name}
for field_name, tp in self.widgets.items():
widget, field_type = tp
if field_type == list:
field_value = str(widget.currentText())
else:
try:
field_value = field_type(widget.text())
except ValueError:
display_name = NAME_DISPLAY_MAP.get(field_name, field_name)
QtWidgets.QMessageBox.warning(
self,
"参数错误",
f"{display_name}参数类型应为{field_type},请检查!"
)
return None
setting[field_name] = field_value
return setting
def start_algo(self):
"""
Start algo trading.
"""
setting = self.get_setting()
if setting:
self.algo_engine.start_algo(setting)
def update_setting(self, setting_name: str, setting: dict):
"""
Update setting into widgets.
"""
self.setting_name_line.setText(setting_name)
for name, tp in self.widgets.items():
widget, _ = tp
value = setting[name]
if isinstance(widget, QtWidgets.QLineEdit):
widget.setText(str(value))
elif isinstance(widget, QtWidgets.QComboBox):
ix = widget.findText(value)
widget.setCurrentIndex(ix)
def save_setting(self):
"""
Save algo setting
"""
setting_name = self.setting_name_line.text()
if not setting_name:
return
setting = self.get_setting()
if setting:
self.algo_engine.update_algo_setting(setting_name, setting)
class AlgoMonitor(QtWidgets.QTableWidget):
""""""
parameters_signal = QtCore.pyqtSignal(Event)
variables_signal = QtCore.pyqtSignal(Event)
def __init__(
self,
algo_engine: AlgoEngine,
event_engine: EventEngine,
mode_active: bool
):
""""""
super().__init__()
self.algo_engine = algo_engine
self.event_engine = event_engine
self.mode_active = mode_active
self.algo_cells = {}
self.init_ui()
self.register_event()
def init_ui(self):
""""""
labels = [
"",
"算法",
"参数",
"状态"
]
self.setColumnCount(len(labels))
self.setHorizontalHeaderLabels(labels)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
self.verticalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.ResizeToContents
)
for column in range(2, 4):
self.horizontalHeader().setSectionResizeMode(
column,
QtWidgets.QHeaderView.Stretch
)
self.setWordWrap(True)
if not self.mode_active:
self.hideColumn(0)
def register_event(self):
""""""
self.parameters_signal.connect(self.process_parameters_event)
self.variables_signal.connect(self.process_variables_event)
self.event_engine.register(
EVENT_ALGO_PARAMETERS, self.parameters_signal.emit)
self.event_engine.register(
EVENT_ALGO_VARIABLES, self.variables_signal.emit)
def process_parameters_event(self, event):
""""""
data = event.data
algo_name = data["algo_name"]
parameters = data["parameters"]
cells = self.get_algo_cells(algo_name)
text = to_text(parameters)
cells["parameters"].setText(text)
def process_variables_event(self, event):
""""""
data = event.data
algo_name = data["algo_name"]
variables = data["variables"]
cells = self.get_algo_cells(algo_name)
variables_cell = cells["variables"]
text = to_text(variables)
variables_cell.setText(text)
row = self.row(variables_cell)
active = variables["active"]
if self.mode_active:
if active:
self.showRow(row)
else:
self.hideRow(row)
else:
if active:
self.hideRow(row)
else:
self.showRow(row)
def stop_algo(self, algo_name: str):
""""""
self.algo_engine.stop_algo(algo_name)
def get_algo_cells(self, algo_name: str):
""""""
cells = self.algo_cells.get(algo_name, None)
if not cells:
stop_func = partial(self.stop_algo, algo_name=algo_name)
stop_button = QtWidgets.QPushButton("停止")
stop_button.clicked.connect(stop_func)
name_cell = QtWidgets.QTableWidgetItem(algo_name)
parameters_cell = QtWidgets.QTableWidgetItem()
variables_cell = QtWidgets.QTableWidgetItem()
self.insertRow(0)
self.setCellWidget(0, 0, stop_button)
self.setItem(0, 1, name_cell)
self.setItem(0, 2, parameters_cell)
self.setItem(0, 3, variables_cell)
cells = {
"name": name_cell,
"parameters": parameters_cell,
"variables": variables_cell
}
self.algo_cells[algo_name] = cells
return cells
class ActiveAlgoMonitor(AlgoMonitor):
"""
Monitor for active algos.
"""
def __init__(self, algo_engine: AlgoEngine, event_engine: EventEngine):
""""""
super().__init__(algo_engine, event_engine, True)
class InactiveAlgoMonitor(AlgoMonitor):
"""
Monitor for inactive algos.
"""
def __init__(self, algo_engine: AlgoEngine, event_engine: EventEngine):
""""""
super().__init__(algo_engine, event_engine, False)
class SettingMonitor(QtWidgets.QTableWidget):
""""""
setting_signal = QtCore.pyqtSignal(Event)
use_signal = QtCore.pyqtSignal(dict)
def __init__(self, algo_engine: AlgoEngine, event_engine: EventEngine):
""""""
super().__init__()
self.algo_engine = algo_engine
self.event_engine = event_engine
self.settings = {}
self.setting_cells = {}
self.init_ui()
self.register_event()
def init_ui(self):
""""""
labels = [
"",
"",
"名称",
"配置"
]
self.setColumnCount(len(labels))
self.setHorizontalHeaderLabels(labels)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
self.verticalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.ResizeToContents
)
self.horizontalHeader().setSectionResizeMode(
3,
QtWidgets.QHeaderView.Stretch
)
self.setWordWrap(True)
def register_event(self):
""""""
self.setting_signal.connect(self.process_setting_event)
self.event_engine.register(
EVENT_ALGO_SETTING, self.setting_signal.emit)
def process_setting_event(self, event):
""""""
data = event.data
setting_name = data["setting_name"]
setting = data["setting"]
cells = self.get_setting_cells(setting_name)
if setting:
self.settings[setting_name] = setting
cells["setting"].setText(to_text(setting))
else:
if setting_name in self.settings:
self.settings.pop(setting_name)
row = self.row(cells["setting"])
self.removeRow(row)
self.setting_cells.pop(setting_name)
def get_setting_cells(self, setting_name: str):
""""""
cells = self.setting_cells.get(setting_name, None)
if not cells:
use_func = partial(self.use_setting, setting_name=setting_name)
use_button = QtWidgets.QPushButton("使用")
use_button.clicked.connect(use_func)
remove_func = partial(self.remove_setting,
setting_name=setting_name)
remove_button = QtWidgets.QPushButton("移除")
remove_button.clicked.connect(remove_func)
name_cell = QtWidgets.QTableWidgetItem(setting_name)
setting_cell = QtWidgets.QTableWidgetItem()
self.insertRow(0)
self.setCellWidget(0, 0, use_button)
self.setCellWidget(0, 1, remove_button)
self.setItem(0, 2, name_cell)
self.setItem(0, 3, setting_cell)
cells = {
"name": name_cell,
"setting": setting_cell
}
self.setting_cells[setting_name] = cells
return cells
def use_setting(self, setting_name: str):
""""""
setting = self.settings[setting_name]
setting["setting_name"] = setting_name
self.use_signal.emit(setting)
def remove_setting(self, setting_name: str):
""""""
self.algo_engine.remove_algo_setting(setting_name)
class LogMonitor(QtWidgets.QTableWidget):
""""""
signal = QtCore.pyqtSignal(Event)
def __init__(self, event_engine: EventEngine):
""""""
super().__init__()
self.event_engine = event_engine
self.init_ui()
self.register_event()
def init_ui(self):
""""""
labels = [
"时间",
"信息"
]
self.setColumnCount(len(labels))
self.setHorizontalHeaderLabels(labels)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
self.verticalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.ResizeToContents
)
self.horizontalHeader().setSectionResizeMode(
1,
QtWidgets.QHeaderView.Stretch
)
self.setWordWrap(True)
def register_event(self):
""""""
self.signal.connect(self.process_log_event)
self.event_engine.register(EVENT_ALGO_LOG, self.signal.emit)
def process_log_event(self, event):
""""""
msg = event.data
timestamp = datetime.now().strftime("%H:%M:%S")
timestamp_cell = QtWidgets.QTableWidgetItem(timestamp)
msg_cell = QtWidgets.QTableWidgetItem(msg)
self.insertRow(0)
self.setItem(0, 0, timestamp_cell)
self.setItem(0, 1, msg_cell)
class AlgoManager(QtWidgets.QWidget):
""""""
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
""""""
super().__init__()
self.main_engine = main_engine
self.event_engine = event_engine
self.algo_engine = main_engine.get_engine(APP_NAME)
self.algo_widgets = {}
self.init_ui()
self.algo_engine.init_engine()
def init_ui(self):
""""""
self.setWindowTitle("算法交易")
# Left side control widgets
self.template_combo = QtWidgets.QComboBox()
self.template_combo.currentIndexChanged.connect(self.show_algo_widget)
form = QtWidgets.QFormLayout()
form.addRow("算法", self.template_combo)
widget = QtWidgets.QWidget()
widget.setLayout(form)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(widget)
for algo_template in self.algo_engine.algo_templates.values():
widget = AlgoWidget(self.algo_engine, algo_template)
vbox.addWidget(widget)
template_name = algo_template.__name__
display_name = algo_template.display_name
self.algo_widgets[template_name] = widget
self.template_combo.addItem(display_name, template_name)
vbox.addStretch()
stop_all_button = QtWidgets.QPushButton("全部停止")
stop_all_button.setFixedHeight(stop_all_button.sizeHint().height() * 2)
stop_all_button.clicked.connect(self.algo_engine.stop_all)
vbox.addWidget(stop_all_button)
# Right side monitor widgets
active_algo_monitor = ActiveAlgoMonitor(
self.algo_engine, self.event_engine
)
inactive_algo_monitor = InactiveAlgoMonitor(
self.algo_engine, self.event_engine
)
tab1 = QtWidgets.QTabWidget()
tab1.addTab(active_algo_monitor, "执行中")
tab1.addTab(inactive_algo_monitor, "已结束")
log_monitor = LogMonitor(self.event_engine)
tab2 = QtWidgets.QTabWidget()
tab2.addTab(log_monitor, "日志")
setting_monitor = SettingMonitor(self.algo_engine, self.event_engine)
setting_monitor.use_signal.connect(self.use_setting)
tab3 = QtWidgets.QTabWidget()
tab3.addTab(setting_monitor, "配置")
grid = QtWidgets.QGridLayout()
grid.addWidget(tab1, 0, 0, 1, 2)
grid.addWidget(tab2, 1, 0)
grid.addWidget(tab3, 1, 1)
hbox2 = QtWidgets.QHBoxLayout()
hbox2.addLayout(vbox)
hbox2.addLayout(grid)
self.setLayout(hbox2)
self.show_algo_widget()
def show_algo_widget(self):
""""""
ix = self.template_combo.currentIndex()
current_name = self.template_combo.itemData(ix)
for template_name, widget in self.algo_widgets.items():
if template_name == current_name:
widget.show()
else:
widget.hide()
def use_setting(self, setting: dict):
""""""
setting_name = setting["setting_name"]
template_name = setting["template_name"]
widget = self.algo_widgets[template_name]
widget.update_setting(setting_name, setting)
ix = self.template_combo.findData(template_name)
self.template_combo.setCurrentIndex(ix)
self.show_algo_widget()
def show(self):
""""""
self.showMaximized()
def to_text(data: dict):
"""
Convert dict data into string.
"""
buf = []
for key, value in data.items():
key = NAME_DISPLAY_MAP.get(key, key)
buf.append(f"{key}{value}")
text = "".join(buf)
return text

View File

@ -40,6 +40,7 @@ from vnpy.trader.database import DbTickData, DbBarData
from vnpy.trader.setting import SETTINGS
from .base import (
APP_NAME,
EVENT_CTA_LOG,
EVENT_CTA_STRATEGY,
EVENT_CTA_STOPORDER,
@ -73,7 +74,7 @@ class CtaEngine(BaseEngine):
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
""""""
super(CtaEngine, self).__init__(
main_engine, event_engine, "CtaStrategy")
main_engine, event_engine, APP_NAME)
self.strategy_setting = {} # strategy_name: dict
self.strategy_data = {} # strategy_name: dict

View File

@ -15,7 +15,7 @@ from copy import copy
from datetime import datetime
from vnpy.event import Event
from vnpy.api.rest import RestClient
from vnpy.api.rest import RestClient, Request
from vnpy.api.websocket import WebsocketClient
from vnpy.trader.constant import (
Direction,
@ -268,13 +268,15 @@ class HuobiRestApi(RestClient):
"price": str(req.price),
"source": "api"
}
self.add_request(
method="POST",
path="/v1/order/orders/place",
callback=self.on_send_order,
data=data,
extra=order,
on_error=self.on_send_order_error,
on_failed=self.on_send_order_failed
)
self.order_manager.on_order(order)
@ -283,15 +285,15 @@ class HuobiRestApi(RestClient):
def cancel_order(self, req: CancelRequest):
""""""
sys_orderid = self.order_manager.get_sys_orderid(req.orderid)
path = f"/v1/order/orders/{sys_orderid}/submitcancel"
path = f"/v1/order/orders/{sys_orderid}/submitcancel"
self.add_request(
method="POST",
path=path,
method="POST",
path=path,
callback=self.on_cancel_order,
extra=req
)
def on_query_account(self, data, request):
""""""
if self.check_error(data, "查询账户"):
@ -300,8 +302,8 @@ class HuobiRestApi(RestClient):
for d in data["data"]:
if d["type"] == "spot":
self.account_id = d["id"]
self.gateway.write_log(f"账户代码{self.account_id}查询成功")
self.gateway.write_log(f"账户代码{self.account_id}查询成功")
self.query_account_balance()
def on_query_account_balance(self, data, request):
@ -327,7 +329,7 @@ class HuobiRestApi(RestClient):
self.gateway.on_account(account)
def on_query_order(self, data, request):
""""""
""""""
if self.check_error(data, "查询委托"):
return
@ -354,7 +356,7 @@ class HuobiRestApi(RestClient):
)
self.order_manager.on_order(order)
self.gateway.write_log("委托信息查询成功")
def on_query_contract(self, data, request): # type: (dict, Request)->None
@ -379,7 +381,7 @@ class HuobiRestApi(RestClient):
gateway_name=self.gateway_name,
)
self.gateway.on_contract(contract)
huobi_symbols.add(contract.symbol)
symbol_name_map[contract.symbol] = contract.name
@ -388,7 +390,7 @@ class HuobiRestApi(RestClient):
def on_send_order(self, data, request):
""""""
order = request.extra
if self.check_error(data, "委托"):
order.status = Status.REJECTED
self.order_manager.on_order(order)
@ -396,21 +398,46 @@ class HuobiRestApi(RestClient):
sys_orderid = data["data"]
self.order_manager.update_orderid_map(order.orderid, sys_orderid)
def on_send_order_failed(self, status_code: str, request: Request):
"""
Callback when sending order failed on server.
"""
order = request.extra
order.status = Status.REJECTED
self.gateway.on_order(order)
msg = f"委托失败,状态码:{status_code},信息:{request.response.text}"
self.gateway.write_log(msg)
def on_send_order_error(
self, exception_type: type, exception_value: Exception, tb, request: Request
):
"""
Callback when sending order caused exception.
"""
order = request.extra
order.status = Status.REJECTED
self.gateway.on_order(order)
# Record exception if not ConnectionError
if not issubclass(exception_type, ConnectionError):
self.on_error(exception_type, exception_value, tb, request)
def on_cancel_order(self, data, request):
""""""
if self.check_error(data, "撤单"):
return
cancel_request = request.extra
local_orderid = cancel_request.orderid
order = self.order_manager.get_order_with_local_orderid(local_orderid)
order.status = Status.CANCELLED
if self.check_error(data, "撤单"):
order.status = Status.REJECTED
else:
order.status = Status.CANCELLED
self.gateway.write_log(f"委托撤单成功:{order.orderid}")
self.order_manager.on_order(order)
self.gateway.write_log(f"委托撤单成功:{order.orderid}")
def check_error(self, data: dict, func: str = ""):
""""""
if data["status"] != "error":