Skip to content

Commit b61f7d0

Browse files
committed
Merge branch 'dev' into 'master'
Dev See merge request server/openapi/openapi-python-sdk!123
2 parents 77bf66e + c022e76 commit b61f7d0

File tree

15 files changed

+421
-14
lines changed

15 files changed

+421
-14
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## 2.1.4 (2022-07-18)
2+
### New
3+
- 新增历史资产分析接口 `TradeClient.get_analytics_asset`
4+
- 新增合约价格校正工具函数 `tigeropen.common.util.price_util.PriceUtil`, 可根据请求到的合约tick size, 校正输入的下单价格
5+
- 订单对象新增属性: 更新时间: `update_time`
6+
- 查询订单列表接口 `TradeClieng.get_orders (get_open_orders/get_filled_orders)` 支持指定排序规则, 按照订单创建时间或订单状态更新时间排序
7+
- 查询持仓接口 `TradeClient.get_positions` 支持期权要素(expiry, strike, put_call)参数过滤
8+
9+
110
## 2.1.3 (2022-07-01)
211
### New
312
- 长连接新增逐笔订阅: `PushClient.subscribe_tick`, 退订 `PushClient.unsubscribe_tick`

tests/test_utils.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# @Date : 2022/7/14
4+
# @Author : sukai
5+
import unittest
6+
7+
from tigeropen.common.util.price_util import PriceUtil
8+
9+
10+
class TestUtils(unittest.TestCase):
11+
def test_price_util(self):
12+
delta = 1e-6
13+
tick_sizes = [{'begin': '0', 'end': '1', 'type': 'CLOSED', 'tick_size': 0.0001},
14+
{'begin': '1', 'end': 'Infinity', 'type': 'OPEN', 'tick_size': 0.01}]
15+
self.assertFalse(PriceUtil.match_tick_size(None, None))
16+
self.assertTrue(PriceUtil.match_tick_size(2.33, tick_sizes))
17+
self.assertTrue(PriceUtil.match_tick_size(2.3, tick_sizes))
18+
self.assertFalse(PriceUtil.match_tick_size(1.334, tick_sizes))
19+
self.assertTrue(PriceUtil.match_tick_size(0.5, tick_sizes))
20+
self.assertFalse(PriceUtil.match_tick_size(0.22223, tick_sizes))
21+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(2.334, tick_sizes, True), 2.34, delta=delta)
22+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(2.334, tick_sizes, False), 2.33, delta=delta)
23+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(2.3345, None), 2.3345, delta=delta)
24+
25+
tick_sizes = [{'begin': '0', 'end': '1', 'type': 'CLOSED', 'tick_size': 0.0005},
26+
{'begin': '1', 'end': '100', 'type': 'OPEN_CLOSED', 'tick_size': 0.05},
27+
{'begin': '100', 'end': '1000', 'type': 'OPEN_CLOSED', 'tick_size': 1.0},
28+
{'begin': '1000', 'end': '10000', 'type': 'OPEN_CLOSED', 'tick_size': 2.0},
29+
{'begin': '10000', 'end': 'Infinity', 'type': 'OPEN', 'tick_size': 5.0}]
30+
self.assertTrue(PriceUtil.match_tick_size(0.0005, tick_sizes))
31+
self.assertTrue(PriceUtil.match_tick_size(1.15, tick_sizes))
32+
self.assertFalse(PriceUtil.match_tick_size(0.0008, tick_sizes))
33+
self.assertFalse(PriceUtil.match_tick_size(1.11, tick_sizes))
34+
self.assertTrue(PriceUtil.match_tick_size(300, tick_sizes))
35+
self.assertFalse(PriceUtil.match_tick_size(300.5, tick_sizes))
36+
self.assertFalse(PriceUtil.match_tick_size(5001, tick_sizes))
37+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(0.0021, tick_sizes), 0.002, delta=delta)
38+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(0.0021, tick_sizes, True), 0.0025, delta=delta)
39+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(3.027, tick_sizes), 3.0, delta=delta)
40+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(3.027, tick_sizes, True), 3.05, delta=delta)
41+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(200.5, tick_sizes), 200, delta=delta)
42+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(2001, tick_sizes, True), 2002, delta=delta)
43+
self.assertAlmostEqual(PriceUtil.fix_price_by_tick_size(20001, tick_sizes, True), 20005, delta=delta)

