Skip to content

Commit da59d0a

Browse files
committed
Merge branch 'attach_order' into 'dev'
attach order See merge request !42
2 parents 909c51d + e1fe2f5 commit da59d0a

File tree

5 files changed

+140
-21
lines changed

5 files changed

+140
-21
lines changed

tigeropen/common/util/order_utils.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
@author: gaoan
66
"""
7-
from tigeropen.trade.domain.order import Order
7+
from tigeropen.trade.domain.order import Order, OrderLeg
88
from tigeropen.common.consts import OrderStatus
99

1010

@@ -74,6 +74,33 @@ def trail_order(account, contract, action, quantity, trailing_percent=None, aux_
7474
return Order(account, contract, action, 'TRAIL', quantity, trailing_percent=trailing_percent, aux_price=aux_price)
7575

7676

77+
def order_leg(leg_type, price, time_in_force='DAY', outside_rth=None):
78+
"""
79+
附加订单
80+
:param leg_type: 附加订单类型. PROFIT 止盈单类型, LOSS 止损单类型
81+
:param price: 附加订单价格.
82+
:param time_in_force: 附加订单有效期. 'DAY'(当日有效)和'GTC'(取消前有效 Good-Til-Canceled).
83+
:param outside_rth: 附加订单是否允许盘前盘后交易(美股专属). True 允许, False 不允许.
84+
"""
85+
return OrderLeg(leg_type=leg_type, price=price, time_in_force=time_in_force, outside_rth=outside_rth)
86+
87+
88+
def limit_order_with_legs(account, contract, action, quantity, limit_price, order_legs=None):
89+
"""
90+
限价单 + 附加订单(仅环球账户支持)
91+
:param account:
92+
:param contract:
93+
:param action: BUY/SELL
94+
:param quantity:
95+
:param limit_price: 限价单价格
96+
:param order_legs: 附加订单列表
97+
:return:
98+
"""
99+
if order_legs and len(order_legs) > 2:
100+
raise Exception('2 order legs at most')
101+
return Order(account, contract, action, 'LMT', quantity, limit_price=limit_price, order_legs=order_legs)
102+
103+
77104
def get_order_status(value):
78105
"""
79106
Invalid(-2), Initial(-1), PendingCancel(3), Cancelled(4), Submitted(5), Filled(6), Inactive(7), PendingSubmit(8)

tigeropen/examples/trade_client_demo.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
from tigeropen.trade.trade_client import TradeClient
1515
from tigeropen.quote.request import OpenApiRequest
1616
from tigeropen.examples.client_config import get_client_config
17-
# from tigeropen.common.util.contract_utils import stock_contract, option_contract, future_contract
18-
# from tigeropen.common.util.order_utils import limit_order
17+
# from tigeropen.common.util.contract_utils import stock_contract, option_contract_by_symbol, future_contract, \
18+
# war_contract_by_symbol, iopt_contract_by_symbol
19+
from tigeropen.common.util.order_utils import limit_order, limit_order_with_legs, order_leg
1920

