[Add] LogMonitor for cta strategy and global excepthook

This commit is contained in:
vn.py 2019-01-20 21:34:38 +08:00
parent 4596f3a515
commit a257b18f84
7 changed files with 382 additions and 203 deletions

View File

@ -2,6 +2,7 @@ from pathlib import Path
from vnpy.trader.app import BaseApp from vnpy.trader.app import BaseApp
from .engine import CtaEngine from .engine import CtaEngine
from .template import CtaTemplate
from .base import APP_NAME from .base import APP_NAME

View File

@ -47,31 +47,32 @@ class CtaEngine(BaseEngine):
event_engine, event_engine,
"CtaStrategy") "CtaStrategy")
self._engine_type = EngineType.LIVE # live trading engine self.engine_type = EngineType.LIVE # live trading engine
self.setting_file = None # setting file object self.setting_file = None # setting file object
self._strategy_classes = {} # class_name: stategy_class self.classes = {} # class_name: stategy_class
self._strategies = {} # name: strategy self.strategies = {} # strategy_name: strategy
self._symbol_strategy_map = defaultdict(list) # vt_symbol: strategy list self.symbol_strategy_map = defaultdict(list) # vt_symbol: strategy list
self._orderid_strategy_map = {} # vt_orderid: strategy self.orderid_strategy_map = {} # vt_orderid: strategy
self.strategy_orderid_map = defaultdict(
set
) # strategy_name: orderid list
self._active_orderids = defaultdict(set) # name: active orderid list self.stop_order_count = 0 # for generating stop_orderid
self.stop_orders = {} # stop_orderid: stop_order
self._stop_order_count = 0 # for generating stop_orderid
self._stop_orders = {} # stop_orderid: stop_order
def init_engine(self): def init_engine(self):
""" """
""" """
self.load_strategy_class() self.load_strategy_class()
self.load_setting() self.load_strategy_setting()
self.register_event() self.register_event()
self.write_log("CTA策略引擎初始化成功") self.write_log("CTA策略引擎初始化成功")
def close(self): def close(self):
"""""" """"""
self.save_setting() self.save_strategy_setting()
def register_event(self): def register_event(self):
"""""" """"""
@ -83,26 +84,26 @@ class CtaEngine(BaseEngine):
"""""" """"""
tick = event.data tick = event.data
strategies = self._symbol_strategy_map[tick.vt_symbol] strategies = self.symbol_strategy_map[tick.vt_symbol]
if not strategies: if not strategies:
return return
self.check_stop_order(tick) self.check_stop_order(tick)
for strategy in strategies: for strategy in strategies:
if strategy._inited: if strategy.inited:
self.call_strategy_func(strategy, strategy.on_tick, tick) self.call_strategy_func(strategy, strategy.on_tick, tick)
def process_order_event(self, event: Event): def process_order_event(self, event: Event):
"""""" """"""
order = event.data order = event.data
strategy = self._orderid_strategy_map.get(order.vt_orderid, None) strategy = self.orderid_strategy_map.get(order.vt_orderid, None)
if not strategy: if not strategy:
return return
# Remove vt_orderid if order is no longer active. # Remove vt_orderid if order is no longer active.
vt_orderids = self._active_orderids[strategy.name] vt_orderids = self.strategy_orderid_map[strategy.name]
if order.vt_orderid in vt_orderids and not order.is_active(): if order.vt_orderid in vt_orderids and not order.is_active():
vt_orderids.remove(order.vt_orderid) vt_orderids.remove(order.vt_orderid)
@ -112,20 +113,20 @@ class CtaEngine(BaseEngine):
"""""" """"""
trade = event.data trade = event.data
strategy = self._orderid_strategy_map.get(trade.vt_orderid, None) strategy = self.orderid_strategy_map.get(trade.vt_orderid, None)
if not strategy: if not strategy:
return return
if trade.direction == Direction.LONG: if trade.direction == Direction.LONG:
strategy._pos += trade.volume strategy.pos += trade.volume
else: else:
strategy._pos -= trade.volume strategy.pos -= trade.volume
self.call_strategy_func(strategy, strategy.on_trade, trade) self.call_strategy_func(strategy, strategy.on_trade, trade)
def check_stop_order(self, tick: TickData): def check_stop_order(self, tick: TickData):
"""""" """"""
for stop_order in self._stop_orders.values(): for stop_order in self.stop_orders.values():
if stop_order.vt_symbol != tick.vt_symbol: if stop_order.vt_symbol != tick.vt_symbol:
continue continue
@ -165,9 +166,9 @@ class CtaEngine(BaseEngine):
# Update stop order status if placed successfully # Update stop order status if placed successfully
if vt_orderid: if vt_orderid:
# Remove from relation map. # Remove from relation map.
self._stop_orders.pop(stop_order.stop_orderid) self.stop_orders.pop(stop_order.stop_orderid)
vt_orderids = self._active_orderids[strategy.name] vt_orderids = self.strategy_orderid_map[strategy.name]
if stop_orderid in vt_orderids: if stop_orderid in vt_orderids:
vt_orderids.remove(stop_orderid) vt_orderids.remove(stop_orderid)
@ -214,9 +215,9 @@ class CtaEngine(BaseEngine):
) )
# Save relationship between orderid and strategy. # Save relationship between orderid and strategy.
self._orderid_strategy_map[vt_orderid] = strategy self.orderid_strategy_map[vt_orderid] = strategy
vt_orderids = self._active_orderids[strategy.name] vt_orderids = self.strategy_orderid_map[strategy.name]
vt_orderids.add(vt_orderid) vt_orderids.add(vt_orderid)
return vt_orderid return vt_orderid
@ -231,9 +232,9 @@ class CtaEngine(BaseEngine):
""" """
Send a new order. Send a new order.
""" """
self._stop_order_count += 1 self.stop_order_count += 1
direction, offset = ORDER_CTA2VT[order_type] direction, offset = ORDER_CTA2VT[order_type]
stop_orderid = f"{STOPORDER_PREFIX}.{self._stop_order_count}" stop_orderid = f"{STOPORDER_PREFIX}.{self.stop_order_count}"
stop_order = StopOrder( stop_order = StopOrder(
vt_symbol=strategy.vt_symbol, vt_symbol=strategy.vt_symbol,
@ -245,9 +246,9 @@ class CtaEngine(BaseEngine):
strategy=strategy strategy=strategy
) )
self._stop_orders[stop_orderid] = stop_order self.stop_orders[stop_orderid] = stop_order
vt_orderids = self._active_orderids[strategy.name] vt_orderids = self.strategy_orderid_map[strategy.name]
vt_orderids.add(stop_orderid) vt_orderids.add(stop_orderid)
self.call_strategy_func(strategy, strategy.on_stop_order, stop_order) self.call_strategy_func(strategy, strategy.on_stop_order, stop_order)
@ -270,15 +271,15 @@ class CtaEngine(BaseEngine):
""" """
Cancel a local stop order. Cancel a local stop order.
""" """
stop_order = self._stop_orders.get(stop_orderid, None) stop_order = self.stop_orders.get(stop_orderid, None)
if not stop_order: if not stop_order:
return return
strategy = stop_order.strategy strategy = stop_order.strategy
# Remove from relation map. # Remove from relation map.
self._stop_orders.pop(stop_orderid) self.stop_orders.pop(stop_orderid)
vt_orderids = self._active_orderids[strategy.name] vt_orderids = self.strategy_orderid_map[strategy.name]
if stop_orderid in vt_orderids: if stop_orderid in vt_orderids:
vt_orderids.remove(stop_orderid) vt_orderids.remove(stop_orderid)
@ -314,7 +315,7 @@ class CtaEngine(BaseEngine):
""" """
Cancel all active orders of a strategy. Cancel all active orders of a strategy.
""" """
vt_orderids = self._active_orderids[strategy.name] vt_orderids = self.strategy_orderid_map[strategy.name]
if not vt_orderids: if not vt_orderids:
return return
@ -323,7 +324,7 @@ class CtaEngine(BaseEngine):
def get_engine_type(self): def get_engine_type(self):
"""""" """"""
return self._engine_type return self.engine_type
def call_strategy_func( def call_strategy_func(
self, self,
@ -340,116 +341,120 @@ class CtaEngine(BaseEngine):
else: else:
func() func()
except Exception: except Exception:
strategy._trading = False strategy.trading = False
strategy._inited = False strategy.inited = False
msg = f"触发异常已停止\n{traceback.format_exc()}" msg = f"触发异常已停止\n{traceback.format_exc()}"
self.write_log(msg, strategy) self.write_log(msg, strategy)
def add_strategy(self, setting): def add_strategy(
self,
class_name: str,
strategy_name: str,
vt_symbol: str,
setting: dict
):
""" """
Add a new strategy. Add a new strategy.
""" """
name = setting["name"] if strategy_name in self.strategies:
if name in self._strategies: self.write_log(f"创建策略失败,存在重名{strategy_name}")
self.write_log(f"创建策略失败,存在重名{name}")
return return
class_name = setting["class_name"] strategy_class = self.classes[class_name]
strategy_class = self._strategy_classes[class_name]
strategy = strategy_class(self, setting) strategy = strategy_class(self, strategy_name, vt_symbol, setting)
self._strategies[name] = strategy self.strategies[strategy_name] = strategy
# Add vt_symbol to strategy map. # Add vt_symbol to strategy map.
strategies = self._symbol_strategy_map[strategy.vt_symbol] strategies = self.symbol_strategy_map[vt_symbol]
strategies.append(strategy) strategies.append(strategy)
# Update to setting file. # Update to setting file.
self.update_setting(setting) self.update_strategy_setting(strategy_name, setting)
self.put_strategy_event() self.put_strategy_event(strategy)
def init_strategy(self, name): def init_strategy(self, strategy_name: str):
""" """
Init a strategy. Init a strategy.
""" """
strategy = self._strategies[name] strategy = self.strategies[strategy_name]
self.call_strategy_func(strategy, strategy.on_init) self.call_strategy_func(strategy, strategy.on_init)
strategy._inited = True strategy.inited = True
# Subscribe market data # Subscribe market data
contract = self.main_engine.get_contract(strategy.vt_symbol) contract = self.main_engine.get_contract(strategy.vt_symbol)
if not contract: if not contract:
self.write_log(f"行情订阅失败,找不到合约{strategy.vt_symbol}", strategy) self.write_log(f"行情订阅失败,找不到合约{strategy.vt_symbol}", strategy)
self.put_strategy_event() self.put_strategy_event(strategy)
def start_strategy(self, name): def start_strategy(self, strategy_name: str):
""" """
Start a strategy. Start a strategy.
""" """
strategy = self._strategies[name] strategy = self.strategies[strategy_name]
self.call_strategy_func(strategy, strategy.on_start) self.call_strategy_func(strategy, strategy.on_start)
strategy._trading = True strategy.trading = True
self.put_strategy_event() self.put_strategy_event(strategy)
def stop_strategy(self, name): def stop_strategy(self, strategy_name: str):
""" """
Stop a strategy. Stop a strategy.
""" """
strategy = self._strategies[name] strategy = self.strategies[strategy_name]
self.call_strategy_func(strategy, strategy.on_start) self.call_strategy_func(strategy, strategy.on_start)
strategy._trading = False strategy.trading = False
self.put_strategy_event() self.put_strategy_event(strategy)
def edit_strategy(self, setting): def edit_strategy(self, strategy_name: str, setting: dict):
""" """
Edit parameters of a strategy. Edit parameters of a strategy.
""" """
name = setting["name"] strategy = self.strategies[strategy_name]
strategy = self._strategies[name] strategy.update_setting(setting)
for name in strategy.parameters: self.update_strategy_setting(strategy_name, setting)
setattr(strategy, name, setting[name])
self.update_setting(setting)
self.put_strategy_event(strategy) self.put_strategy_event(strategy)
def remove_strategy(self, name): def remove_strategy(self, strategy_name: str):
""" """
Remove a strategy. Remove a strategy.
""" """
# Remove setting # Remove setting
self.remove_setting(name) self.remove_strategy_setting(strategy_name)
# Remove from symbol strategy map # Remove from symbol strategy map
strategy = self._strategies[name] strategy = self.strategies[strategy_name]
strategies = self._symbol_strategy_map[strategy.vt_symbol] strategies = self.symbol_strategy_map[strategy.vt_symbol]
strategies.remove(strategy) strategies.remove(strategy)
# Remove from active orderid map # Remove from active orderid map
if name in self._active_orderids: if strategy_name in self.strategy_orderid_map:
vt_orderids = self._active_orderids.pop(name) vt_orderids = self.strategy_orderid_map.pop(strategy_name)
# Remove vt_orderid strategy map # Remove vt_orderid strategy map
for vt_orderid in vt_orderids: for vt_orderid in vt_orderids:
self._orderid_strategy_map.pop(vt_orderid) self.orderid_strategy_map.pop(vt_orderid)
# Remove from strategies # Remove from strategies
self._strategies.pop(name) self.strategies.pop(strategy_name)
def load_strategy_class(self): def load_strategy_class(self):
""" """
Load strategy class from source code. Load strategy class from source code.
""" """
path1 = Path(__file__).parent.joinpath("strategies") path1 = Path(__file__).parent.joinpath("strategies")
self.load_strategy_class_from_folder(path1, __name__) self.load_strategy_class_from_folder(
path1,
"vnpy.app.cta_strategy.strategies"
)
path2 = Path.cwd().joinpath("strategies") path2 = Path.cwd().joinpath("strategies")
self.load_strategy_class_from_folder(path2) self.load_strategy_class_from_folder(path2, "strategies")
def load_strategy_class_from_folder( def load_strategy_class_from_folder(
self, self,
@ -460,8 +465,12 @@ class CtaEngine(BaseEngine):
Load strategy class from certain folder. Load strategy class from certain folder.
""" """
for dirpath, dirnames, filenames in os.walk(path): for dirpath, dirnames, filenames in os.walk(path):
for name in filenames: for filename in filenames:
module_name = ".".join([module_name, name.replace(".py", "")]) module_name = ".".join(
[module_name,
filename.replace(".py",
"")]
)
self.load_strategy_class_from_module(module_name) self.load_strategy_class_from_module(module_name)
def load_strategy_class_from_module(self, module_name: str): def load_strategy_class_from_module(self, module_name: str):
@ -473,8 +482,8 @@ class CtaEngine(BaseEngine):
for name in dir(module): for name in dir(module):
value = getattr(module, name) value = getattr(module, name)
if isinstance(value, CtaTemplate): if issubclass(value, CtaTemplate) and value is not CtaTemplate:
self._strategy_classes[value.__name__] = value self.classes[value.__name__] = value
except: except:
msg = f"策略文件{module_name}加载失败,触发异常:\n{traceback.format_exc()}" msg = f"策略文件{module_name}加载失败,触发异常:\n{traceback.format_exc()}"
self.write_log(msg) self.write_log(msg)
@ -483,13 +492,13 @@ class CtaEngine(BaseEngine):
""" """
Return names of strategy classes loaded. Return names of strategy classes loaded.
""" """
return list(self._strategy_classes.keys()) return list(self.classes.keys())
def get_strategy_class_parameters(self, class_name: str): def get_strategy_class_parameters(self, class_name: str):
""" """
Get default parameters of a strategy. Get default parameters of a strategy class.
""" """
strategy_class = self._strategy_classes[class_name] strategy_class = self.classes[class_name]
parameters = {} parameters = {}
for name in strategy_class.parameters: for name in strategy_class.parameters:
@ -497,51 +506,67 @@ class CtaEngine(BaseEngine):
return parameters return parameters
def get_strategy_parameters(self, strategy_name):
"""
Get parameters of a strategy.
"""
strategy = self.strategies[strategy_name]
return strategy.get_parameters()
def init_all_strategies(self): def init_all_strategies(self):
""" """
""" """
for name in self._strategies.keys(): for strategy_name in self.strategies.keys():
self.init_strategy(name) self.init_strategy(strategy_name)
def start_all_strategies(self): def start_all_strategies(self):
""" """
""" """
for name in self._strategies.keys(): for strategy_name in self.strategies.keys():
self.start_strategy(name) self.start_strategy(strategy_name)
def stop_all_strategies(self): def stop_all_strategies(self):
""" """
""" """
for name in self._strategies.keys(): for strategy_name in self.strategies.keys():
self.stop_strategy(name) self.stop_strategy(strategy_name)
def load_setting(self): def load_strategy_setting(self):
""" """
Load setting file. Load setting file.
""" """
filepath = get_temp_path(self.filename) filepath = str(get_temp_path(self.filename))
self.setting_file = shelve.open(filename) self.setting_file = shelve.open(filepath)
for setting in list(self.setting_file.values()):
self.add_strategy(setting)
def update_setting(self, setting: dict): for tp in list(self.setting_file.values()):
class_name, strategy_name, vt_symbol, setting = tp
self.add_strategy(class_name, strategy_name, vt_symbol, setting)
def update_strategy_setting(self, strategy_name: str, setting: dict):
""" """
Update setting file. Update setting file.
""" """
self.setting_file[new_setting["name"]] = new_setting strategy = self.strategies[strategy_name]
self.setting_file[strategy_name] = (
strategy.__class__.__name__,
strategy_name,
strategy.vt_symbol,
setting
)
self.setting_file.sync() self.setting_file.sync()
def remove_setting(self, name: str): def remove_strategy_setting(self, strategy_name: str):
""" """
Update setting file. Update setting file.
""" """
if name not in self.setting_file: if strategy_name not in self.setting_file:
return return
self.setting_file.pop(name) self.setting_file.pop(strategy_name)
self.setting_file.sync() self.setting_file.sync()
def save_setting(self): def save_strategy_setting(self):
""" """
Save and close setting file. Save and close setting file.
""" """
@ -558,25 +583,7 @@ class CtaEngine(BaseEngine):
""" """
Put an event to update strategy status. Put an event to update strategy status.
""" """
parameters = {} data = strategy.get_data()
for name in strategy.parameters:
parameters[name] = getattr(strategy, name)
variables = {}
for name in strategy.variables:
variables[name] = getattr(strategy, name)
data = {
"name": name,
"class_name": strategy.__class__.__name__,
"inited": strategy._inited,
"trading": strategy._trading,
"pos": strategy._pos,
"author": strategy.author,
"vt_symbol": strategy.vt_symbol,
"parameters": parameters,
"variables": variables
}
event = Event(EVENT_CTA_STRATEGY, data) event = Event(EVENT_CTA_STRATEGY, data)
self.event_engine.put(event) self.event_engine.put(event)
@ -585,7 +592,7 @@ class CtaEngine(BaseEngine):
Create cta engine log event. Create cta engine log event.
""" """
if strategy: if strategy:
msg = f"{strategy.name}: {msg}" msg = f"{strategy.strategy_name}: {msg}"
log = LogData(msg=msg, gateway_name="CtaStrategy") log = LogData(msg=msg, gateway_name="CtaStrategy")
event = Event(type=EVENT_CTA_LOG, data=log) event = Event(type=EVENT_CTA_LOG, data=log)

View File

@ -0,0 +1,23 @@
from vnpy.app.cta_strategy import CtaTemplate
class DoubleMaStrategy(CtaTemplate):
author = "用Python的交易员"
fast_window = 10
slow_window = 20.10
fast_ma = 0.0
slow_ma = 0.0
parameters = ["fast_window", "slow_window"]
variables = ["fast_ma", "slow_ma"]
def __init__(self, cta_engine, strategy_name, vt_symbol, setting):
""""""
super(DoubleMaStrategy,
self).__init__(cta_engine,
strategy_name,
vt_symbol,
setting)

View File

@ -11,26 +11,82 @@ from .base import CtaOrderType, StopOrder
class CtaTemplate(ABC): class CtaTemplate(ABC):
"""""" """"""
_inited = False
_trading = False
_pos = 0
author = "" author = ""
vt_symbol = ""
parameters = [] parameters = []
variables = [] variables = []
def __init__(self, engine: BaseEngine, setting: dict): def __init__(
self,
cta_engine: BaseEngine,
strategy_name: str,
vt_symbol: str,
setting: dict
):
"""""" """"""
self.engine = engine self.cta_engine = cta_engine
self.strategy_name = strategy_name
self.vt_symbol = vt_symbol
self.vt_symbol = setting["vt_symbol"] self.inited = False
self.trading = False
self.pos = 0
self.variables.insert(0, "inited")
self.variables.insert(1, "trading")
self.variables.insert(2, "pos")
self.update_setting(setting)
def update_setting(self, setting: dict):
"""
Update strategy parameter wtih value in setting dict.
"""
for name in self.parameters: for name in self.parameters:
if name in setting: if name in setting:
setattr(self, name, setting[name]) setattr(self, name, setting[name])
@classmethod
def get_class_parameters(cls):
"""
Get default parameters dict of strategy class.
"""
class_parameters = {}
for name in cls.parameters:
class_parameters[name] = getattr(cls, name)
return class_parameters
def get_parameters(self):
"""
Get strategy parameters dict.
"""
strategy_parameters = {}
for name in self.parameters:
strategy_parameters[name] = getattr(self, name)
return strategy_parameters
def get_variables(self):
"""
Get strategy variables dict.
"""
strategy_variables = {}
for name in self.variables:
strategy_variables[name] = getattr(self, name)
return strategy_variables
def get_data(self):
"""
Get strategy data.
"""
strategy_data = {
"strategy_name": self.strategy_name,
"vt_symbol": self.vt_symbol,
"class_name": self.__class__.__name__,
"author": self.author,
"parameters": self.get_parameters(),
"variables": self.get_variables()
}
return strategy_data
def on_init(self): def on_init(self):
""" """
Callback when strategy is inited. Callback when strategy is inited.
@ -107,34 +163,34 @@ class CtaTemplate(ABC):
""" """
Send a new order. Send a new order.
""" """
return self.engine.send_order(self, order_type, price, volume, stop) return self.cta_engine.send_order(self, order_type, price, volume, stop)
def cancel_order(self, vt_orderid): def cancel_order(self, vt_orderid):
""" """
Cancel an existing order. Cancel an existing order.
""" """
self.engine.cancel_order(vt_orderid) self.cta_engine.cancel_order(vt_orderid)
def cancel_all(self): def cancel_all(self):
""" """
Cancel all orders sent by strategy. Cancel all orders sent by strategy.
""" """
self.engine.cancel_all(self) self.cta_engine.cancel_all(self)
def write_log(self, msg): def write_log(self, msg):
""" """
Write a log message. Write a log message.
""" """
self.engine.write_log(self, msg) self.cta_engine.write_log(self, msg)
def get_engine_type(self): def get_engine_type(self):
""" """
Return whether the engine is backtesting or live trading. Return whether the cta_engine is backtesting or live trading.
""" """
return self.engine.get_engine_type() return self.cta_engine.get_engine_type()
def get_pos(self): def put_event(self):
""" """
Return current net position of the strategy. Put an strategy data event for ui update.
""" """
return self._pos self.cta_engine.put_strategy_event(self)

View File

@ -3,7 +3,7 @@ from typing import Any
from vnpy.event import EventEngine, Event from vnpy.event import EventEngine, Event
from vnpy.trader.engine import BaseEngine, MainEngine from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.ui import QtGui, QtWidgets, QtCore from vnpy.trader.ui import QtGui, QtWidgets, QtCore
from vnpy.trader.ui.widget import BaseMonitor, BaseCell, EnumCell, DirectionCell from vnpy.trader.ui.widget import BaseMonitor, BaseCell, EnumCell, TimeCell, MsgCell
from ..engine import CtaEngine from ..engine import CtaEngine
from ..base import APP_NAME, EVENT_CTA_LOG, EVENT_CTA_STOPORDER, EVENT_CTA_STRATEGY from ..base import APP_NAME, EVENT_CTA_LOG, EVENT_CTA_STOPORDER, EVENT_CTA_STRATEGY
@ -21,12 +21,12 @@ class CtaManager(QtWidgets.QWidget):
self.event_engine = event_engine self.event_engine = event_engine
self.cta_engine = main_engine.get_engine(APP_NAME) self.cta_engine = main_engine.get_engine(APP_NAME)
self.cta_engine.init_engine()
self.managers = {} self.managers = {}
self.init_ui() self.init_ui()
self.register_event() self.register_event()
self.cta_engine.init_engine()
self.update_class_combo()
def init_ui(self): def init_ui(self):
"""""" """"""
@ -34,9 +34,6 @@ class CtaManager(QtWidgets.QWidget):
# Create widgets # Create widgets
self.class_combo = QtWidgets.QComboBox() self.class_combo = QtWidgets.QComboBox()
self.class_combo.addItems(
self.cta_engine.get_all_strategy_class_names()
)
add_button = QtWidgets.QPushButton("添加策略") add_button = QtWidgets.QPushButton("添加策略")
add_button.clicked.connect(self.add_strategy) add_button.clicked.connect(self.add_strategy)
@ -51,6 +48,7 @@ class CtaManager(QtWidgets.QWidget):
stop_button.clicked.connect(self.cta_engine.stop_all_strategies) stop_button.clicked.connect(self.cta_engine.stop_all_strategies)
self.scroll_layout = QtWidgets.QVBoxLayout() self.scroll_layout = QtWidgets.QVBoxLayout()
self.scroll_layout.addStretch()
scroll_widget = QtWidgets.QWidget() scroll_widget = QtWidgets.QWidget()
scroll_widget.setLayout(self.scroll_layout) scroll_widget.setLayout(self.scroll_layout)
@ -61,15 +59,12 @@ class CtaManager(QtWidgets.QWidget):
bottom_height = 300 bottom_height = 300
self.log_monitor = QtWidgets.QTextEdit() self.log_monitor = LogMonitor(self.main_engine, self.event_engine)
self.log_monitor.setReadOnly(True)
self.log_monitor.setMaximumHeight(bottom_height)
self.stop_order_monitor = StopOrderMonitor( self.stop_order_monitor = StopOrderMonitor(
self.main_engine, self.main_engine,
self.event_engine self.event_engine
) )
self.stop_order_monitor.setMaximumHeight(bottom_height)
# Set layout # Set layout
hbox1 = QtWidgets.QHBoxLayout() hbox1 = QtWidgets.QHBoxLayout()
@ -80,54 +75,50 @@ class CtaManager(QtWidgets.QWidget):
hbox1.addWidget(start_button) hbox1.addWidget(start_button)
hbox1.addWidget(stop_button) hbox1.addWidget(stop_button)
hbox2 = QtWidgets.QHBoxLayout() grid = QtWidgets.QGridLayout()
hbox2.addWidget(self.log_monitor) grid.addWidget(scroll_area, 0, 0, 2, 1)
hbox2.addWidget(self.stop_order_monitor) grid.addWidget(self.stop_order_monitor, 0, 1)
grid.addWidget(self.log_monitor, 1, 1)
vbox = QtWidgets.QVBoxLayout() vbox = QtWidgets.QVBoxLayout()
vbox.addLayout(hbox1) vbox.addLayout(hbox1)
vbox.addWidget(scroll_area) vbox.addLayout(grid)
vbox.addLayout(hbox2)
self.setLayout(vbox) self.setLayout(vbox)
def update_class_combo(self):
""""""
self.class_combo.addItems(
self.cta_engine.get_all_strategy_class_names()
)
def register_event(self): def register_event(self):
"""""" """"""
self.signal_log.connect(self.process_log_event)
self.signal_strategy.connect(self.process_strategy_event) self.signal_strategy.connect(self.process_strategy_event)
self.event_engine.register(EVENT_CTA_LOG, self.signal_log.emit)
self.event_engine.register( self.event_engine.register(
EVENT_CTA_STRATEGY, EVENT_CTA_STRATEGY,
self.signal_strategy.emit self.signal_strategy.emit
) )
def process_log_event(self, event):
"""
Update log output.
"""
log = event.data
time = log.time.strftime("%H:%M:S")
msg = f"{time}:\t{log.msg}"
self.log_monitor.append(msg)
def process_strategy_event(self, event): def process_strategy_event(self, event):
""" """
Update strategy status onto its monitor. Update strategy status onto its monitor.
""" """
data = event.data data = event.data
name = data["name"] strategy_name = data["strategy_name"]
if name in self.managers: if strategy_name in self.managers:
manager = self.managers[name] manager = self.managers[strategy_name]
manager.update_data(data) manager.update_data(data)
else: else:
manager = StrategyManager(self, self.cta_engine, data) manager = StrategyManager(self, self.cta_engine, data)
self.scroll_layout.insertWidget(0, manager) self.scroll_layout.insertWidget(0, manager)
self.managers[strategy_name] = manager
def remove_strategy(self, name): def remove_strategy(self, strategy_name):
"""""" """"""
manager = self.managers[name] manager = self.managers[strategy_name]
manager.deleteLater() manager.deleteLater()
def add_strategy(self): def add_strategy(self):
@ -137,20 +128,27 @@ class CtaManager(QtWidgets.QWidget):
return return
parameters = self.cta_engine.get_strategy_class_parameters(class_name) parameters = self.cta_engine.get_strategy_class_parameters(class_name)
editor = SettingEditor(parameters, class_name=class_name) editor = SettingEditor(parameters, class_name=class_name)
n = editor.exec_() n = editor.exec_()
if n == editor.Accepted: if n == editor.Accepted:
setting = editor.get_setting() setting = editor.get_setting()
self.cta_engine.add_strategy(setting) vt_symbol = setting.pop("vt_symbol")
strategy_name = setting.pop("strategy_name")
self.cta_engine.add_strategy(
class_name,
strategy_name,
vt_symbol,
setting
)
def show(self): def show(self):
"""""" """"""
self.showMaximized() self.showMaximized()
class StrategyManager(QtWidgets.QWidget): class StrategyManager(QtWidgets.QFrame):
""" """
Manager for a strategy Manager for a strategy
""" """
@ -162,16 +160,22 @@ class StrategyManager(QtWidgets.QWidget):
data: dict data: dict
): ):
"""""" """"""
super(StrategyManager, self).__init__()
self.cta_manager = cta_manager self.cta_manager = cta_manager
self.cta_engine = cta_engine self.cta_engine = cta_engine
self.name = data["name"] self.strategy_name = data["strategy_name"]
self._data = data self._data = data
self.init_ui() self.init_ui()
def init_ui(self): def init_ui(self):
"""""" """"""
self.setMaximumHeight(200)
self.setFrameShape(self.Box)
self.setLineWidth(1)
init_button = QtWidgets.QPushButton("初始化") init_button = QtWidgets.QPushButton("初始化")
init_button.clicked.connect(self.init_strategy) init_button.clicked.connect(self.init_strategy)
@ -187,27 +191,19 @@ class StrategyManager(QtWidgets.QWidget):
remove_button = QtWidgets.QPushButton("移除") remove_button = QtWidgets.QPushButton("移除")
remove_button.clicked.connect(self.remove_strategy) remove_button.clicked.connect(self.remove_strategy)
self.name_label = QtWidgets.QLabel(f"策略名:{self._data['name']}") strategy_name = self._data["strategy_name"]
self.class_label = QtWidgets.QLabel(f"策略类:{self._data['class_name']}") vt_symbol = self._data["vt_symbol"]
self.symbol_label = QtWidgets.QLabel(f"代码:{self._data['vt_symbol']}") class_name = self._data["class_name"]
self.author_label = QtWidgets.QLabel(f"作者:{self._data['author']}") author = self._data["author"]
self.inited_label = QtWidgets.QLabel(f"inited:{self._data['inited']}")
self.trading_label = QtWidgets.QLabel( label_text = f"{strategy_name} - {vt_symbol} ({class_name} by {author})"
f"trading:{self._data['trading']}" label = QtWidgets.QLabel(label_text)
) label.setAlignment(QtCore.Qt.AlignCenter)
self.pos_label = QtWidgets.QLabel(f"pos:{self._data['pos']}")
self.parameters_monitor = DataMonitor(self._data["parameters"]) self.parameters_monitor = DataMonitor(self._data["parameters"])
self.variables_monitor = DataMonitor(self._data["variables"]) self.variables_monitor = DataMonitor(self._data["variables"])
hbox = QtWidgets.QHBoxLayout() hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(self.name_label)
hbox.addWidget(self.class_label)
hbox.addWidget(self.symbol_label)
hbox.addWidget(self.author_label)
hbox.addWidget(self.inited_label)
hbox.addWidget(self.trading_label)
hbox.addWidget(self.pos_label)
hbox.addWidget(init_button) hbox.addWidget(init_button)
hbox.addWidget(start_button) hbox.addWidget(start_button)
hbox.addWidget(stop_button) hbox.addWidget(stop_button)
@ -215,6 +211,7 @@ class StrategyManager(QtWidgets.QWidget):
hbox.addWidget(remove_button) hbox.addWidget(remove_button)
vbox = QtWidgets.QVBoxLayout() vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(label)
vbox.addLayout(hbox) vbox.addLayout(hbox)
vbox.addWidget(self.parameters_monitor) vbox.addWidget(self.parameters_monitor)
vbox.addWidget(self.variables_monitor) vbox.addWidget(self.variables_monitor)
@ -224,33 +221,39 @@ class StrategyManager(QtWidgets.QWidget):
"""""" """"""
self._data = data self._data = data
self.inited_label.setText(f"inited:{data['inited']}")
self.trading_label.setText(f"trading:{data['trading']}")
self.pos_label.setText(f"pos:{data['pos']}")
self.parameters_monitor.update_data(data["parameters"]) self.parameters_monitor.update_data(data["parameters"])
self.variables_monitor.update_data(data["variables"]) self.variables_monitor.update_data(data["variables"])
def init_strategy(self): def init_strategy(self):
"""""" """"""
self.cta_engine.init_strategy(self.name) self.cta_engine.init_strategy(self.strategy_name)
def start_strategy(self): def start_strategy(self):
"""""" """"""
self.cta_engine.start_strategy(self.name) self.cta_engine.start_strategy(self.strategy_name)
def stop_strategy(self): def stop_strategy(self):
"""""" """"""
self.cta_engine.stop_strategy(self.name) self.cta_engine.stop_strategy(self.strategy_name)
def edit_strategy(self): def edit_strategy(self):
"""""" """"""
pass vt_symbol = self._data["vt_symbol"]
class_name = self._data["class_name"]
strategy_name = self._data["strategy_name"]
parameters = self.cta_engine.get_strategy_parameters(strategy_name)
editor = SettingEditor(parameters, strategy_name=strategy_name)
n = editor.exec_()
if n == editor.Accepted:
setting = editor.get_setting()
self.cta_engine.edit_strategy(strategy_name, setting)
def remove_strategy(self): def remove_strategy(self):
"""""" """"""
self.cta_engine.remove_strategy(self.name) self.cta_engine.remove_strategy(self.strategy_name)
self.cta_manager.remove_strategy(self.name) self.cta_manager.remove_strategy(self.strategy_name)
class DataMonitor(QtWidgets.QTableWidget): class DataMonitor(QtWidgets.QTableWidget):
@ -270,16 +273,22 @@ class DataMonitor(QtWidgets.QTableWidget):
def init_ui(self): def init_ui(self):
"""""" """"""
labels = list(self._data.keys()) labels = list(self._data.keys())
self.setColumnCount(labels) self.setColumnCount(len(labels))
self.setHorizontalHeaderLabels(labels) self.setHorizontalHeaderLabels(labels)
self.setRowCount(1) self.setRowCount(1)
self.verticalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
self.verticalHeader().setVisible(False) self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers) self.setEditTriggers(self.NoEditTriggers)
for column, name in enumerate(self._data.items()): for column, name in enumerate(self._data.keys()):
value = self._data[name] value = self._data[name]
cell = QtWidgets.QTableWidgetItem(str(value)) cell = QtWidgets.QTableWidgetItem(str(value))
cell.setTextAlignment(QtCore.Qt.AlignCenter)
self.setItem(0, column, cell) self.setItem(0, column, cell)
self.cells[name] = cell self.cells[name] = cell
@ -304,7 +313,7 @@ class StrategyCell(BaseCell):
Set text using enum.constant.value. Set text using enum.constant.value.
""" """
if content: if content:
super(StrategyCell, self).set_content(content.name, data) super(StrategyCell, self).set_content(content.strategy_name, data)
class StopOrderMonitor(BaseMonitor): class StopOrderMonitor(BaseMonitor):
@ -358,6 +367,56 @@ class StopOrderMonitor(BaseMonitor):
} }
} }
def init_ui(self):
"""
Stretch columns.
"""
super(StopOrderMonitor, self).init_ui()
self.horizontalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
class LogMonitor(BaseMonitor):
"""
Monitor for log data.
"""
event_type = EVENT_CTA_LOG
data_key = ""
sorting = False
headers = {
"time": {
"display": "时间",
"cell": TimeCell,
"update": False
},
"msg": {
"display": "信息",
"cell": MsgCell,
"update": False
}
}
def init_ui(self):
"""
Stretch last column.
"""
super(LogMonitor, self).init_ui()
self.horizontalHeader().setSectionResizeMode(
1,
QtWidgets.QHeaderView.Stretch
)
def insert_new_row(self, data):
"""
Insert a new row at the top of table.
"""
super(LogMonitor, self).insert_new_row(data)
self.resizeRowToContents(0)
class SettingEditor(QtWidgets.QDialog): class SettingEditor(QtWidgets.QDialog):
""" """
@ -400,12 +459,19 @@ class SettingEditor(QtWidgets.QDialog):
type_ = type(value) type_ = type(value)
edit = QtWidgets.QLineEdit(str(value)) edit = QtWidgets.QLineEdit(str(value))
if type_ is int:
validator = QtGui.QIntValidator()
edit.setValidator(validator)
elif type_ is float:
validator = QtGui.QDoubleValidator()
edit.setValidator(validator)
form.addRow(f"{name} {type_}", edit) form.addRow(f"{name} {type_}", edit)
self.edits[name] = (edit, type_) self.edits[name] = (edit, type_)
button = QtWidgets.QPushButton(button_text) button = QtWidgets.QPushButton(button_text)
button.clicked.connect(self.accpet) button.clicked.connect(self.accept)
form.addRow(button) form.addRow(button)
self.setLayout(form) self.setLayout(form)