tigeropen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
55
@author: gaoan
66
"""
7-
__VERSION__ = '2.1.3'
7+
__VERSION__ = '2.1.4'

tigeropen/common/consts/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ class SecurityType(Enum):
4949
CASH = 'CASH' # 外汇
5050

5151

52+
@unique
53+
class SegmentType(Enum):
54+
SEC = 'SEC'
55+
FUT = 'FUT'
56+
57+
5258
@unique
5359
class Currency(Enum):
5460
"""Enum for currency """
@@ -140,3 +146,17 @@ class IndustryLevel(Enum):
140146
GGROUP = 'GGROUP'
141147
GIND = 'GIND'
142148
GSUBIND = 'GSUBIND'
149+
150+
151+
@unique
152+
class TickSizeType(Enum):
153+
CLOSED = 'CLOSED'
154+
OPEN_CLOSED = 'OPEN_CLOSED'
155+
OPEN = 'OPEN'
156+
157+
158+
@unique
159+
class OrderSortBy(Enum):
160+
LATEST_CREATED = 'LATEST_CREATED'
161+
LATEST_STATUS_UPDATED = 'LATEST_STATUS_UPDATED'
162+

tigeropen/common/consts/service_types.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
INACTIVE_ORDERS = "inactive_orders" # 已撤销订单
2424
FILLED_ORDERS = "filled_orders" # 已成交订单
2525
ORDER_TRANSACTIONS = "order_transactions" # 订单成交记录
26+
ANALYTICS_ASSET = "analytics_asset"
2627

2728
"""
2829
合约
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# @Date : 2022/7/13
4+
# @Author : sukai
5+
from decimal import Decimal, ROUND_DOWN
6+
7+
import math
8+
9+
from tigeropen.common.consts import TickSizeType
10+
11+
12+
class PriceUtil:
13+
INFINITY = 'Infinity'
14+
RELATIVE_TOLERANCE = 1e-6
15+
16+
@staticmethod
17+
def match_tick_size(price, tick_sizes):
18+
"""
19+
check if the price matches the tick sizes
20+
:param price: user input price
21+
:param tick_sizes: tick sizes list like:
22+
[{'begin': '0', 'end': '1', 'type': 'CLOSED', 'tick_size': 0.0001},
23+
{'begin': '1', 'end': 'Infinity', 'type': 'OPEN', 'tick_size': 0.01}]
24+
:return:
25+
"""
26+
if not price or not tick_sizes:
27+
return False
28+
fixed_price = PriceUtil.fix_price_by_tick_size(price=price, tick_sizes=tick_sizes)
29+
return math.isclose(price, fixed_price, rel_tol=PriceUtil.RELATIVE_TOLERANCE)
30+
31+
@staticmethod
32+
def fix_price_by_tick_size(price, tick_sizes, is_up=False):
33+
"""
34+
fix the user input price by tick sizes of the contract
35+
:param price:
36+
:param tick_sizes:
37+
:param is_up:
38+
:return:
39+
"""
40+
if not price:
41+
return None
42+
tick_size_item = PriceUtil._find_tick_size_item(price=price, tick_sizes=tick_sizes)
43+
if not tick_size_item:
44+
return price
45+
min_tick = tick_size_item.get('tick_size')
46+
begin = tick_size_item.get('begin')
47+
return PriceUtil._round_with_tick(price=price, begin=begin, min_tick=min_tick, is_up=is_up)
48+
49+
@staticmethod
50+
def _find_tick_size_item(price, tick_sizes):
51+
"""
52+
:param price:
53+
:param tick_sizes: tick size infos, example:
54+
[{'begin': '0', 'end': '1', 'type': 'CLOSED', 'tick_size': 0.0001},
55+
{'begin': '1', 'end': 'Infinity', 'type': 'OPEN', 'tick_size': 0.01}]
56+
:return: dict
57+
"""
58+
if not price or not tick_sizes:
59+
return None
60+
for item in tick_sizes:
61+
type_ = item.get('type')
62+
begin = float(item.get('begin'))
63+
end = float('inf') if PriceUtil.INFINITY == item.get('end') else float(item.get('end'))
64+
65+
if TickSizeType.OPEN.value == type_:
66+
if begin < price < end:
67+
return item
68+
elif TickSizeType.OPEN_CLOSED.value == type_:
69+
if begin < price <= end:
70+
return item
71+
elif TickSizeType.CLOSED.value == type_:
72+
if begin <= price <= end:
73+
return item
74+
return None
75+
76+
@staticmethod
77+
def _round_with_tick(price, begin, min_tick, is_up):
78+
p = Decimal(str(price))
79+
t = Decimal(str(min_tick))
80+
base = Decimal(str(begin))
81+
multiple = (p - base) / t
82+
if multiple <= 0:
83+
return price
84+
if is_up:
85+
multiple += Decimal(1)
86+
87+
return float(multiple.quantize(0, ROUND_DOWN) * t + base)

tigeropen/examples/trade_client_demo.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import logging
88
import traceback
99

