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.cta_strategy import CtaStrategyApp
from vnpy.app.csv_loader import CsvLoaderApp from vnpy.app.csv_loader import CsvLoaderApp
from vnpy.app.algo_trading import AlgoTradingApp
def main(): def main():
@ -35,6 +36,7 @@ def main():
main_engine.add_app(CtaStrategyApp) main_engine.add_app(CtaStrategyApp)
main_engine.add_app(CsvLoaderApp) main_engine.add_app(CsvLoaderApp)
main_engine.add_app(AlgoTradingApp)
main_window = MainWindow(main_engine, event_engine) main_window = MainWindow(main_engine, event_engine)
main_window.showMaximized() main_window.showMaximized()

View File

@ -3,9 +3,10 @@ from pathlib import Path
from vnpy.trader.app import BaseApp from vnpy.trader.app import BaseApp
from .engine import AlgoEngine, APP_NAME from .engine import AlgoEngine, APP_NAME
from .template import AlgoTemplate
class CtaStrategyApp(BaseApp): class AlgoTradingApp(BaseApp):
"""""" """"""
app_name = APP_NAME 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.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): class AlgoEngine(BaseEngine):
"""""" """"""
setting_filename = "algo_trading_setting.json"
def __init__(self, main_engine: MainEngine, event_engine: EventEngine): def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
"""Constructor""" """Constructor"""
super().__init__(main_engine, event_engine) super().__init__(main_engine, event_engine, APP_NAME)
self.algos = {} self.algos = {}
self.symbol_algo_map = {} self.symbol_algo_map = {}
self.orderid_algo_map = {} self.orderid_algo_map = {}
self.algo_templates = {}
self.algo_settings = {}
self.load_algo_template()
self.register_event() 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): def register_event(self):
"""""" """"""
self.event_engine.register(EVENT_TICK, self.process_tick_event) 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_TIMER, self.process_timer_event)
self.event_engine.register(EVENT_ORDER, self.process_order_event) self.event_engine.register(EVENT_ORDER, self.process_order_event)
self.event_engine.register(EVENT_TRADE, self.process_trade_event) self.event_engine.register(EVENT_TRADE, self.process_trade_event)
def process_tick_event(self): def process_tick_event(self, event: Event):
"""""" """"""
pass tick = event.data
def process_timer_event(self): 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 for algo in self.algos.values():
algo.update_timer()
def process_trade_event(self):
def process_trade_event(self, event: Event):
"""""" """"""
pass trade = event.data
def process_order_event(self): 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): def start_algo(self, setting: dict):
"""""" """"""
pass template_name = setting["template_name"]
algo_template = self.algo_templates[template_name]
def stop_algo(self, algo_name: dict):
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): def stop_all(self):
"""""" """"""
pass for algo_name in list(self.algos.keys()):
self.stop_algo(algo_name)
def subscribe(self, algo, vt_symbol):
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( def send_order(
self, self,
algo, algo: AlgoTemplate,
vt_symbol 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.engine import BaseEngine
from vnpy.trader.object import TickData, OrderData, TradeData 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: class AlgoTemplate:
"""""" """"""
count = 0
_count = 0
display_name = ""
default_setting = {}
variables = []
def __init__( def __init__(
self, self,
algo_engine: BaseEngine, algo_engine: BaseEngine,
algo_name: str, algo_name: str,
setting: dict setting: dict
): ):
"""Constructor""" """Constructor"""
self.algo_engine = algo_engine self.algo_engine = algo_engine
self.algo_name = algo_name self.algo_name = algo_name
self.active = False
self.active_orders = {} # vt_orderid:order
@staticmethod self.active = False
def new(cls, algo_engine:BaseEngine, setting: dict): self.active_orders = {} # vt_orderid:order
self.variables.insert(0, "active")
@classmethod
def new(cls, algo_engine: BaseEngine, setting: dict):
"""Create new algo instance""" """Create new algo instance"""
cls.count += 1 cls._count += 1
algo_name = f"{cls.__name__}_{cls.count}" algo_name = f"{cls.__name__}_{cls._count}"
algo = cls(algo_engine, algo_name, setting) algo = cls(algo_engine, algo_name, setting)
return algo
def update_tick(self, tick: TickData): def update_tick(self, tick: TickData):
"""""" """"""
@ -38,27 +46,27 @@ class AlgoTemplate:
self.active_orders[order.vt_orderid] = order self.active_orders[order.vt_orderid] = order
elif order.vt_orderid in self.active_orders: elif order.vt_orderid in self.active_orders:
self.active_orders.pop(order.vt_orderid) self.active_orders.pop(order.vt_orderid)
self.on_order(order) self.on_order(order)
def update_trade(self, trade: TradeData): def update_trade(self, trade: TradeData):
"""""" """"""
if self.active: if self.active:
self.on_trade(trade) self.on_trade(trade)
def update_timer(self): def update_timer(self):
"""""" """"""
if self.active: if self.active:
self.on_timer() self.on_timer()
def on_start(self): def on_start(self):
"""""" """"""
pass pass
def on_stop(self): def on_stop(self):
"""""" """"""
pass pass
def on_tick(self, tick: TickData): def on_tick(self, tick: TickData):
"""""" """"""
pass pass
@ -66,58 +74,114 @@ class AlgoTemplate:
def on_order(self, order: OrderData): def on_order(self, order: OrderData):
"""""" """"""
pass pass
def on_trade(self, trade: TradeData): def on_trade(self, trade: TradeData):
"""""" """"""
pass pass
def on_timer(self): def on_timer(self):
"""""" """"""
pass pass
def start(self): def start(self):
"""""" """"""
pass self.active = True
self.on_start()
self.put_variables_event()
def stop(self): 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( def buy(
self, self,
vt_symbol, vt_symbol,
price, price,
volume, volume,
order_type: OrderType = OrderType.LIMIT, order_type: OrderType = OrderType.LIMIT,
offset: Offset = Offset.NONE 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, vt_symbol,
Direction.LONG,
price, price,
volume, volume,
order_type, order_type,
offset offset
) )
def sell( def sell(
self, self,
vt_symbol, vt_symbol,
price, price,
volume, volume,
order_type: OrderType = OrderType.LIMIT, order_type: OrderType = OrderType.LIMIT,
offset: Offset = Offset.NONE 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, vt_symbol,
Direction.SHORT,
price, price,
volume, volume,
order_type, order_type,
offset 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 vnpy.trader.setting import SETTINGS
from .base import ( from .base import (
APP_NAME,
EVENT_CTA_LOG, EVENT_CTA_LOG,
EVENT_CTA_STRATEGY, EVENT_CTA_STRATEGY,
EVENT_CTA_STOPORDER, EVENT_CTA_STOPORDER,
@ -73,7 +74,7 @@ class CtaEngine(BaseEngine):
def __init__(self, main_engine: MainEngine, event_engine: EventEngine): def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
"""""" """"""
super(CtaEngine, self).__init__( super(CtaEngine, self).__init__(
main_engine, event_engine, "CtaStrategy") main_engine, event_engine, APP_NAME)
self.strategy_setting = {} # strategy_name: dict self.strategy_setting = {} # strategy_name: dict
self.strategy_data = {} # strategy_name: dict self.strategy_data = {} # strategy_name: dict

View File

@ -15,7 +15,7 @@ from copy import copy
from datetime import datetime from datetime import datetime
from vnpy.event import Event 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.api.websocket import WebsocketClient
from vnpy.trader.constant import ( from vnpy.trader.constant import (
Direction, Direction,
@ -268,13 +268,15 @@ class HuobiRestApi(RestClient):
"price": str(req.price), "price": str(req.price),
"source": "api" "source": "api"
} }
self.add_request( self.add_request(
method="POST", method="POST",
path="/v1/order/orders/place", path="/v1/order/orders/place",
callback=self.on_send_order, callback=self.on_send_order,
data=data, data=data,
extra=order, extra=order,
on_error=self.on_send_order_error,
on_failed=self.on_send_order_failed
) )
self.order_manager.on_order(order) self.order_manager.on_order(order)
@ -283,15 +285,15 @@ class HuobiRestApi(RestClient):
def cancel_order(self, req: CancelRequest): def cancel_order(self, req: CancelRequest):
"""""" """"""
sys_orderid = self.order_manager.get_sys_orderid(req.orderid) 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( self.add_request(
method="POST", method="POST",
path=path, path=path,
callback=self.on_cancel_order, callback=self.on_cancel_order,
extra=req extra=req
) )
def on_query_account(self, data, request): def on_query_account(self, data, request):
"""""" """"""
if self.check_error(data, "查询账户"): if self.check_error(data, "查询账户"):
@ -300,8 +302,8 @@ class HuobiRestApi(RestClient):
for d in data["data"]: for d in data["data"]:
if d["type"] == "spot": if d["type"] == "spot":
self.account_id = d["id"] 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() self.query_account_balance()
def on_query_account_balance(self, data, request): def on_query_account_balance(self, data, request):
@ -327,7 +329,7 @@ class HuobiRestApi(RestClient):
self.gateway.on_account(account) self.gateway.on_account(account)
def on_query_order(self, data, request): def on_query_order(self, data, request):
"""""" """"""
if self.check_error(data, "查询委托"): if self.check_error(data, "查询委托"):
return return
@ -354,7 +356,7 @@ class HuobiRestApi(RestClient):
) )
self.order_manager.on_order(order) self.order_manager.on_order(order)
self.gateway.write_log("委托信息查询成功") self.gateway.write_log("委托信息查询成功")
def on_query_contract(self, data, request): # type: (dict, Request)->None def on_query_contract(self, data, request): # type: (dict, Request)->None
@ -379,7 +381,7 @@ class HuobiRestApi(RestClient):
gateway_name=self.gateway_name, gateway_name=self.gateway_name,
) )
self.gateway.on_contract(contract) self.gateway.on_contract(contract)
huobi_symbols.add(contract.symbol) huobi_symbols.add(contract.symbol)
symbol_name_map[contract.symbol] = contract.name symbol_name_map[contract.symbol] = contract.name
@ -388,7 +390,7 @@ class HuobiRestApi(RestClient):
def on_send_order(self, data, request): def on_send_order(self, data, request):
"""""" """"""
order = request.extra order = request.extra
if self.check_error(data, "委托"): if self.check_error(data, "委托"):
order.status = Status.REJECTED order.status = Status.REJECTED
self.order_manager.on_order(order) self.order_manager.on_order(order)
@ -396,21 +398,46 @@ class HuobiRestApi(RestClient):
sys_orderid = data["data"] sys_orderid = data["data"]
self.order_manager.update_orderid_map(order.orderid, sys_orderid) 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): def on_cancel_order(self, data, request):
"""""" """"""
if self.check_error(data, "撤单"):
return
cancel_request = request.extra cancel_request = request.extra
local_orderid = cancel_request.orderid local_orderid = cancel_request.orderid
order = self.order_manager.get_order_with_local_orderid(local_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.order_manager.on_order(order)
self.gateway.write_log(f"委托撤单成功:{order.orderid}")
def check_error(self, data: dict, func: str = ""): def check_error(self, data: dict, func: str = ""):
"""""" """"""
if data["status"] != "error": if data["status"] != "error":