Skip to content

Commit 3c99168

Browse files
committed
price util used to adjust price
1 parent 73348ab commit 3c99168

File tree

3 files changed

+129
-1
lines changed

3 files changed

+129
-1
lines changed

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/common/util/price_util.py

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,86 @@
22
#
33
# @Date : 2022/7/13
44
# @Author : sukai
5+
from decimal import Decimal, ROUND_DOWN
6+
7+
import math
8+
9+
from tigeropen.common.consts import TickSizeType
10+
511

612
class PriceUtil:
13+
INFINITY = 'Infinity'
14+
RELATIVE_TOLERANCE = 1e-6
715

816
@staticmethod
917
def match_tick_size(price, tick_sizes):
10-
pass
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: 8 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
@@ -119,6 +120,13 @@ def trade_apis():
119120
order_legs = openapi_client.get_open_orders(account, parent_id=main_order.order_id)
120121
print(order_legs)
121122

123+
# adjust price by contract tick sizes
124+
contract = openapi_client.get_contract('UVXY')
125+
price = 10.125
126+
if not PriceUtil.match_tick_size(price, contract.tick_sizes):
127+
price = PriceUtil.fix_price_by_tick_size(price, contract.tick_sizes)
128+
129+
122130

123131
def algo_order_demo():
124132
account = client_config.account

0 commit comments

Comments
 (0)