[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 .engine import CtaEngine
from .template import CtaTemplate
from .base import APP_NAME

View File

@ -47,31 +47,32 @@ class CtaEngine(BaseEngine):
event_engine,
"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._strategy_classes = {} # class_name: stategy_class
self._strategies = {} # name: strategy
self.classes = {} # class_name: stategy_class
self.strategies = {} # strategy_name: strategy
self._symbol_strategy_map = defaultdict(list) # vt_symbol: strategy list
self._orderid_strategy_map = {} # vt_orderid: strategy
self.symbol_strategy_map = defaultdict(list) # vt_symbol: strategy list
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):
"""
"""
self.load_strategy_class()
self.load_setting()
self.load_strategy_setting()
self.register_event()
self.write_log("CTA策略引擎初始化成功")
def close(self):
""""""
self.save_setting()
self.save_strategy_setting()
def register_event(self):
""""""
@ -83,26 +84,26 @@ class CtaEngine(BaseEngine):
""""""
tick = event.data
strategies = self._symbol_strategy_map[tick.vt_symbol]
strategies = self.symbol_strategy_map[tick.vt_symbol]
if not strategies:
return
self.check_stop_order(tick)
for strategy in strategies:
if strategy._inited:
if strategy.inited:
self.call_strategy_func(strategy, strategy.on_tick, tick)
def process_order_event(self, event: Event):
""""""
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:
return
# 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():
vt_orderids.remove(order.vt_orderid)
@ -112,20 +113,20 @@ class CtaEngine(BaseEngine):
""""""
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:
return
if trade.direction == Direction.LONG:
strategy._pos += trade.volume
strategy.pos += trade.volume
else:
strategy._pos -= trade.volume
strategy.pos -= trade.volume
self.call_strategy_func(strategy, strategy.on_trade, trade)
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:
continue
@ -165,9 +166,9 @@ class CtaEngine(BaseEngine):
# Update stop order status if placed successfully
if vt_orderid:
# 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:
vt_orderids.remove(stop_orderid)
@ -214,9 +215,9 @@ class CtaEngine(BaseEngine):
)
# 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)
return vt_orderid
@ -231,9 +232,9 @@ class CtaEngine(BaseEngine):
"""
Send a new order.
"""
self._stop_order_count += 1
self.stop_order_count += 1
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(
vt_symbol=strategy.vt_symbol,
@ -245,9 +246,9 @@ class CtaEngine(BaseEngine):
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)
self.call_strategy_func(strategy, strategy.on_stop_order, stop_order)
@ -270,15 +271,15 @@ class CtaEngine(BaseEngine):
"""
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:
return
strategy = stop_order.strategy
# 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:
vt_orderids.remove(stop_orderid)
@ -314,7 +315,7 @@ class CtaEngine(BaseEngine):
"""
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:
return
@ -323,7 +324,7 @@ class CtaEngine(BaseEngine):
def get_engine_type(self):
""""""
return self._engine_type
return self.engine_type
def call_strategy_func(
self,
@ -340,116 +341,120 @@ class CtaEngine(BaseEngine):
else:
func()
except Exception:
strategy._trading = False
strategy._inited = False
strategy.trading = False
strategy.inited = False
msg = f"触发异常已停止\n{traceback.format_exc()}"
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.
"""
name = setting["name"]
if name in self._strategies:
self.write_log(f"创建策略失败,存在重名{name}")
if strategy_name in self.strategies:
self.write_log(f"创建策略失败,存在重名{strategy_name}")
return
class_name = setting["class_name"]
strategy_class = self._strategy_classes[class_name]
strategy_class = self.classes[class_name]
strategy = strategy_class(self, setting)
self._strategies[name] = strategy
strategy = strategy_class(self, strategy_name, vt_symbol, setting)
self.strategies[strategy_name] = strategy
# Add vt_symbol to strategy map.
strategies = self._symbol_strategy_map[strategy.vt_symbol]
strategies = self.symbol_strategy_map[vt_symbol]
strategies.append(strategy)
# 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.
"""
strategy = self._strategies[name]
strategy = self.strategies[strategy_name]
self.call_strategy_func(strategy, strategy.on_init)
strategy._inited = True
strategy.inited = True
# Subscribe market data
contract = self.main_engine.get_contract(strategy.vt_symbol)
if not contract:
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.
"""
strategy = self._strategies[name]
strategy = self.strategies[strategy_name]
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.
"""
strategy = self._strategies[name]
strategy = self.strategies[strategy_name]
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.
"""
name = setting["name"]
strategy = self._strategies[name]
strategy = self.strategies[strategy_name]
strategy.update_setting(setting)
for name in strategy.parameters:
setattr(strategy, name, setting[name])
self.update_setting(setting)
self.update_strategy_setting(strategy_name, setting)
self.put_strategy_event(strategy)
def remove_strategy(self, name):
def remove_strategy(self, strategy_name: str):
"""
Remove a strategy.
"""
# Remove setting
self.remove_setting(name)
self.remove_strategy_setting(strategy_name)
# Remove from symbol strategy map
strategy = self._strategies[name]
strategies = self._symbol_strategy_map[strategy.vt_symbol]
strategy = self.strategies[strategy_name]
strategies = self.symbol_strategy_map[strategy.vt_symbol]
strategies.remove(strategy)
# Remove from active orderid map
if name in self._active_orderids:
vt_orderids = self._active_orderids.pop(name)
if strategy_name in self.strategy_orderid_map:
vt_orderids = self.strategy_orderid_map.pop(strategy_name)
# Remove vt_orderid strategy map
for vt_orderid in vt_orderids:
self._orderid_strategy_map.pop(vt_orderid)
self.orderid_strategy_map.pop(vt_orderid)
# Remove from strategies
self._strategies.pop(name)
self.strategies.pop(strategy_name)
def load_strategy_class(self):
"""
Load strategy class from source code.
"""
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")
self.load_strategy_class_from_folder(path2)
self.load_strategy_class_from_folder(path2, "strategies")
def load_strategy_class_from_folder(
self,
@ -460,8 +465,12 @@ class CtaEngine(BaseEngine):
Load strategy class from certain folder.
"""
for dirpath, dirnames, filenames in os.walk(path):
for name in filenames:
module_name = ".".join([module_name, name.replace(".py", "")])
for filename in filenames:
module_name = ".".join(
[module_name,
filename.replace(".py",
"")]
)
self.load_strategy_class_from_module(module_name)
def load_strategy_class_from_module(self, module_name: str):
@ -473,8 +482,8 @@ class CtaEngine(BaseEngine):
for name in dir(module):
value = getattr(module, name)
if isinstance(value, CtaTemplate):
self._strategy_classes[value.__name__] = value
if issubclass(value, CtaTemplate) and value is not CtaTemplate:
self.classes[value.__name__] = value
except:
msg = f"策略文件{module_name}加载失败,触发异常:\n{traceback.format_exc()}"
self.write_log(msg)
@ -483,13 +492,13 @@ class CtaEngine(BaseEngine):
"""
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):
"""
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 = {}
for name in strategy_class.parameters:
@ -497,51 +506,67 @@ class CtaEngine(BaseEngine):
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):
"""
"""
for name in self._strategies.keys():
self.init_strategy(name)
for strategy_name in self.strategies.keys():
self.init_strategy(strategy_name)
def start_all_strategies(self):
"""
"""
for name in self._strategies.keys():
self.start_strategy(name)
for strategy_name in self.strategies.keys():
self.start_strategy(strategy_name)
def stop_all_strategies(self):
"""
"""
for name in self._strategies.keys():
self.stop_strategy(name)
for strategy_name in self.strategies.keys():
self.stop_strategy(strategy_name)
def load_setting(self):
def load_strategy_setting(self):
"""
Load setting file.
"""
filepath = get_temp_path(self.filename)
self.setting_file = shelve.open(filename)
for setting in list(self.setting_file.values()):
self.add_strategy(setting)
filepath = str(get_temp_path(self.filename))
self.setting_file = shelve.open(filepath)
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.
"""
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()
def remove_setting(self, name: str):
def remove_strategy_setting(self, strategy_name: str):
"""
Update setting file.
"""
if name not in self.setting_file:
if strategy_name not in self.setting_file:
return
self.setting_file.pop(name)
self.setting_file.pop(strategy_name)
self.setting_file.sync()
def save_setting(self):
def save_strategy_setting(self):
"""
Save and close setting file.
"""
@ -558,25 +583,7 @@ class CtaEngine(BaseEngine):
"""
Put an event to update strategy status.
"""
parameters = {}
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
}
data = strategy.get_data()
event = Event(EVENT_CTA_STRATEGY, data)
self.event_engine.put(event)
@ -585,7 +592,7 @@ class CtaEngine(BaseEngine):
Create cta engine log event.
"""
if strategy:
msg = f"{strategy.name}: {msg}"
msg = f"{strategy.strategy_name}: {msg}"
log = LogData(msg=msg, gateway_name="CtaStrategy")
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):
""""""
_inited = False
_trading = False
_pos = 0
author = ""
vt_symbol = ""
parameters = []
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:
if name in setting:
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):
"""
Callback when strategy is inited.
@ -107,34 +163,34 @@ class CtaTemplate(ABC):
"""
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):
"""
Cancel an existing order.
"""
self.engine.cancel_order(vt_orderid)
self.cta_engine.cancel_order(vt_orderid)
def cancel_all(self):
"""
Cancel all orders sent by strategy.
"""
self.engine.cancel_all(self)
self.cta_engine.cancel_all(self)
def write_log(self, msg):
"""
Write a log message.
"""
self.engine.write_log(self, msg)
self.cta_engine.write_log(self, msg)
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.trader.engine import BaseEngine, MainEngine
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 ..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.cta_engine = main_engine.get_engine(APP_NAME)
self.cta_engine.init_engine()
self.managers = {}
self.init_ui()
self.register_event()
self.cta_engine.init_engine()
self.update_class_combo()
def init_ui(self):
""""""
@ -34,9 +34,6 @@ class CtaManager(QtWidgets.QWidget):
# Create widgets
self.class_combo = QtWidgets.QComboBox()
self.class_combo.addItems(
self.cta_engine.get_all_strategy_class_names()
)
add_button = QtWidgets.QPushButton("添加策略")
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)
self.scroll_layout = QtWidgets.QVBoxLayout()
self.scroll_layout.addStretch()
scroll_widget = QtWidgets.QWidget()
scroll_widget.setLayout(self.scroll_layout)
@ -61,15 +59,12 @@ class CtaManager(QtWidgets.QWidget):
bottom_height = 300
self.log_monitor = QtWidgets.QTextEdit()
self.log_monitor.setReadOnly(True)
self.log_monitor.setMaximumHeight(bottom_height)
self.log_monitor = LogMonitor(self.main_engine, self.event_engine)
self.stop_order_monitor = StopOrderMonitor(
self.main_engine,
self.event_engine
)
self.stop_order_monitor.setMaximumHeight(bottom_height)
# Set layout
hbox1 = QtWidgets.QHBoxLayout()
@ -80,54 +75,50 @@ class CtaManager(QtWidgets.QWidget):
hbox1.addWidget(start_button)
hbox1.addWidget(stop_button)
hbox2 = QtWidgets.QHBoxLayout()
hbox2.addWidget(self.log_monitor)
hbox2.addWidget(self.stop_order_monitor)
grid = QtWidgets.QGridLayout()
grid.addWidget(scroll_area, 0, 0, 2, 1)
grid.addWidget(self.stop_order_monitor, 0, 1)
grid.addWidget(self.log_monitor, 1, 1)
vbox = QtWidgets.QVBoxLayout()
vbox.addLayout(hbox1)
vbox.addWidget(scroll_area)
vbox.addLayout(hbox2)
vbox.addLayout(grid)
self.setLayout(vbox)
def update_class_combo(self):
""""""
self.class_combo.addItems(
self.cta_engine.get_all_strategy_class_names()
)
def register_event(self):
""""""
self.signal_log.connect(self.process_log_event)
self.signal_strategy.connect(self.process_strategy_event)
self.event_engine.register(EVENT_CTA_LOG, self.signal_log.emit)
self.event_engine.register(
EVENT_CTA_STRATEGY,
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):
"""
Update strategy status onto its monitor.
"""
data = event.data
name = data["name"]
strategy_name = data["strategy_name"]
if name in self.managers:
manager = self.managers[name]
if strategy_name in self.managers:
manager = self.managers[strategy_name]
manager.update_data(data)
else:
manager = StrategyManager(self, self.cta_engine, data)
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()
def add_strategy(self):
@ -137,20 +128,27 @@ class CtaManager(QtWidgets.QWidget):
return
parameters = self.cta_engine.get_strategy_class_parameters(class_name)
editor = SettingEditor(parameters, class_name=class_name)
n = editor.exec_()
if n == editor.Accepted:
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):
""""""
self.showMaximized()
class StrategyManager(QtWidgets.QWidget):
class StrategyManager(QtWidgets.QFrame):
"""
Manager for a strategy
"""
@ -162,16 +160,22 @@ class StrategyManager(QtWidgets.QWidget):
data: dict
):
""""""
super(StrategyManager, self).__init__()
self.cta_manager = cta_manager
self.cta_engine = cta_engine
self.name = data["name"]
self.strategy_name = data["strategy_name"]
self._data = data
self.init_ui()
def init_ui(self):
""""""
self.setMaximumHeight(200)
self.setFrameShape(self.Box)
self.setLineWidth(1)
init_button = QtWidgets.QPushButton("初始化")
init_button.clicked.connect(self.init_strategy)
@ -187,27 +191,19 @@ class StrategyManager(QtWidgets.QWidget):
remove_button = QtWidgets.QPushButton("移除")
remove_button.clicked.connect(self.remove_strategy)
self.name_label = QtWidgets.QLabel(f"策略名:{self._data['name']}")
self.class_label = QtWidgets.QLabel(f"策略类:{self._data['class_name']}")
self.symbol_label = QtWidgets.QLabel(f"代码:{self._data['vt_symbol']}")
self.author_label = QtWidgets.QLabel(f"作者:{self._data['author']}")
self.inited_label = QtWidgets.QLabel(f"inited:{self._data['inited']}")
self.trading_label = QtWidgets.QLabel(
f"trading:{self._data['trading']}"
)
self.pos_label = QtWidgets.QLabel(f"pos:{self._data['pos']}")
strategy_name = self._data["strategy_name"]
vt_symbol = self._data["vt_symbol"]
class_name = self._data["class_name"]
author = self._data["author"]
label_text = f"{strategy_name} - {vt_symbol} ({class_name} by {author})"
label = QtWidgets.QLabel(label_text)
label.setAlignment(QtCore.Qt.AlignCenter)
self.parameters_monitor = DataMonitor(self._data["parameters"])
self.variables_monitor = DataMonitor(self._data["variables"])
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(start_button)
hbox.addWidget(stop_button)
@ -215,6 +211,7 @@ class StrategyManager(QtWidgets.QWidget):
hbox.addWidget(remove_button)
vbox = QtWidgets.QVBoxLayout()
vbox.addWidget(label)
vbox.addLayout(hbox)
vbox.addWidget(self.parameters_monitor)
vbox.addWidget(self.variables_monitor)
@ -224,33 +221,39 @@ class StrategyManager(QtWidgets.QWidget):
""""""
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.variables_monitor.update_data(data["variables"])
def init_strategy(self):
""""""
self.cta_engine.init_strategy(self.name)
self.cta_engine.init_strategy(self.strategy_name)
def start_strategy(self):
""""""
self.cta_engine.start_strategy(self.name)
self.cta_engine.start_strategy(self.strategy_name)
def stop_strategy(self):
""""""
self.cta_engine.stop_strategy(self.name)
self.cta_engine.stop_strategy(self.strategy_name)
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):
""""""
self.cta_engine.remove_strategy(self.name)
self.cta_manager.remove_strategy(self.name)
self.cta_engine.remove_strategy(self.strategy_name)
self.cta_manager.remove_strategy(self.strategy_name)
class DataMonitor(QtWidgets.QTableWidget):
@ -270,16 +273,22 @@ class DataMonitor(QtWidgets.QTableWidget):
def init_ui(self):
""""""
labels = list(self._data.keys())
self.setColumnCount(labels)
self.setColumnCount(len(labels))
self.setHorizontalHeaderLabels(labels)
self.setRowCount(1)
self.verticalHeader().setSectionResizeMode(
QtWidgets.QHeaderView.Stretch
)
self.verticalHeader().setVisible(False)
self.setEditTriggers(self.NoEditTriggers)
for column, name in enumerate(self._data.items()):
for column, name in enumerate(self._data.keys()):
value = self._data[name]
cell = QtWidgets.QTableWidgetItem(str(value))
cell.setTextAlignment(QtCore.Qt.AlignCenter)
self.setItem(0, column, cell)
self.cells[name] = cell
@ -304,7 +313,7 @@ class StrategyCell(BaseCell):
Set text using enum.constant.value.
"""
if content:
super(StrategyCell, self).set_content(content.name, data)
super(StrategyCell, self).set_content(content.strategy_name, data)
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):
"""
@ -400,12 +459,19 @@ class SettingEditor(QtWidgets.QDialog):
type_ = type(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)
self.edits[name] = (edit, type_)
button = QtWidgets.QPushButton(button_text)
button.clicked.connect(self.accpet)
button.clicked.connect(self.accept)
form.addRow(button)
self.setLayout(form)

View File

@ -1,5 +1,7 @@
import platform
import ctypes
import traceback
import sys
from pathlib import Path
import qdarkstyle
@ -10,10 +12,23 @@ from ..setting import SETTINGS
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():
"""
Create Qt Application.
"""
sys.excepthook = excepthook
qapp = QtWidgets.QApplication([])
qapp.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5())

View File

@ -164,6 +164,17 @@ class TimeCell(BaseCell):
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):
"""
Monitor data update in VN Trader.
@ -422,7 +433,7 @@ class LogMonitor(BaseMonitor):
},
"msg": {
"display": "信息",
"cell": BaseCell,
"cell": MsgCell,
"update": False
},
"gateway_name": {