1. 不要迷恋停止单,它缺点多的很
当你想以比市场价更高的价格买,或者以比市场价更低的价格卖时,使用send_order()是会立即执行的,但是用停止单却可以做到这一点,这是停止单的优点。
但是实际使用中停止单也是有缺点的:
- 当以比市场价更低的价格买,它会立即被执行
- 当以比市场价更高的价格卖,它会立即被执行
- 实际运行中有多个停止单通知满足条件,接口在极短时间内接受多个停止单发出的委托,发生委托覆盖。表现为明明策略发出过多个停止单,但是只有最后一个停止单有结果,其他的委托莫名其妙的人间蒸发了,不见了,接口不回应、不通知,策略也不知道,用户无法查。
- 停止单一经发出,触发价也是执行价,无法根据当时的市场价格做出价格调整
- 只有CTA策略才可以使用停止单,其他类型的策略无法使用,因为它的执行逻辑和具体合约耦合度太高
2. 什么是条件单?
这是本人给它取的名字,它其实是本人以前提到的交易线(TradeLine)的改进和增强。
它主要就是为解决停止单上述缺点而设计的,当然应该具备上述优点。
- 可以设定触发价格和触发条件
- 触发条件包括四种:>=、<=、>、<,与执行的委托方向无关
- 当市场价格满足触发条件时,条件单立即执行,执行收最小流控限制
- 执行价格可以是触发价、市场价或极限价(买时为涨停价,卖时为跌停价)
- 条件单管理器还可以提供手工取消条件单的功能,双击就可以取消
先看一眼条件单的效果图
2.1 它数据结构包含如下:
在vnpy_ctastrategy\base.py中增加如下代码:
class Condition(Enum): # hxxjava add
""" 条件单的条件 """
BT = ">"
LT = "<"
BE = ">="
LE = "<="
class ExecutePrice(Enum): # hxxjava add
""" 执行价格 """
SETPRICE = "设定价"
MARKET = "市场价"
EXTREME = "极限价"
class CondOrderStatus(Enum): # hxxjava add
""" 条件单状态 """
WAITING = "等待中"
CANCELLED = "已撤销"
TRIGGERED = "已触发"
@dataclass
class ConditionOrder: # hxxjava add
""" 条件单 """
strategy_name: str
vt_symbol: str
direction: Direction
offset: Offset
price: float
volume: float
condition:Condition
execute_price:ExecutePrice = ExecutePrice.SETPRICE
create_time: datetime = datetime.now()
trigger_time: datetime = None
cond_orderid: str = "" # 条件单编号
status: CondOrderStatus = CondOrderStatus.WAITING
def __post_init__(self):
""" """
if not self.cond_orderid:
self.cond_orderid = datetime.now().strftime("%m%d%H%M%S%f")[:13]
EVENT_CONDITION_ORDER = "eConditionOrder" # hxxjava add
2.2 条件单管理器
2.2.1 修改CTA策略管理器
修改vnpy_ctastrategy\ui\widget.py中的class CtaManager,代码如下:
class CtaManager(QtWidgets.QWidget):
""""""
signal_log: QtCore.Signal = QtCore.Signal(Event)
signal_strategy: QtCore.Signal = QtCore.Signal(Event)
def __init__(self, main_engine: MainEngine, event_engine: EventEngine) -> None:
""""""
super().__init__()
self.main_engine: MainEngine = main_engine
self.event_engine: EventEngine = event_engine
self.cta_engine: CtaEngine = main_engine.get_engine(APP_NAME)
self.managers: Dict[str, StrategyManager] = {}
self.init_ui()
self.register_event()
self.cta_engine.init_engine()
self.update_class_combo()
def init_ui(self) -> None:
""""""
self.setWindowTitle("CTA策略")
# Create widgets
self.class_combo: QtWidgets.QComboBox = QtWidgets.QComboBox()
add_button: QtWidgets.QPushButton = QtWidgets.QPushButton("添加策略")
add_button.clicked.connect(self.add_strategy)
init_button: QtWidgets.QPushButton = QtWidgets.QPushButton("全部初始化")
init_button.clicked.connect(self.cta_engine.init_all_strategies)
start_button: QtWidgets.QPushButton = QtWidgets.QPushButton("全部启动")
start_button.clicked.connect(self.cta_engine.start_all_strategies)
stop_button: QtWidgets.QPushButton = QtWidgets.QPushButton("全部停止")
stop_button.clicked.connect(self.cta_engine.stop_all_strategies)
clear_button: QtWidgets.QPushButton = QtWidgets.QPushButton("清空日志")
clear_button.clicked.connect(self.clear_log)
roll_button: QtWidgets.QPushButton = QtWidgets.QPushButton("移仓助手")
roll_button.clicked.connect(self.roll)
self.scroll_layout: QtWidgets.QVBoxLayout = QtWidgets.QVBoxLayout()
self.scroll_layout.addStretch()
scroll_widget: QtWidgets.QWidget = QtWidgets.QWidget()
scroll_widget.setLayout(self.scroll_layout)
self.scroll_area: QtWidgets.QScrollArea = QtWidgets.QScrollArea()
self.scroll_area.setWidgetResizable(True)
self.scroll_area.setWidget(scroll_widget)
self.log_monitor: LogMonitor = LogMonitor(self.main_engine, self.event_engine)
self.stop_order_monitor: StopOrderMonitor = StopOrderMonitor(
self.main_engine, self.event_engine
)
self.strategy_combo = QtWidgets.QComboBox()
self.strategy_combo.setMinimumWidth(200)
find_button = QtWidgets.QPushButton("查找")
find_button.clicked.connect(self.find_strategy)
# hxxjava add
self.condition_order_monitor = ConditionOrderMonitor(self.cta_engine)
# Set layout
hbox1: QtWidgets.QHBoxLayout = QtWidgets.QHBoxLayout()
hbox1.addWidget(self.class_combo)
hbox1.addWidget(add_button)
hbox1.addStretch()
hbox1.addWidget(self.strategy_combo)
hbox1.addWidget(find_button)
hbox1.addStretch()
hbox1.addWidget(init_button)
hbox1.addWidget(start_button)
hbox1.addWidget(stop_button)
hbox1.addWidget(clear_button)
hbox1.addWidget(roll_button)
grid = QtWidgets.QGridLayout()
# grid.addWidget(self.scroll_area, 0, 0, 2, 1)
grid.addWidget(self.scroll_area, 0, 0, 3, 1) # hxxjava change 3 rows , 1 column
grid.addWidget(self.stop_order_monitor, 0, 1)
grid.addWidget(self.condition_order_monitor, 1, 1) # hxxjava add
# grid.addWidget(self.log_monitor, 1, 1)
grid.addWidget(self.log_monitor, 2, 1) # hxxjava change
vbox: QtWidgets.QVBoxLayout = QtWidgets.QVBoxLayout()
vbox.addLayout(hbox1)
vbox.addLayout(grid)
self.setLayout(vbox)
def update_class_combo(self) -> None:
""""""
names = self.cta_engine.get_all_strategy_class_names()
names.sort()
self.class_combo.addItems(names)
def update_strategy_combo(self) -> None:
""""""
names = list(self.managers.keys())
names.sort()
self.strategy_combo.clear()
self.strategy_combo.addItems(names)
def register_event(self) -> None:
""""""
self.signal_strategy.connect(self.process_strategy_event)
self.event_engine.register(
EVENT_CTA_STRATEGY, self.signal_strategy.emit
)
def process_strategy_event(self, event) -> None:
"""
Update strategy status onto its monitor.
"""
data = event.data
strategy_name: str = data["strategy_name"]
if strategy_name in self.managers:
manager: StrategyManager = self.managers[strategy_name]
manager.update_data(data)
else:
manager: StrategyManager = StrategyManager(self, self.cta_engine, data)
self.scroll_layout.insertWidget(0, manager)
self.managers[strategy_name] = manager
self.update_strategy_combo()
def remove_strategy(self, strategy_name) -> None:
""""""
manager: StrategyManager = self.managers.pop(strategy_name)
manager.deleteLater()
self.update_strategy_combo()
def add_strategy(self) -> None:
""""""
class_name: str = str(self.class_combo.currentText())
if not class_name:
return
parameters: dict = self.cta_engine.get_strategy_class_parameters(class_name)
editor: SettingEditor = SettingEditor(parameters, class_name=class_name)
n: int = editor.exec_()
if n == editor.Accepted:
setting: dict = editor.get_setting()
vt_symbol: str = setting.pop("vt_symbol")
strategy_name: str = setting.pop("strategy_name")
self.cta_engine.add_strategy(
class_name, strategy_name, vt_symbol, setting
)
def find_strategy(self) -> None:
""""""
strategy_name = self.strategy_combo.currentText()
manager = self.managers[strategy_name]
self.scroll_area.ensureWidgetVisible(manager)
def clear_log(self) -> None:
""""""
self.log_monitor.setRowCount(0)
def show(self) -> None:
""""""
self.showMaximized()
def roll(self) -> None:
""""""
dialog: RolloverTool = RolloverTool(self)
dialog.exec_()
2.2.2 条件单管理器代码
在vnpy_ctastrategy\ui\widget.py中增加如下代码:
class ConditionOrderMonitor(BaseMonitor): # hxxjava add
"""
Monitor for condition order.
"""
event_type = EVENT_CONDITION_ORDER
data_key = "cond_orderid"
sorting = True
headers = {
"cond_orderid": {
"display": "条件单号",
"cell": BaseCell,
"update": False,
},
"vt_symbol": {"display": "本地代码", "cell": BaseCell, "update": False},
"direction": {"display": "方向", "cell": EnumCell, "update": False},
"offset": {"display": "开平", "cell": EnumCell, "update": False},
"price": {"display": "触发价", "cell": BaseCell, "update": False},
"volume": {"display": "数量", "cell": BaseCell, "update": False},
"condition": {"display": "触发条件", "cell": EnumCell, "update": False},
"execute_price": {"display": "执行价", "cell": EnumCell, "update": False},
"create_time": {"display": "生成时间", "cell": TimeCell, "update": False},
"trigger_time": {"display": "触发时间", "cell": TimeCell, "update": False},
"status": {"display": "状态", "cell": EnumCell, "update": True},
"strategy_name": {"display": "策略名称", "cell": BaseCell, "update": False},
}
def __init__(self,cta_engine : MyCtaEngine):
""""""
super().__init__(cta_engine.main_engine, cta_engine.event_engine)
self.cta_engine = cta_engine
def init_ui(self):
"""
Connect signal.
"""
super().init_ui()
self.setToolTip("双击单元格可停止条件单")
self.itemDoubleClicked.connect(self.stop_condition_order)
def stop_condition_order(self, cell):
"""
Stop algo if cell double clicked.
"""
order = cell.get_data()
if order:
self.cta_engine.cancel_condition_order(order.cond_orderid)
2.2.3 加载条件单管理器
修改策略管理器StrategyManager的代码如下:
class StrategyManager(QtWidgets.QFrame):
"""
Manager for a strategy
"""
def __init__(
self, cta_manager: CtaManager, cta_engine: CtaEngine, data: dict
):
""""""
super(StrategyManager, self).__init__()
self.cta_manager = cta_manager
self.cta_engine = cta_engine
self.strategy_name = data["strategy_name"]
self._data = data
self.tradetool : TradingWidget = None # hxxjava add
self.init_ui()
def init_ui(self):
""""""
self.setFixedHeight(300)
self.setFrameShape(self.Box)
self.setLineWidth(1)
self.init_button = QtWidgets.QPushButton("初始化")
self.init_button.clicked.connect(self.init_strategy)
self.start_button = QtWidgets.QPushButton("启动")
self.start_button.clicked.connect(self.start_strategy)
self.start_button.setEnabled(False)
self.stop_button = QtWidgets.QPushButton("停止")
self.stop_button.clicked.connect(self.stop_strategy)
self.stop_button.setEnabled(False)
self.trade_button = QtWidgets.QPushButton("交易") # hxxjava add
self.trade_button.clicked.connect(self.show_tradetool) # hxxjava add
self.trade_button.setEnabled(False) # hxxjava add
self.edit_button = QtWidgets.QPushButton("编辑")
self.edit_button.clicked.connect(self.edit_strategy)
self.remove_button = QtWidgets.QPushButton("移除")
self.remove_button.clicked.connect(self.remove_strategy)
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.init_button)
hbox.addWidget(self.start_button)
hbox.addWidget(self.trade_button) # hxxjava add
hbox.addWidget(self.stop_button)
hbox.addWidget(self.edit_button)
hbox.addWidget(self.remove_button)
# hxxjava change to self.vbox,old is vbox
self.vbox = QtWidgets.QVBoxLayout()
self.vbox.addWidget(label)
self.vbox.addLayout(hbox)
self.vbox.addWidget(self.parameters_monitor)
self.vbox.addWidget(self.variables_monitor)
self.setLayout(self.vbox)
def update_data(self, data: dict):
""""""
self._data = data
self.parameters_monitor.update_data(data["parameters"])
self.variables_monitor.update_data(data["variables"])
# Update button status
variables = data["variables"]
inited = variables["inited"]
trading = variables["trading"]
if not inited:
return
self.init_button.setEnabled(False)
if trading:
self.start_button.setEnabled(False)
self.trade_button.setEnabled(True) # hxxjava
self.stop_button.setEnabled(True)
self.edit_button.setEnabled(False)
self.remove_button.setEnabled(False)
else:
self.start_button.setEnabled(True)
self.trade_button.setEnabled(False) # hxxjava
self.stop_button.setEnabled(False)
self.edit_button.setEnabled(True)
self.remove_button.setEnabled(True)
def init_strategy(self):
""""""
self.cta_engine.init_strategy(self.strategy_name)
def start_strategy(self):
""""""
self.cta_engine.start_strategy(self.strategy_name)
def show_tradetool(self): # hxxjava add
""" 为策略显示交易工具 """
if not self.tradetool:
strategy = self.cta_engine.strategies.get(self.strategy_name,None)
if strategy and strategy.trading:
self.tradetool = TradingWidget(strategy,self.cta_engine.event_engine)
self.vbox.addWidget(self.tradetool)
else:
is_visible = self.tradetool.isVisible()
self.tradetool.setVisible(not is_visible)
def stop_strategy(self):
""""""
self.cta_engine.stop_strategy(self.strategy_name)
def edit_strategy(self):
""""""
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):
""""""
result = self.cta_engine.remove_strategy(self.strategy_name)
# Only remove strategy gui manager if it has been removed from engine
if result:
self.cta_manager.remove_strategy(self.strategy_name)
2.2.4 交易组件
创建vnpy\usertools\trading_widget.py文件,其中内容:
"""
条件单交易组件
作者:hxxjava
日线:2022-5-10
"""
from vnpy.trader.ui import QtCore, QtWidgets, QtGui
from vnpy.trader.constant import Direction,Offset
from vnpy.trader.event import EVENT_TICK
from vnpy.event.engine import Event,EventEngine
from vnpy_ctastrategy.base import Condition,CondOrderStatus,ExecutePrice,ConditionOrder
from vnpy_ctastrategy.template import CtaTemplate
class TradingWidget(QtWidgets.QWidget):
"""
CTA strategy manual trading widget.
"""
signal_tick = QtCore.pyqtSignal(Event)
def __init__(self, strategy: CtaTemplate, event_engine: EventEngine):
""""""
super().__init__()
self.strategy: CtaTemplate = strategy
self.event_engine: EventEngine = event_engine
self.vt_symbol: str = strategy.vt_symbol
self.price_digits: int = 0
self.init_ui()
self.register_event()
def init_ui(self) -> None:
""""""
# 交易方向:多/空
self.direction_combo = QtWidgets.QComboBox()
self.direction_combo.addItems(
[Direction.LONG.value, Direction.SHORT.value])
# 开平选择:开/平
self.offset_combo = QtWidgets.QComboBox()
self.offset_combo.addItems([offset.value for offset in Offset])
# 条件类型
conditions = [Condition.BE,Condition.LE,Condition.BT,Condition.LT]
self.condition_combo = QtWidgets.QComboBox()
self.condition_combo.addItems(
[condition.value for condition in conditions])
double_validator = QtGui.QDoubleValidator()
double_validator.setBottom(0)
self.price_line = QtWidgets.QLineEdit()
self.price_line.setValidator(double_validator)
self.exit_line = QtWidgets.QLineEdit()
self.exit_line.setValidator(double_validator)
self.volume_line = QtWidgets.QLineEdit()
self.volume_line.setValidator(double_validator)
self.price_check = QtWidgets.QCheckBox()
self.price_check.setToolTip("设置价格随行情更新")
execute_prices = [ExecutePrice.SETPRICE,ExecutePrice.MARKET,ExecutePrice.EXTREME]
self.execute_price_combo = QtWidgets.QComboBox()
self.execute_price_combo.addItems(
[execute_price.value for execute_price in execute_prices])
send_button = QtWidgets.QPushButton("发出")
send_button.clicked.connect(self.send_condition_order)
hbox = QtWidgets.QHBoxLayout()
hbox.addWidget(QtWidgets.QLabel(f"合约:{self.vt_symbol}"))
hbox.addWidget(QtWidgets.QLabel("方向"))
hbox.addWidget(self.direction_combo)
hbox.addWidget(QtWidgets.QLabel("开平"))
hbox.addWidget(self.offset_combo)
hbox.addWidget(QtWidgets.QLabel("条件"))
hbox.addWidget(self.condition_combo)
hbox.addWidget(QtWidgets.QLabel("触发价"))
hbox.addWidget(self.price_line)
hbox.addWidget(self.price_check)
hbox.addWidget(QtWidgets.QLabel("数量"))
hbox.addWidget(self.volume_line)
hbox.addWidget(QtWidgets.QLabel("执行价"))
hbox.addWidget(self.execute_price_combo)
hbox.addWidget(send_button)
# Overall layout
self.setLayout(hbox)
def register_event(self) -> None:
""""""
self.signal_tick.connect(self.process_tick_event)
self.event_engine.register(EVENT_TICK, self.signal_tick.emit)
def process_tick_event(self, event: Event) -> None:
""""""
tick = event.data
if tick.vt_symbol != self.vt_symbol:
return
if self.price_check.isChecked():
self.price_line.setText(f"{tick.last_price}")
def send_condition_order(self) -> bool:
"""
Send new order manually.
"""
try:
direction = Direction(self.direction_combo.currentText())
offset = Offset(self.offset_combo.currentText())
condition = Condition(self.condition_combo.currentText())
price = float(self.price_line.text())
volume = float(self.volume_line.text())
execute_price = ExecutePrice(self.execute_price_combo.currentText())
order = ConditionOrder(
strategy_name = self.strategy.strategy_name,
vt_symbol=self.vt_symbol,
direction=direction,
offset=offset,
price=price,
volume=volume,
condition=condition,
execute_price=execute_price
)
self.strategy.send_condition_order(order=order)
print(f"发出条件单 : vt_symbol={self.vt_symbol},success ! {order}")
return True
except:
print(f"发出条件单 : vt_symbol={self.vt_symbol},input error !")
return False
2.3 有条件单功能的CTA引擎——MyCtaEngine
2.3.1 MyCtaEngine的实现
在vnpy_ctastrategy\engine.py中对CtaEngine进行如下扩展:
class MyCtaEngine(CtaEngine):
""" """
condition_filename = "condition_order.json" # 历史条件单存储文件
def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
""""""
super().__init__(main_engine,event_engine)
self.condition_orders:Dict[str,ConditionOrder] = {} # strategy_name: dict
def load_active_condtion_orders(self):
""" """
return {}
def process_tick_event(self,event:Event):
""" 用tick的价格检查条件单 """
super().process_tick_event(event)
tick:TickData = event.data
all_condition_orders = [order for order in self.condition_orders.values() \
if order.vt_symbol == tick.vt_symbol and order.status == CondOrderStatus.WAITING]
for order in all_condition_orders:
# 检查条件单是否满足条件
self.check_condition_order(order,tick)
def check_condition_order(self,order:ConditionOrder,tick:TickData):
""" 检查条件单是否满足条件 """
strategy = self.strategies.get(order.strategy_name,None)
if not strategy or not strategy.trading:
return False
price = tick.last_price
is_be = order.condition == Condition.BE and price >= order.price
is_le = order.condition == Condition.LE and price <= order.price
is_bt = order.condition == Condition.BT and price > order.price
is_lt = order.condition == Condition.LT and price < order.price
if is_be or is_le or is_bt or is_lt:
# 满足触发条件
if order.execute_price == ExecutePrice.MARKET:
# 取市场价
price = tick.last_price
elif order.execute_price == ExecutePrice.EXTREME:
# 取极限价
price = tick.limit_up if order.direction == Direction.LONG else tick.limit_down
else:
# 取设定价
price = order.price
# 执行委托
order_ids = strategy.send_order(
direction = order.direction,
offset=order.offset,
price=price,
volume=order.volume
)
if order_ids:
order.trigger_time = tick.datetime
order.status = CondOrderStatus.TRIGGERED
self.event_engine.put(Event(EVENT_CONDITION_ORDER,order))
def send_condition_order(self,order:ConditionOrder):
""" """
strategy = self.strategies.get(order.strategy_name,None)
if not strategy or not strategy.trading:
return False
if order.cond_orderid not in self.condition_orders:
self.condition_orders[order.cond_orderid] = order
self.event_engine.put(Event(EVENT_CONDITION_ORDER,order))
return True
return False
def cancel_condition_order(self,cond_orderid:str):
""" """
order:ConditionOrder = self.condition_orders.get(cond_orderid,None)
if not order:
return False
order.status = CondOrderStatus.CANCELLED
self.event_engine.put(Event(EVENT_CONDITION_ORDER,order))
return True
def cancel_all_condition_orders(self,strategy_name:str):
""" """
for order in self.condition_orders.values():
if order.strategy_name == strategy_name and order.status == CondOrderStatus.WAITING:
order.status = CondOrderStatus.CANCELLED
self.call_strategy_func(strategy,strategy.on_condition_order)
self.event_engine.put(Event(EVENT_CONDITION_ORDER,order))
return True
2.3.2 更换CtaEngine
对vnpy_ctastrategy__init__.py中的CtaTemplate进行如下修改:
from .engine import MyCtaEngine # hxxjava add
class CtaStrategyApp(BaseApp):
""""""
app_name = APP_NAME
app_module = __module__
app_path = Path(__file__).parent
display_name = "CTA策略"
# engine_class = CtaEngine
engine_class = MyCtaEngine # hxxjava add
widget_name = "CtaManager"
icon_name = str(app_path.joinpath("ui", "cta.ico"))
2.3.3 对CtaTemplate进行扩展
对vnpy_ctastrategy\template.py中的CtaTemplate进行如下扩展:
@virtual
def on_condition_order(self, cond_order: ConditionOrder):
"""
Callback of condition order update.
"""
pass
def send_condition_order(self,order:ConditionOrder): # hxxjava add
""" """
if not self.trading:
return False
return self.cta_engine.send_condition_order(order)
def cancel_condition_order(self,cond_orderid:str): # hxxjava add
""" """
return self.cta_engine.cancel_condition_order(cond_orderid)
def cancel_all_condition_orders(self): # hxxjava add
""" """
return self.cta_engine.cancel_all_condition_orders(self)
2.3.4 CTA用户策略中如何使用条件单功能
1)CTA策略中的条件单被触发点回调通知:
def on_condition_order(self, cond_order: ConditionOrder):
"""
Callback of condition order update.
"""
print(f"条件单已经执行,cond_order = {cond_order}")
2)发起条件单
cond_order = ConditionOrder(... ...)
self.send_condition_order(cond_order)
3)取消条件单
self.cancel_condition_order(cond_orderid)
4)取消策略的所有条件单
self.cancel_all_condition_orders()
3. 条件单应该在vnpy系统中被广泛使用
- 它应该运行在整个vnpy系统的底层,为各类的应用策略提供委托支持,
- 对连续密集触发点条件单实施流控限制,避免交易接口出现丢单的流控问题
- 各类应用的引擎应该提供send_condition_order()接口,实现条件单到不同应用委托执行逻辑
- 各类应用的模板应该提供on_condition_order回调,解决条件单触发后对不同类型用户策略的触发通知
- 用户策略尽量使用条件单来执行交易,避免直接执行来自接口的委托函数