2021
logging.basicConfig(level=logging.INFO,
2122
format='%(asctime)s %(levelname)s %(message)s',
@@ -96,6 +97,22 @@ def trade_apis():
9697
result = openapi_client.preview_order(order)
9798
print(result)
9899

100+
# 限价单 + 附加订单 (仅主订单为限价单时支持附加订单)
101+
stop_loss_order_leg = order_leg('LOSS', 8.0, time_in_force='GTC') # 附加止损
102+
profit_taker_order_leg = order_leg('PROFIT', 12.0, time_in_force='GTC') # 附加止盈
103+
104+
main_order = openapi_client.create_order(account, contract, 'BUY', 'LMT', quantity=100, limit_price=10.0,
105+
order_legs=[stop_loss_order_leg, profit_taker_order_leg])
106+
# 本地构造限价单 + 附加订单
107+
# main_order = limit_order_with_legs(account, contract, 'BUY', 100, limit_price=10.0,
108+
# order_legs=[stop_loss_order_leg])
109+
110+
openapi_client.place_order(main_order)
111+
print(main_order)
112+
# 查询主订单所关联的附加订单
113+
order_legs = openapi_client.get_open_orders(account, parent_id=main_order.order_id)
114+
print(order_legs)
115+
99116

100117
if __name__ == '__main__':
101118
get_account_info()

tigeropen/trade/domain/order.py

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,38 @@ class Order(object):
1414
__slots__ = ["account", "id", "order_id", "parent_id", "order_time", "reason", "trade_time", "contract", "action",
1515
"quantity", "filled", "_remaining", "avg_fill_price", "commission", "realized_pnl", "_status",
1616
"trail_stop_price", "limit_price", "aux_price", "trailing_percent", "percent_offset", "action",
17-
"order_type", "time_in_force", "outside_rth"]
17+
"order_type", "time_in_force", "outside_rth", "order_legs"]
1818

1919
def __init__(self, account, contract, action, order_type, quantity, limit_price=None, aux_price=None,
2020
trail_stop_price=None, trailing_percent=None, percent_offset=None, time_in_force=None,
2121
outside_rth=None, filled=0, avg_fill_price=0, commission=None, realized_pnl=None,
22-
id=None, order_id=None, parent_id=None, order_time=None, trade_time=None):
22+
id=None, order_id=None, parent_id=None, order_time=None, trade_time=None, order_legs=None):
2323
"""
2424
- account: 订单所属的账户
2525
- id: 全局订单 id
2626
- order_id: 账户自增订单号
27-
- parent_id: 母订单id,目前只用于 TigerTrade App端的附加订单中
27+
- parent_id: 主订单id, 目前只用于 TigerTrade App端的附加订单中
2828
- order_time: 下单时间
29-
- reason: 下单失败时会返回失败原因的描述
29+
- reason: 下单失败时, 会返回失败原因的描述
3030
- trade_time: 最新成交时间
31-
- action: 交易方向 'BUY' / 'SELL'
31+
- action: 交易方向, 'BUY' / 'SELL'
3232
- quantity: 下单数量
3333
- filled: 成交数量
3434
- avg_fill_price: 包含佣金的平均成交价
35-
- commission: 包含佣金印花税证监会费等系列费用
35+
- commission: 包含佣金, 印花税, 证监会费等系列费用
3636
- realized_pnl: 实现盈亏
3737
- trail_stop_price: 跟踪止损单--触发止损单的价格
3838
- limit_price: 限价单价格
39-
- aux_price: 在止损单中表示出发止损单的价格 在移动止损单中 表示跟踪的价差
40-
- trailing_percent: 跟踪止损单-百分比取值范围为0-100
39+
- aux_price: 在止损单中, 表示出发止损单的价格, 在移动止损单中, 表示跟踪的价差
40+
- trailing_percent: 跟踪止损单-百分比, 取值范围为0-100
4141
- percent_offset: None,
42-
- order_type: 订单类型, 'MKT'市价单/'LMT'限价单/'STP'止损单/'STP_LMT'止损限价单/'TRAIL'跟踪止损单
43-
- time_in_force: 有效期,'DAY'日内有效/'GTC'撤销前有效
44-
- outside_rth: 是否支持盘前盘后交易,美股专属
42+
- order_type: 订单类型, 'MKT' 市价单 / 'LMT' 限价单 / 'STP' 止损单 / 'STP_LMT' 止损限价单 / 'TRAIL' 跟踪止损单
43+
- time_in_force: 有效期,'DAY' 日内有效 / 'GTC' 撤销前有效
44+
- outside_rth: 是否允许盘前盘后交易(outside of regular trading hours 美股专属). True 允许, False 不允许
4545
- contract: 合约对象
46-
- status: Order_Status 的枚举 表示订单状态
46+
- status: Order_Status 的枚举, 表示订单状态
4747
- remaining: 未成交的数量
48+
- order_legs: 附加订单列表
4849
"""
4950