10+
from tigeropen.common.util.price_util import PriceUtil
1011
from tigeropen.trade.domain.order import OrderStatus
1112
from tigeropen.trade.request.model import AccountsParams
1213
from tigeropen.tiger_open_client import TigerOpenClient
@@ -63,6 +64,9 @@ def get_account_apis():
6364
# 综合/模拟账户获取资产
6465
openapi_client.get_prime_assets()
6566

67+
# get asset history
68+
openapi_client.get_analytics_asset(start_date='2021-12-01', end_date='2021-12-07')
69+
6670

6771
def trade_apis():
6872
account = client_config.account
@@ -119,6 +123,13 @@ def trade_apis():
119123
order_legs = openapi_client.get_open_orders(account, parent_id=main_order.order_id)
120124
print(order_legs)
121125

126+
# adjust price by contract tick sizes
127+
contract = openapi_client.get_contract('UVXY')
128+
price = 10.125
129+
if not PriceUtil.match_tick_size(price, contract.tick_sizes):
130+
price = PriceUtil.fix_price_by_tick_size(price, contract.tick_sizes)
131+
132+
122133

123134
def algo_order_demo():
124135
account = client_config.account

tigeropen/push/push_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@
6363
'latestTime': 'trade_time', 'contractId': 'contract_id', 'trailStopPrice': 'trail_stop_price',
6464
'trailingPercent': 'trailing_percent', 'percentOffset': 'percent_offset', 'action': 'action',
6565
'status': 'status', 'currency': 'currency', 'remaining': 'remaining', 'id': 'id',
66-
'segment': 'segment', 'identifier': 'identifier', 'replaceStatus': 'replace_status'}
66+
'segment': 'segment', 'identifier': 'identifier', 'replaceStatus': 'replace_status',
67+
'updateTime': 'update_time'}
6768

6869
if sys.platform == 'linux' or sys.platform == 'linux2':
6970
KEEPALIVE = True

tigeropen/trade/domain/contract.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def __init__(self, symbol=None, currency=None, contract_id=None, sec_type=None,
1515
long_maintenance_margin=None, contract_month=None, identifier=None, primary_exchange=None,
1616
market=None, min_tick=None, trading_class=None, status=None, continuous=None, trade=None,
1717
marginable=None, close_only=None,
18-
last_trading_date=None, first_notice_date=None, last_bidding_close_time=None):
18+
last_trading_date=None, first_notice_date=None, last_bidding_close_time=None, tick_sizes=None):
1919
self.contract_id = contract_id
2020
self.symbol = symbol
2121
self.currency = get_enum_value(currency)
@@ -54,6 +54,8 @@ def __init__(self, symbol=None, currency=None, contract_id=None, sec_type=None,
5454
self.market = market
5555
# 最小报价单位
5656
self.min_tick = min_tick
57+
# tick size info list
58+
self.tick_sizes = tick_sizes
5759
# 合约的交易级别名称
5860
self.trading_class = trading_class
5961
# 状态

tigeropen/trade/domain/order.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ class Order:
1616
"quantity", "filled", "_remaining", "avg_fill_price", "commission", "realized_pnl", "_status",
1717
"trail_stop_price", "limit_price", "aux_price", "trailing_percent", "percent_offset", "action",
1818
"order_type", "time_in_force", "outside_rth", "order_legs", "algo_params", "algo_strategy",
19-
"secret_key", "liquidation", "discount", "attr_desc", "source", 'adjust_limit', 'sub_ids', "user_mark"]
19+
"secret_key", "liquidation", "discount", "attr_desc", "source", 'adjust_limit', 'sub_ids', "user_mark",
20+
"update_time"]
2021

2122
def __init__(self, account, contract, action, order_type, quantity, limit_price=None, aux_price=None,
2223
trail_stop_price=None, trailing_percent=None, percent_offset=None, time_in_force=None,
@@ -31,6 +32,7 @@ def __init__(self, account, contract, action, order_type, quantity, limit_price=
3132
- order_time: 下单时间
3233
- reason: 下单失败时, 会返回失败原因的描述
3334
- trade_time: 最新成交时间
35+
- update_time: order updated time
3436
- action: 交易方向, 'BUY' / 'SELL'
3537
- quantity: 下单数量
3638
- filled: 成交数量
@@ -84,6 +86,7 @@ def __init__(self, account, contract, action, order_type, quantity, limit_price=
8486
self.percent_offset = percent_offset
8587
self.order_time = order_time
8688
self.trade_time = trade_time
89+
self.update_time = kwargs.get('update_time')
8790
self.order_legs = order_legs
8891
self.algo_params = algo_params
8992
self.secret_key = secret_key

0 commit comments

Comments
 (0)