opinion-clob-sdk 0.1.9__py3-none-any.whl → 0.1.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of opinion-clob-sdk might be problematic. Click here for more details.
- opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +1 -1
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +52 -28
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/__init__.py +26 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/__init__.py +0 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contract_caller.py +390 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contracts/__init__.py +0 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contracts/conditional_tokens.py +707 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/contracts/erc20.py +111 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/exception.py +11 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/__init__.py +0 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/__init__.py +0 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/base_builder.py +41 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/exception.py +2 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/order_builder.py +90 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/builders/order_builder_test.py +40 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/constants.py +2 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/__init__.py +0 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/order.py +254 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/order_type.py +9 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/sides.py +8 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/model/signatures.py +8 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/signer.py +20 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/py_order_utils/utils.py +139 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/__init__.py +0 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/constants.py +19 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/eip712/__init__.py +176 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/enums.py +6 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/exceptions.py +94 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/multisend.py +347 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe.py +141 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/__init__.py +0 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/compatibility_fallback_handler_v1_3_0.py +327 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/multisend_v1_3_0.py +22 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/safe_v1_3_0.py +1035 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_contracts/utils.py +26 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_signature.py +364 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_test.py +37 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/safe_tx.py +437 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/signatures.py +63 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/typing.py +17 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/chain/safe/utils.py +218 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/config.py +4 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/model.py +19 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/sdk.py +957 -0
- opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/opinion_clob_sdk/verify_api_calls.py +135 -0
- {opinion_clob_sdk-0.1.9.dist-info → opinion_clob_sdk-0.1.10.dist-info}/METADATA +1 -1
- {opinion_clob_sdk-0.1.9.dist-info → opinion_clob_sdk-0.1.10.dist-info}/RECORD +65 -22
- {opinion_clob_sdk-0.1.9.dist-info → opinion_clob_sdk-0.1.10.dist-info}/WHEEL +0 -0
- {opinion_clob_sdk-0.1.9.dist-info → opinion_clob_sdk-0.1.10.dist-info}/top_level.txt +0 -0
|
@@ -42,19 +42,22 @@ def prepend_zx(in_str: str) -> str:
|
|
|
42
42
|
def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, decimals: int) -> tuple[int, int]:
|
|
43
43
|
"""
|
|
44
44
|
Calculate the maker and taker amounts based on the price and side.
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
Uses precise Decimal arithmetic to ensure precision within 6 significant digits
|
|
47
|
+
(matching the matching engine's precision limit).
|
|
48
|
+
|
|
46
49
|
Args:
|
|
47
50
|
price: The price of the order (between 0.001 and 0.999)
|
|
48
51
|
maker_amount: The maker amount in base units
|
|
49
52
|
side: The order side (BUY or SELL)
|
|
50
|
-
decimals: The number of decimal places for the currency
|
|
51
|
-
|
|
53
|
+
decimals: The number of decimal places for the currency (unused, kept for compatibility)
|
|
54
|
+
|
|
52
55
|
Returns:
|
|
53
56
|
tuple[int, int]: A tuple containing (recalculated_maker_amount, taker_amount)
|
|
54
|
-
For BUY: price = taker/
|
|
55
|
-
For SELL: price = taker/maker
|
|
57
|
+
For BUY: price = maker/taker, so taker = maker/price
|
|
58
|
+
For SELL: price = taker/maker, so taker = maker*price
|
|
56
59
|
"""
|
|
57
|
-
|
|
60
|
+
|
|
58
61
|
# Validate price using Decimal for exact comparison
|
|
59
62
|
try:
|
|
60
63
|
price_decimal = Decimal(str(price))
|
|
@@ -68,48 +71,69 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
|
|
|
68
71
|
if not (min_price <= price_decimal <= max_price):
|
|
69
72
|
raise ValueError(f"Price must be between {min_price} and {max_price} (inclusive), got {price}")
|
|
70
73
|
|
|
71
|
-
# Ensure price doesn't have excessive precision (max
|
|
72
|
-
if price_decimal.as_tuple().exponent < -
|
|
73
|
-
raise ValueError(f"Price precision cannot exceed
|
|
74
|
+
# Ensure price doesn't have excessive precision (max 6 decimal places to match engine precision)
|
|
75
|
+
if price_decimal.as_tuple().exponent < -6:
|
|
76
|
+
raise ValueError(f"Price precision cannot exceed 6 decimal places, got {price}")
|
|
74
77
|
|
|
75
78
|
maker_decimal = Decimal(str(maker_amount))
|
|
76
|
-
decimals_decimal = Decimal(str(decimals))
|
|
77
79
|
|
|
78
80
|
if side == OrderSide.BUY:
|
|
79
81
|
# For BUY: price = maker/taker
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
82
|
+
# Goal: Find maximum taker such that floor(taker * price) <= maker
|
|
83
|
+
# Then use recalculated_maker = floor(taker * price)
|
|
84
|
+
#
|
|
85
|
+
# This ensures precision: given (recalculated_maker, taker, price),
|
|
86
|
+
# you can always verify: recalculated_maker = floor(taker * price)
|
|
87
|
+
|
|
88
|
+
# Calculate exact taker = maker / price
|
|
83
89
|
exact_taker = maker_decimal / price_decimal
|
|
84
|
-
taker_amount = int(math.floor(exact_taker))
|
|
85
90
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
# Start with floor(exact_taker)
|
|
92
|
+
taker_amount = int(exact_taker)
|
|
93
|
+
|
|
94
|
+
# Calculate corresponding maker = floor(taker * price)
|
|
95
|
+
recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
|
|
96
|
+
recalculated_maker_amount = int(recalculated_maker_decimal)
|
|
97
|
+
|
|
98
|
+
# Try to increment taker to use more of the available maker amount
|
|
99
|
+
# but ensure we don't exceed the original maker_amount
|
|
100
|
+
while taker_amount > 0:
|
|
101
|
+
test_taker = taker_amount + 1
|
|
102
|
+
test_maker_decimal = Decimal(str(test_taker)) * price_decimal
|
|
103
|
+
test_maker = int(test_maker_decimal)
|
|
104
|
+
|
|
105
|
+
if test_maker <= maker_amount:
|
|
106
|
+
# We can use this higher taker value
|
|
107
|
+
taker_amount = test_taker
|
|
108
|
+
recalculated_maker_amount = test_maker
|
|
109
|
+
else:
|
|
110
|
+
# Can't increase further without exceeding maker_amount
|
|
111
|
+
break
|
|
89
112
|
|
|
90
|
-
# If recalculated maker exceeds original, reduce taker by 1 and recalculate
|
|
91
|
-
while recalculated_maker_amount > maker_amount and taker_amount > 0:
|
|
92
|
-
taker_amount -= 1
|
|
93
|
-
exact_maker = Decimal(str(taker_amount)) * price_decimal
|
|
94
|
-
recalculated_maker_amount = int(math.floor(exact_maker))
|
|
95
113
|
else: # SELL
|
|
96
114
|
# For SELL: price = taker/maker
|
|
97
|
-
#
|
|
115
|
+
# We want: taker = floor(maker * price)
|
|
116
|
+
# Maker stays the same
|
|
117
|
+
|
|
118
|
+
# Calculate exact taker = maker * price
|
|
98
119
|
exact_taker = maker_decimal * price_decimal
|
|
99
|
-
|
|
120
|
+
|
|
121
|
+
# Round down to get integer taker
|
|
122
|
+
taker_amount = int(exact_taker)
|
|
100
123
|
|
|
101
124
|
# For SELL, maker amount doesn't change
|
|
102
125
|
recalculated_maker_amount = int(maker_amount)
|
|
103
|
-
|
|
126
|
+
|
|
104
127
|
# Ensure amounts are at least 1
|
|
105
128
|
taker_amount = int(max(1, taker_amount))
|
|
106
129
|
recalculated_maker_amount = int(max(1, recalculated_maker_amount))
|
|
107
130
|
|
|
108
131
|
logging.debug(f"Order calculation: taker_amount={taker_amount}, recalculated_maker_amount={recalculated_maker_amount}")
|
|
109
132
|
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
# Validate the calculated price is within bounds
|
|
134
|
+
calculated_price = recalculated_maker_amount / taker_amount if side == OrderSide.BUY else taker_amount / recalculated_maker_amount
|
|
135
|
+
|
|
112
136
|
if calculated_price > 0.999 or calculated_price < 0.001:
|
|
113
137
|
raise ValueError("invalid taker_amount and recalculated_maker_amount")
|
|
114
|
-
|
|
138
|
+
|
|
115
139
|
return recalculated_maker_amount, taker_amount
|
|
@@ -42,19 +42,22 @@ def prepend_zx(in_str: str) -> str:
|
|
|
42
42
|
def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, decimals: int) -> tuple[int, int]:
|
|
43
43
|
"""
|
|
44
44
|
Calculate the maker and taker amounts based on the price and side.
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
Uses precise Decimal arithmetic to ensure precision within 6 significant digits
|
|
47
|
+
(matching the matching engine's precision limit).
|
|
48
|
+
|
|
46
49
|
Args:
|
|
47
50
|
price: The price of the order (between 0.001 and 0.999)
|
|
48
51
|
maker_amount: The maker amount in base units
|
|
49
52
|
side: The order side (BUY or SELL)
|
|
50
|
-
decimals: The number of decimal places for the currency
|
|
51
|
-
|
|
53
|
+
decimals: The number of decimal places for the currency (unused, kept for compatibility)
|
|
54
|
+
|
|
52
55
|
Returns:
|
|
53
56
|
tuple[int, int]: A tuple containing (recalculated_maker_amount, taker_amount)
|
|
54
|
-
For BUY: price = taker/
|
|
55
|
-
For SELL: price = taker/maker
|
|
57
|
+
For BUY: price = maker/taker, so taker = maker/price
|
|
58
|
+
For SELL: price = taker/maker, so taker = maker*price
|
|
56
59
|
"""
|
|
57
|
-
|
|
60
|
+
|
|
58
61
|
# Validate price using Decimal for exact comparison
|
|
59
62
|
try:
|
|
60
63
|
price_decimal = Decimal(str(price))
|
|
@@ -68,48 +71,69 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
|
|
|
68
71
|
if not (min_price <= price_decimal <= max_price):
|
|
69
72
|
raise ValueError(f"Price must be between {min_price} and {max_price} (inclusive), got {price}")
|
|
70
73
|
|
|
71
|
-
# Ensure price doesn't have excessive precision (max
|
|
72
|
-
if price_decimal.as_tuple().exponent < -
|
|
73
|
-
raise ValueError(f"Price precision cannot exceed
|
|
74
|
+
# Ensure price doesn't have excessive precision (max 6 decimal places to match engine precision)
|
|
75
|
+
if price_decimal.as_tuple().exponent < -6:
|
|
76
|
+
raise ValueError(f"Price precision cannot exceed 6 decimal places, got {price}")
|
|
74
77
|
|
|
75
78
|
maker_decimal = Decimal(str(maker_amount))
|
|
76
|
-
decimals_decimal = Decimal(str(decimals))
|
|
77
79
|
|
|
78
80
|
if side == OrderSide.BUY:
|
|
79
81
|
# For BUY: price = maker/taker
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
82
|
+
# Goal: Find maximum taker such that floor(taker * price) <= maker
|
|
83
|
+
# Then use recalculated_maker = floor(taker * price)
|
|
84
|
+
#
|
|
85
|
+
# This ensures precision: given (recalculated_maker, taker, price),
|
|
86
|
+
# you can always verify: recalculated_maker = floor(taker * price)
|
|
87
|
+
|
|
88
|
+
# Calculate exact taker = maker / price
|
|
83
89
|
exact_taker = maker_decimal / price_decimal
|
|
84
|
-
taker_amount = int(math.floor(exact_taker))
|
|
85
90
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
# Start with floor(exact_taker)
|
|
92
|
+
taker_amount = int(exact_taker)
|
|
93
|
+
|
|
94
|
+
# Calculate corresponding maker = floor(taker * price)
|
|
95
|
+
recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
|
|
96
|
+
recalculated_maker_amount = int(recalculated_maker_decimal)
|
|
97
|
+
|
|
98
|
+
# Try to increment taker to use more of the available maker amount
|
|
99
|
+
# but ensure we don't exceed the original maker_amount
|
|
100
|
+
while taker_amount > 0:
|
|
101
|
+
test_taker = taker_amount + 1
|
|
102
|
+
test_maker_decimal = Decimal(str(test_taker)) * price_decimal
|
|
103
|
+
test_maker = int(test_maker_decimal)
|
|
104
|
+
|
|
105
|
+
if test_maker <= maker_amount:
|
|
106
|
+
# We can use this higher taker value
|
|
107
|
+
taker_amount = test_taker
|
|
108
|
+
recalculated_maker_amount = test_maker
|
|
109
|
+
else:
|
|
110
|
+
# Can't increase further without exceeding maker_amount
|
|
111
|
+
break
|
|
89
112
|
|
|
90
|
-
# If recalculated maker exceeds original, reduce taker by 1 and recalculate
|
|
91
|
-
while recalculated_maker_amount > maker_amount and taker_amount > 0:
|
|
92
|
-
taker_amount -= 1
|
|
93
|
-
exact_maker = Decimal(str(taker_amount)) * price_decimal
|
|
94
|
-
recalculated_maker_amount = int(math.floor(exact_maker))
|
|
95
113
|
else: # SELL
|
|
96
114
|
# For SELL: price = taker/maker
|
|
97
|
-
#
|
|
115
|
+
# We want: taker = floor(maker * price)
|
|
116
|
+
# Maker stays the same
|
|
117
|
+
|
|
118
|
+
# Calculate exact taker = maker * price
|
|
98
119
|
exact_taker = maker_decimal * price_decimal
|
|
99
|
-
|
|
120
|
+
|
|
121
|
+
# Round down to get integer taker
|
|
122
|
+
taker_amount = int(exact_taker)
|
|
100
123
|
|
|
101
124
|
# For SELL, maker amount doesn't change
|
|
102
125
|
recalculated_maker_amount = int(maker_amount)
|
|
103
|
-
|
|
126
|
+
|
|
104
127
|
# Ensure amounts are at least 1
|
|
105
128
|
taker_amount = int(max(1, taker_amount))
|
|
106
129
|
recalculated_maker_amount = int(max(1, recalculated_maker_amount))
|
|
107
130
|
|
|
108
131
|
logging.debug(f"Order calculation: taker_amount={taker_amount}, recalculated_maker_amount={recalculated_maker_amount}")
|
|
109
132
|
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
# Validate the calculated price is within bounds
|
|
134
|
+
calculated_price = recalculated_maker_amount / taker_amount if side == OrderSide.BUY else taker_amount / recalculated_maker_amount
|
|
135
|
+
|
|
112
136
|
if calculated_price > 0.999 or calculated_price < 0.001:
|
|
113
137
|
raise ValueError("invalid taker_amount and recalculated_maker_amount")
|
|
114
|
-
|
|
138
|
+
|
|
115
139
|
return recalculated_maker_amount, taker_amount
|
|
@@ -42,19 +42,22 @@ def prepend_zx(in_str: str) -> str:
|
|
|
42
42
|
def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, decimals: int) -> tuple[int, int]:
|
|
43
43
|
"""
|
|
44
44
|
Calculate the maker and taker amounts based on the price and side.
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
Uses precise Decimal arithmetic to ensure precision within 6 significant digits
|
|
47
|
+
(matching the matching engine's precision limit).
|
|
48
|
+
|
|
46
49
|
Args:
|
|
47
50
|
price: The price of the order (between 0.001 and 0.999)
|
|
48
51
|
maker_amount: The maker amount in base units
|
|
49
52
|
side: The order side (BUY or SELL)
|
|
50
|
-
decimals: The number of decimal places for the currency
|
|
51
|
-
|
|
53
|
+
decimals: The number of decimal places for the currency (unused, kept for compatibility)
|
|
54
|
+
|
|
52
55
|
Returns:
|
|
53
56
|
tuple[int, int]: A tuple containing (recalculated_maker_amount, taker_amount)
|
|
54
|
-
For BUY: price = taker/
|
|
55
|
-
For SELL: price = taker/maker
|
|
57
|
+
For BUY: price = maker/taker, so taker = maker/price
|
|
58
|
+
For SELL: price = taker/maker, so taker = maker*price
|
|
56
59
|
"""
|
|
57
|
-
|
|
60
|
+
|
|
58
61
|
# Validate price using Decimal for exact comparison
|
|
59
62
|
try:
|
|
60
63
|
price_decimal = Decimal(str(price))
|
|
@@ -68,48 +71,69 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
|
|
|
68
71
|
if not (min_price <= price_decimal <= max_price):
|
|
69
72
|
raise ValueError(f"Price must be between {min_price} and {max_price} (inclusive), got {price}")
|
|
70
73
|
|
|
71
|
-
# Ensure price doesn't have excessive precision (max
|
|
72
|
-
if price_decimal.as_tuple().exponent < -
|
|
73
|
-
raise ValueError(f"Price precision cannot exceed
|
|
74
|
+
# Ensure price doesn't have excessive precision (max 6 decimal places to match engine precision)
|
|
75
|
+
if price_decimal.as_tuple().exponent < -6:
|
|
76
|
+
raise ValueError(f"Price precision cannot exceed 6 decimal places, got {price}")
|
|
74
77
|
|
|
75
78
|
maker_decimal = Decimal(str(maker_amount))
|
|
76
|
-
decimals_decimal = Decimal(str(decimals))
|
|
77
79
|
|
|
78
80
|
if side == OrderSide.BUY:
|
|
79
81
|
# For BUY: price = maker/taker
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
82
|
+
# Goal: Find maximum taker such that floor(taker * price) <= maker
|
|
83
|
+
# Then use recalculated_maker = floor(taker * price)
|
|
84
|
+
#
|
|
85
|
+
# This ensures precision: given (recalculated_maker, taker, price),
|
|
86
|
+
# you can always verify: recalculated_maker = floor(taker * price)
|
|
87
|
+
|
|
88
|
+
# Calculate exact taker = maker / price
|
|
83
89
|
exact_taker = maker_decimal / price_decimal
|
|
84
|
-
taker_amount = int(math.floor(exact_taker))
|
|
85
90
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
# Start with floor(exact_taker)
|
|
92
|
+
taker_amount = int(exact_taker)
|
|
93
|
+
|
|
94
|
+
# Calculate corresponding maker = floor(taker * price)
|
|
95
|
+
recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
|
|
96
|
+
recalculated_maker_amount = int(recalculated_maker_decimal)
|
|
97
|
+
|
|
98
|
+
# Try to increment taker to use more of the available maker amount
|
|
99
|
+
# but ensure we don't exceed the original maker_amount
|
|
100
|
+
while taker_amount > 0:
|
|
101
|
+
test_taker = taker_amount + 1
|
|
102
|
+
test_maker_decimal = Decimal(str(test_taker)) * price_decimal
|
|
103
|
+
test_maker = int(test_maker_decimal)
|
|
104
|
+
|
|
105
|
+
if test_maker <= maker_amount:
|
|
106
|
+
# We can use this higher taker value
|
|
107
|
+
taker_amount = test_taker
|
|
108
|
+
recalculated_maker_amount = test_maker
|
|
109
|
+
else:
|
|
110
|
+
# Can't increase further without exceeding maker_amount
|
|
111
|
+
break
|
|
89
112
|
|
|
90
|
-
# If recalculated maker exceeds original, reduce taker by 1 and recalculate
|
|
91
|
-
while recalculated_maker_amount > maker_amount and taker_amount > 0:
|
|
92
|
-
taker_amount -= 1
|
|
93
|
-
exact_maker = Decimal(str(taker_amount)) * price_decimal
|
|
94
|
-
recalculated_maker_amount = int(math.floor(exact_maker))
|
|
95
113
|
else: # SELL
|
|
96
114
|
# For SELL: price = taker/maker
|
|
97
|
-
#
|
|
115
|
+
# We want: taker = floor(maker * price)
|
|
116
|
+
# Maker stays the same
|
|
117
|
+
|
|
118
|
+
# Calculate exact taker = maker * price
|
|
98
119
|
exact_taker = maker_decimal * price_decimal
|
|
99
|
-
|
|
120
|
+
|
|
121
|
+
# Round down to get integer taker
|
|
122
|
+
taker_amount = int(exact_taker)
|
|
100
123
|
|
|
101
124
|
# For SELL, maker amount doesn't change
|
|
102
125
|
recalculated_maker_amount = int(maker_amount)
|
|
103
|
-
|
|
126
|
+
|
|
104
127
|
# Ensure amounts are at least 1
|
|
105
128
|
taker_amount = int(max(1, taker_amount))
|
|
106
129
|
recalculated_maker_amount = int(max(1, recalculated_maker_amount))
|
|
107
130
|
|
|
108
131
|
logging.debug(f"Order calculation: taker_amount={taker_amount}, recalculated_maker_amount={recalculated_maker_amount}")
|
|
109
132
|
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
# Validate the calculated price is within bounds
|
|
134
|
+
calculated_price = recalculated_maker_amount / taker_amount if side == OrderSide.BUY else taker_amount / recalculated_maker_amount
|
|
135
|
+
|
|
112
136
|
if calculated_price > 0.999 or calculated_price < 0.001:
|
|
113
137
|
raise ValueError("invalid taker_amount and recalculated_maker_amount")
|
|
114
|
-
|
|
138
|
+
|
|
115
139
|
return recalculated_maker_amount, taker_amount
|
|
@@ -42,19 +42,22 @@ def prepend_zx(in_str: str) -> str:
|
|
|
42
42
|
def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, decimals: int) -> tuple[int, int]:
|
|
43
43
|
"""
|
|
44
44
|
Calculate the maker and taker amounts based on the price and side.
|
|
45
|
-
|
|
45
|
+
|
|
46
|
+
Uses precise Decimal arithmetic to ensure precision within 6 significant digits
|
|
47
|
+
(matching the matching engine's precision limit).
|
|
48
|
+
|
|
46
49
|
Args:
|
|
47
50
|
price: The price of the order (between 0.001 and 0.999)
|
|
48
51
|
maker_amount: The maker amount in base units
|
|
49
52
|
side: The order side (BUY or SELL)
|
|
50
|
-
decimals: The number of decimal places for the currency
|
|
51
|
-
|
|
53
|
+
decimals: The number of decimal places for the currency (unused, kept for compatibility)
|
|
54
|
+
|
|
52
55
|
Returns:
|
|
53
56
|
tuple[int, int]: A tuple containing (recalculated_maker_amount, taker_amount)
|
|
54
|
-
For BUY: price = taker/
|
|
55
|
-
For SELL: price = taker/maker
|
|
57
|
+
For BUY: price = maker/taker, so taker = maker/price
|
|
58
|
+
For SELL: price = taker/maker, so taker = maker*price
|
|
56
59
|
"""
|
|
57
|
-
|
|
60
|
+
|
|
58
61
|
# Validate price using Decimal for exact comparison
|
|
59
62
|
try:
|
|
60
63
|
price_decimal = Decimal(str(price))
|
|
@@ -68,48 +71,69 @@ def calculate_order_amounts(price: float, maker_amount: int, side: OrderSide, de
|
|
|
68
71
|
if not (min_price <= price_decimal <= max_price):
|
|
69
72
|
raise ValueError(f"Price must be between {min_price} and {max_price} (inclusive), got {price}")
|
|
70
73
|
|
|
71
|
-
# Ensure price doesn't have excessive precision (max
|
|
72
|
-
if price_decimal.as_tuple().exponent < -
|
|
73
|
-
raise ValueError(f"Price precision cannot exceed
|
|
74
|
+
# Ensure price doesn't have excessive precision (max 6 decimal places to match engine precision)
|
|
75
|
+
if price_decimal.as_tuple().exponent < -6:
|
|
76
|
+
raise ValueError(f"Price precision cannot exceed 6 decimal places, got {price}")
|
|
74
77
|
|
|
75
78
|
maker_decimal = Decimal(str(maker_amount))
|
|
76
|
-
decimals_decimal = Decimal(str(decimals))
|
|
77
79
|
|
|
78
80
|
if side == OrderSide.BUY:
|
|
79
81
|
# For BUY: price = maker/taker
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
82
|
+
# Goal: Find maximum taker such that floor(taker * price) <= maker
|
|
83
|
+
# Then use recalculated_maker = floor(taker * price)
|
|
84
|
+
#
|
|
85
|
+
# This ensures precision: given (recalculated_maker, taker, price),
|
|
86
|
+
# you can always verify: recalculated_maker = floor(taker * price)
|
|
87
|
+
|
|
88
|
+
# Calculate exact taker = maker / price
|
|
83
89
|
exact_taker = maker_decimal / price_decimal
|
|
84
|
-
taker_amount = int(math.floor(exact_taker))
|
|
85
90
|
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
# Start with floor(exact_taker)
|
|
92
|
+
taker_amount = int(exact_taker)
|
|
93
|
+
|
|
94
|
+
# Calculate corresponding maker = floor(taker * price)
|
|
95
|
+
recalculated_maker_decimal = Decimal(str(taker_amount)) * price_decimal
|
|
96
|
+
recalculated_maker_amount = int(recalculated_maker_decimal)
|
|
97
|
+
|
|
98
|
+
# Try to increment taker to use more of the available maker amount
|
|
99
|
+
# but ensure we don't exceed the original maker_amount
|
|
100
|
+
while taker_amount > 0:
|
|
101
|
+
test_taker = taker_amount + 1
|
|
102
|
+
test_maker_decimal = Decimal(str(test_taker)) * price_decimal
|
|
103
|
+
test_maker = int(test_maker_decimal)
|
|
104
|
+
|
|
105
|
+
if test_maker <= maker_amount:
|
|
106
|
+
# We can use this higher taker value
|
|
107
|
+
taker_amount = test_taker
|
|
108
|
+
recalculated_maker_amount = test_maker
|
|
109
|
+
else:
|
|
110
|
+
# Can't increase further without exceeding maker_amount
|
|
111
|
+
break
|
|
89
112
|
|
|
90
|
-
# If recalculated maker exceeds original, reduce taker by 1 and recalculate
|
|
91
|
-
while recalculated_maker_amount > maker_amount and taker_amount > 0:
|
|
92
|
-
taker_amount -= 1
|
|
93
|
-
exact_maker = Decimal(str(taker_amount)) * price_decimal
|
|
94
|
-
recalculated_maker_amount = int(math.floor(exact_maker))
|
|
95
113
|
else: # SELL
|
|
96
114
|
# For SELL: price = taker/maker
|
|
97
|
-
#
|
|
115
|
+
# We want: taker = floor(maker * price)
|
|
116
|
+
# Maker stays the same
|
|
117
|
+
|
|
118
|
+
# Calculate exact taker = maker * price
|
|
98
119
|
exact_taker = maker_decimal * price_decimal
|
|
99
|
-
|
|
120
|
+
|
|
121
|
+
# Round down to get integer taker
|
|
122
|
+
taker_amount = int(exact_taker)
|
|
100
123
|
|
|
101
124
|
# For SELL, maker amount doesn't change
|
|
102
125
|
recalculated_maker_amount = int(maker_amount)
|
|
103
|
-
|
|
126
|
+
|
|
104
127
|
# Ensure amounts are at least 1
|
|
105
128
|
taker_amount = int(max(1, taker_amount))
|
|
106
129
|
recalculated_maker_amount = int(max(1, recalculated_maker_amount))
|
|
107
130
|
|
|
108
131
|
logging.debug(f"Order calculation: taker_amount={taker_amount}, recalculated_maker_amount={recalculated_maker_amount}")
|
|
109
132
|
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
# Validate the calculated price is within bounds
|
|
134
|
+
calculated_price = recalculated_maker_amount / taker_amount if side == OrderSide.BUY else taker_amount / recalculated_maker_amount
|
|
135
|
+
|
|
112
136
|
if calculated_price > 0.999 or calculated_price < 0.001:
|
|
113
137
|
raise ValueError("invalid taker_amount and recalculated_maker_amount")
|
|
114
|
-
|
|
138
|
+
|
|
115
139
|
return recalculated_maker_amount, taker_amount
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Opinion CLOB SDK - Python SDK for Opinion Prediction Market CLOB API"""
|
|
2
|
+
|
|
3
|
+
from opinion_clob_sdk.sdk import (
|
|
4
|
+
Client,
|
|
5
|
+
CHAIN_ID_BASE_MAINNET,
|
|
6
|
+
SUPPORTED_CHAIN_IDS
|
|
7
|
+
)
|
|
8
|
+
from opinion_clob_sdk.model import TopicStatus, TopicType, TopicStatusFilter
|
|
9
|
+
from opinion_clob_sdk.chain.exception import (
|
|
10
|
+
BalanceNotEnough,
|
|
11
|
+
NoPositionsToRedeem,
|
|
12
|
+
InsufficientGasBalance
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
__version__ = "0.1.10"
|
|
16
|
+
__all__ = [
|
|
17
|
+
"Client",
|
|
18
|
+
"TopicStatus",
|
|
19
|
+
"TopicType",
|
|
20
|
+
"TopicStatusFilter",
|
|
21
|
+
"CHAIN_ID_BASE_MAINNET",
|
|
22
|
+
"SUPPORTED_CHAIN_IDS",
|
|
23
|
+
"BalanceNotEnough",
|
|
24
|
+
"NoPositionsToRedeem",
|
|
25
|
+
"InsufficientGasBalance"
|
|
26
|
+
]
|