[Add] LogMonitor for cta strategy and global excepthook
This commit is contained in:
parent
4596f3a515
commit
a257b18f84
@ -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
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
23
vnpy/app/cta_strategy/strategies/double_ma_strategy.py
Normal file
23
vnpy/app/cta_strategy/strategies/double_ma_strategy.py
Normal 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)
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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())
|
||||
|
||||
|
@ -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": {
|
||||
|
Loading…
Reference in New Issue
Block a user