View File

@ -1,5 +1,7 @@
import platform import platform
import ctypes import ctypes
import traceback
import sys
from pathlib import Path from pathlib import Path
import qdarkstyle import qdarkstyle
@ -10,10 +12,23 @@ from ..setting import SETTINGS
from ..utility import get_icon_path from ..utility import get_icon_path
def excepthook(exctype, value, tb):
"""异常捕捉钩子"""
msg = ''.join(traceback.format_exception(exctype, value, tb))
QtWidgets.QMessageBox.critical(
None,
u'Exception',
msg,
QtWidgets.QMessageBox.Ok
)
def create_qapp(): def create_qapp():
""" """
Create Qt Application. Create Qt Application.
""" """
sys.excepthook = excepthook
qapp = QtWidgets.QApplication([]) qapp = QtWidgets.QApplication([])
qapp.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) qapp.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

View File

@ -164,6 +164,17 @@ class TimeCell(BaseCell):
self._data = data self._data = data
class MsgCell(BaseCell):
"""
Cell used for showing msg data.
"""
def __init__(self, content: str, data: Any):
""""""
super(MsgCell, self).__init__(content, data)
self.setTextAlignment(QtCore.Qt.AlignLeft|QtCore.Qt.AlignVCenter)
class BaseMonitor(QtWidgets.QTableWidget): class BaseMonitor(QtWidgets.QTableWidget):
""" """
Monitor data update in VN Trader. Monitor data update in VN Trader.
@ -422,7 +433,7 @@ class LogMonitor(BaseMonitor):
}, },
"msg": { "msg": {
"display": "信息", "display": "信息",
"cell": BaseCell, "cell": MsgCell,
"update": False "update": False
}, },
"gateway_name": { "gateway_name": {