5051
self.id = id
@@ -71,6 +72,7 @@ def __init__(self, account, contract, action, order_type, quantity, limit_price=
7172
self.percent_offset = percent_offset
7273
self.order_time = order_time
7374
self.trade_time = trade_time
75+
self.order_legs = order_legs
7476

7577
def to_dict(self):
7678
dct = {name: getattr(self, name) for name in self.__slots__ if name not in ORDER_FIELDS_TO_IGNORE}
@@ -115,3 +117,27 @@ def __unicode__(self):
115117
Unicode representation for this object.
116118
"""
117119
return text_type(repr(self))
120+
121+
122+
class OrderLeg(object):
123+
"""
124+
附加订单
125+
"""
126+
127+
def __init__(self, leg_type, price, time_in_force='DAY', outside_rth=None):
128+
"""
129+
:param leg_type: 附加订单类型(仅限价单支持). PROFIT 止盈单类型, LOSS 止损单类型
130+
:param price: 附加订单价格
131+
:param time_in_force: 附加订单有效期. 'DAY'(当日有效)和'GTC'(取消前有效).
132+
:param outside_rth: 附加订单是否允许盘前盘后交易(美股专属). True 允许, False 不允许.
133+
"""
134+
self.leg_type = leg_type
135+
self.price = price
136+
self.time_in_force = time_in_force
137+
self.outside_rth = outside_rth
138+
139+
def to_dict(self):
140+
return self.__dict__
141+
142+
def __repr__(self):
143+
return "OrderLeg(%s)" % self.to_dict()

tigeropen/trade/request/model.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def expiry(self):
228228

229229
@expiry.setter
230230
def expiry(self, value):
231-
self._expiry= value
231+
self._expiry = value
232232

233233
@property
234234
def strike(self):
@@ -358,6 +358,7 @@ def __init__(self):
358358
self._end_date = None
359359
self._limit = None
360360
self._states = None
361+
self._parent_id = None
361362

362363
@property
363364
def account(self):
@@ -439,6 +440,14 @@ def states(self):
439440
def states(self, value):
440441
self._states = value
441442

443+
@property
444+
def parent_id(self):
445+
return self._parent_id
446+
447+
@parent_id.setter
448+
def parent_id(self, value):
449+
self._parent_id = value
450+
442451
def to_openapi_dict(self):
443452
params = dict()
444453
if self.account:
@@ -471,6 +480,9 @@ def to_openapi_dict(self):
471480
if self.states:
472481
params['states'] = self.states
473482

483+
if self.parent_id:
484+
params['parent_id'] = self.parent_id
485+
474486
return params
475487

476488

@@ -490,6 +502,7 @@ def __init__(self):
490502
self.percent_offset = None
491503
self.time_in_force = None
492504
self.outside_rth = None
505+
self.order_legs = None
493506

494507
def to_openapi_dict(self):
495508
params = dict()
@@ -542,6 +555,33 @@ def to_openapi_dict(self):
542555
if self.outside_rth is not None:
543556
params['outside_rth'] = self.outside_rth
544557

558+
if self.order_legs:
559+
if len(self.order_legs) > 2:
560+
raise Exception('2 order legs at most')
561+
leg_types = set()
562+
for order_leg in self.order_legs:
563+
if order_leg.leg_type == 'PROFIT':
564+
leg_types.add('PROFIT')
565+
params['attach_type'] = 'PROFIT'
566+
if order_leg.price is not None:
567+
params['profit_taker_price'] = order_leg.price
568+
if order_leg.time_in_force is not None:
569+
params['profit_taker_tif'] = order_leg.time_in_force
570+
if order_leg.outside_rth is not None:
571+
params['profit_taker_rth'] = order_leg.outside_rth
572+
if order_leg.leg_type == 'LOSS':
573+
leg_types.add('LOSS')
574+
params['attach_type'] = 'LOSS'
575+
if order_leg.price is not None:
576+
params['stop_loss_price'] = order_leg.price
577+
if order_leg.time_in_force is not None:
578+
params['stop_loss_tif'] = order_leg.time_in_force
579+
if order_leg.outside_rth is not None:
580+
params['stop_loss_rth'] = order_leg.outside_rth
581+
# 括号订单(止盈和止损)
582+
if len(leg_types) == 2:
583+
params['attach_type'] = 'BRACKETS'
584+
545585
return params
546586

547587

tigeropen/trade/trade_client.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,10 @@ def get_orders(self, account=None, sec_type=None, market=Market.ALL, symbol=None
279279
return None
280280

281281
def get_open_orders(self, account=None, sec_type=None, market=Market.ALL, symbol=None, start_time=None,
282-
end_time=None):
282+
end_time=None, parent_id=None):
283283
"""
284284
获取待成交订单列表. 参数同 get_orders
285+
:param parent_id: 主订单 order_id
285286
"""
286287
params = OrdersParams()
287288
params.account = account if account else self._account
@@ -291,6 +292,7 @@ def get_open_orders(self, account=None, sec_type=None, market=Market.ALL, symbol
291292
params.symbol = symbol
292293
params.start_date = start_time
293294
params.end_date = end_time
295+
params.parent_id = parent_id
294296
request = OpenApiRequest(ACTIVE_ORDERS, biz_model=params)
295297
response_content = self.__fetch_data(request)
296298
if response_content:
@@ -377,7 +379,7 @@ def get_order(self, account=None, id=None, order_id=None, is_brief=False):
377379

378380
def create_order(self, account, contract, action, order_type, quantity, limit_price=None, aux_price=None,
379381
trail_stop_price=None, trailing_percent=None, percent_offset=None, time_in_force=None,
380-
outside_rth=None):
382+
outside_rth=None, order_legs=None):
381383
"""
382384
创建订单对象.
383385
:param account:
@@ -392,7 +394,7 @@ def create_order(self, account, contract, action, order_type, quantity, limit_pr
392394
:param percent_offset:
393395
:param time_in_force: 订单有效期, 'DAY'(当日有效)和'GTC'(取消前有效)
394396
:param outside_rth: 是否允许盘前盘后交易(美股专属)
395-
:return:
397+
:param order_legs: 附加订单
396398
"""
397399
params = AccountsParams()
398400
params.account = account if account else self._account
@@ -406,7 +408,8 @@ def create_order(self, account, contract, action, order_type, quantity, limit_pr
406408
order = Order(account, contract, action, order_type, quantity, limit_price=limit_price,
407409
aux_price=aux_price, trail_stop_price=trail_stop_price,
408410
trailing_percent=trailing_percent, percent_offset=percent_offset,
409-
time_in_force=time_in_force, outside_rth=outside_rth, order_id=order_id)
411+
time_in_force=time_in_force, outside_rth=outside_rth, order_id=order_id,
412+
order_legs=order_legs)
410413
return order
411414
else:
412415
raise ApiException(response.code, response.message)
@@ -476,14 +479,20 @@ def place_order(self, order):
476479
params.percent_offset = order.percent_offset
477480
params.time_in_force = order.time_in_force
478481
params.outside_rth = order.outside_rth
482+
params.order_legs = order.order_legs
483+
479484
request = OpenApiRequest(PLACE_ORDER, biz_model=params)
480485
response_content = self.__fetch_data(request)
481486
if response_content:
482487
response = OrderIdResponse()
483488
response.parse_response_content(response_content)
484489
if response.is_success():
485490
order.id = response.id
486-
return response.order_id == order.order_id if order.order_id else True
491+
if order.order_id:
492+
return response.order_id == order.order_id
493+
else:
494+
order.order_id = response.order_id
495+
return True
487496
else:
488497
raise ApiException(response.code, response.message)
489498

0 commit comments

Comments
 (0)