ostium-python-sdk 0.2.107__tar.gz → 2.0.0__tar.gz
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.
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/PKG-INFO +37 -2
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/README.md +31 -1
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/constants.py +1 -0
- ostium_python_sdk-2.0.0/ostium_python_sdk/formulae.py +313 -0
- ostium_python_sdk-2.0.0/ostium_python_sdk/formulae_wrapper.py +204 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/scscript/funding.py +4 -2
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/sdk.py +36 -78
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/subgraph.py +11 -9
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk.egg-info/PKG-INFO +37 -2
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk.egg-info/SOURCES.txt +12 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/setup.py +1 -1
- ostium_python_sdk-2.0.0/tests/test_current_total_profit_p.py +37 -0
- ostium_python_sdk-2.0.0/tests/test_current_total_profit_raw.py +57 -0
- ostium_python_sdk-2.0.0/tests/test_current_trade_profit_p.py +111 -0
- ostium_python_sdk-2.0.0/tests/test_current_trade_profit_raw.py +29 -0
- ostium_python_sdk-2.0.0/tests/test_funding.py +518 -0
- ostium_python_sdk-2.0.0/tests/test_get_trade_funding_fee.py +25 -0
- ostium_python_sdk-2.0.0/tests/test_get_trade_rollover_fee.py +25 -0
- ostium_python_sdk-2.0.0/tests/test_remove_collateral_from_leverage.py +44 -0
- ostium_python_sdk-2.0.0/tests/test_remove_collateral_with_collateral.py +44 -0
- ostium_python_sdk-2.0.0/tests/test_top_up_with_collateral.py +52 -0
- ostium_python_sdk-2.0.0/tests/test_top_up_with_leverage.py +45 -0
- ostium_python_sdk-2.0.0/tests/test_trade_get_sl_price.py +47 -0
- ostium_python_sdk-2.0.0/tests/test_trade_get_tp_price.py +67 -0
- ostium_python_sdk-2.0.0/tests/test_trade_liquidation_price.py +88 -0
- ostium_python_sdk-0.2.107/ostium_python_sdk/formulae.py +0 -449
- ostium_python_sdk-0.2.107/ostium_python_sdk/formulae_wrapper.py +0 -316
- ostium_python_sdk-0.2.107/tests/test_trade_get_tp_price.py +0 -39
- ostium_python_sdk-0.2.107/tests/test_trade_liquidation_price.py +0 -32
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/MANIFEST.in +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/__init__.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/abi/__init__.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/abi/abi.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/abi/faucet_abi.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/balance.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/config.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/exceptions.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/faucet.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/ostium.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/price.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/scscript/__init__.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk/utils.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk.egg-info/dependency_links.txt +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk.egg-info/requires.txt +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/ostium_python_sdk.egg-info/top_level.txt +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/pyproject.toml +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/requirements-dev.txt +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/requirements.txt +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/setup.cfg +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/tests/__init__.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/tests/test_get_price_impact.py +0 -0
- {ostium_python_sdk-0.2.107 → ostium_python_sdk-2.0.0}/tests/test_slippage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ostium-python-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: A python based SDK developed for interacting with Ostium, a leveraged trading application for trading currencies, commodities, indices, crypto and more.
|
|
5
5
|
Home-page: https://github.com/0xOstium/ostium-python-sdk
|
|
6
6
|
Author: ami@ostium.io
|
|
@@ -86,8 +86,38 @@ First, install the package with development dependencies:
|
|
|
86
86
|
pip install -e ".[dev]"
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
## Running tests
|
|
90
|
+
|
|
91
|
+
### Run specific tests
|
|
90
92
|
|
|
93
|
+
```bash
|
|
94
|
+
pytest -v tests/test_trade_liquidation_price.py
|
|
95
|
+
pytest -v tests/test_funding.py
|
|
96
|
+
pytest -v tests/test_trade_get_tp_price.py
|
|
97
|
+
pytest -v tests/test_trade_get_sl_price.py
|
|
98
|
+
pytest -v tests/test_current_trade_profit_p.py
|
|
99
|
+
pytest -v tests/test_top_up_with_collateral.py
|
|
100
|
+
pytest -v tests/test_top_up_with_leverage.py
|
|
101
|
+
pytest -v tests/test_remove_collateral_with_collateral.py
|
|
102
|
+
pytest -v tests/test_remove_collateral_from_leverage.py
|
|
103
|
+
pytest -v tests/test_current_total_profit_p.py
|
|
104
|
+
pytest -v tests/test_current_trade_profit_raw.py
|
|
105
|
+
pytest -v tests/test_current_total_profit_raw.py
|
|
106
|
+
pytest -v tests/test_get_trade_funding_fee.py
|
|
107
|
+
pytest -v tests/test_get_trade_rollover_fee.py
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Run ALL tests
|
|
91
121
|
```bash
|
|
92
122
|
pytest
|
|
93
123
|
```
|
|
@@ -514,6 +544,11 @@ All notable changes to the Ostium Python SDK will be documented in this file.
|
|
|
514
544
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
515
545
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
516
546
|
|
|
547
|
+
|
|
548
|
+
## [2.0.0] - 2025-03-04
|
|
549
|
+
|
|
550
|
+
Version 2.0
|
|
551
|
+
|
|
517
552
|
## [0.2.1] - 2025-02-21
|
|
518
553
|
|
|
519
554
|
### Added
|
|
@@ -47,8 +47,38 @@ First, install the package with development dependencies:
|
|
|
47
47
|
pip install -e ".[dev]"
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
## Running tests
|
|
51
51
|
|
|
52
|
+
### Run specific tests
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pytest -v tests/test_trade_liquidation_price.py
|
|
56
|
+
pytest -v tests/test_funding.py
|
|
57
|
+
pytest -v tests/test_trade_get_tp_price.py
|
|
58
|
+
pytest -v tests/test_trade_get_sl_price.py
|
|
59
|
+
pytest -v tests/test_current_trade_profit_p.py
|
|
60
|
+
pytest -v tests/test_top_up_with_collateral.py
|
|
61
|
+
pytest -v tests/test_top_up_with_leverage.py
|
|
62
|
+
pytest -v tests/test_remove_collateral_with_collateral.py
|
|
63
|
+
pytest -v tests/test_remove_collateral_from_leverage.py
|
|
64
|
+
pytest -v tests/test_current_total_profit_p.py
|
|
65
|
+
pytest -v tests/test_current_trade_profit_raw.py
|
|
66
|
+
pytest -v tests/test_current_total_profit_raw.py
|
|
67
|
+
pytest -v tests/test_get_trade_funding_fee.py
|
|
68
|
+
pytest -v tests/test_get_trade_rollover_fee.py
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Run ALL tests
|
|
52
82
|
```bash
|
|
53
83
|
pytest
|
|
54
84
|
```
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
from decimal import Decimal, getcontext, ROUND_DOWN
|
|
2
|
+
from .constants import MAX_PROFIT_P, MIN_LOSS_P, MAX_STOP_LOSS_P, PRECISION_16, PRECISION_2, PRECISION_6, PRECISION_12, PRECISION_18, LIQ_THRESHOLD_P
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from .scscript.funding import getPendingAccFundingFees, getTargetFundingRate
|
|
5
|
+
|
|
6
|
+
quantization_6 = Decimal('0.000001')
|
|
7
|
+
quantization_18 = Decimal('0.000000000000000001')
|
|
8
|
+
|
|
9
|
+
# v2 (formulae v1.3.3)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def GetTakeProfitPrice(open_price: Decimal, profit_p: Decimal, leverage: Decimal, is_long: bool) -> Decimal:
|
|
13
|
+
open_price = Decimal(open_price)
|
|
14
|
+
profit_p = Decimal(profit_p)
|
|
15
|
+
leverage = Decimal(leverage)
|
|
16
|
+
|
|
17
|
+
price_diff = (open_price * profit_p) / (leverage * Decimal('100'))
|
|
18
|
+
|
|
19
|
+
if (is_long):
|
|
20
|
+
tp_price = open_price + price_diff
|
|
21
|
+
else:
|
|
22
|
+
tp_price = open_price - price_diff
|
|
23
|
+
|
|
24
|
+
return Decimal(tp_price if tp_price > 0 else '0')
|
|
25
|
+
|
|
26
|
+
# v2 (formulae v1.3.3)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def GetStopLossPrice(open_price: Decimal, loss_p: Decimal, leverage: Decimal, is_long: bool) -> Decimal:
|
|
30
|
+
open_price = Decimal(open_price)
|
|
31
|
+
loss_p = Decimal(loss_p)
|
|
32
|
+
leverage = Decimal(leverage)
|
|
33
|
+
|
|
34
|
+
# price_diff matches your existing TP logic style, except using 'loss_p'
|
|
35
|
+
price_diff = (open_price * loss_p) / (leverage * Decimal('100'))
|
|
36
|
+
|
|
37
|
+
sl_price = open_price - price_diff if is_long else open_price + price_diff
|
|
38
|
+
return sl_price if sl_price > 0 else Decimal('0')
|
|
39
|
+
|
|
40
|
+
# v2 (formulae v1.3.3)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def CurrentTradeProfitP(
|
|
44
|
+
open_price: Decimal,
|
|
45
|
+
current_price: Decimal,
|
|
46
|
+
long: bool,
|
|
47
|
+
leverage: Decimal,
|
|
48
|
+
highest_leverage: Decimal
|
|
49
|
+
) -> Decimal:
|
|
50
|
+
leverage_to_use = leverage if leverage > highest_leverage else highest_leverage
|
|
51
|
+
if long:
|
|
52
|
+
price_diff = current_price - open_price
|
|
53
|
+
else:
|
|
54
|
+
price_diff = open_price - current_price
|
|
55
|
+
|
|
56
|
+
profit_p = (price_diff / open_price) * leverage_to_use * Decimal("100")
|
|
57
|
+
|
|
58
|
+
if profit_p >= MAX_PROFIT_P:
|
|
59
|
+
profit_p = MAX_PROFIT_P
|
|
60
|
+
|
|
61
|
+
profit_p *= (leverage / leverage_to_use)
|
|
62
|
+
|
|
63
|
+
return profit_p
|
|
64
|
+
|
|
65
|
+
# v2 (formulae v1.3.3)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def TopUpWithCollateral(
|
|
69
|
+
leverage: Decimal,
|
|
70
|
+
collateral: Decimal,
|
|
71
|
+
added_collateral: Decimal
|
|
72
|
+
) -> Decimal:
|
|
73
|
+
new_leverage = (collateral * leverage) / (collateral + added_collateral)
|
|
74
|
+
return new_leverage
|
|
75
|
+
|
|
76
|
+
# v2 (formulae v1.3.3)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def TopUpWithLeverage(
|
|
80
|
+
leverage: Decimal,
|
|
81
|
+
desired_leverage: Decimal,
|
|
82
|
+
collateral: Decimal
|
|
83
|
+
) -> Decimal:
|
|
84
|
+
added_c = (collateral * leverage) / desired_leverage - collateral
|
|
85
|
+
return added_c
|
|
86
|
+
|
|
87
|
+
# v2 (formulae v1.3.3)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def RemoveCollateralWithCollateral(
|
|
91
|
+
leverage: Decimal,
|
|
92
|
+
collateral: Decimal,
|
|
93
|
+
removed_collateral: Decimal
|
|
94
|
+
) -> Decimal:
|
|
95
|
+
new_leverage = (collateral * leverage) / (collateral - removed_collateral)
|
|
96
|
+
return new_leverage
|
|
97
|
+
|
|
98
|
+
# v2 (formulae v1.3.3)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def RemoveCollateralFromLeverage(
|
|
102
|
+
leverage: Decimal,
|
|
103
|
+
desired_leverage: Decimal,
|
|
104
|
+
collateral: Decimal
|
|
105
|
+
) -> Decimal:
|
|
106
|
+
added_c = collateral - (collateral * leverage / desired_leverage)
|
|
107
|
+
return added_c
|
|
108
|
+
|
|
109
|
+
# tbd - used by SDK
|
|
110
|
+
# v2 (formulae v1.3.3)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def GetTradeLiquidationPrice(
|
|
114
|
+
open_price: Decimal,
|
|
115
|
+
long: bool,
|
|
116
|
+
collateral: Decimal,
|
|
117
|
+
leverage: Decimal,
|
|
118
|
+
rollover_fee: Decimal,
|
|
119
|
+
funding_fee: Decimal
|
|
120
|
+
) -> Decimal:
|
|
121
|
+
print(
|
|
122
|
+
f"***** GetTradeLiquidationPrice: open_price: {open_price}, long: {long}, collateral: {collateral}, leverage: {leverage}, rollover_fee: {rollover_fee}, funding_fee: {funding_fee}")
|
|
123
|
+
|
|
124
|
+
liq_price_distance = (
|
|
125
|
+
open_price *
|
|
126
|
+
(collateral * (LIQ_THRESHOLD_P) / Decimal(100) - rollover_fee - funding_fee) /
|
|
127
|
+
collateral /
|
|
128
|
+
leverage
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
liq_price = open_price - liq_price_distance if long else open_price + liq_price_distance
|
|
132
|
+
liq_price = liq_price if liq_price > 0 else 0
|
|
133
|
+
return liq_price
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ???
|
|
137
|
+
def GetCurrentRolloverFee(
|
|
138
|
+
acc_rollover: str,
|
|
139
|
+
last_rollover_block: str,
|
|
140
|
+
rollover_fee_per_block: str,
|
|
141
|
+
latest_block: str
|
|
142
|
+
) -> Decimal:
|
|
143
|
+
try:
|
|
144
|
+
acc_rollover = Decimal(acc_rollover)
|
|
145
|
+
last_rollover_block = Decimal(last_rollover_block)
|
|
146
|
+
rollover_fee_per_block = Decimal(rollover_fee_per_block)
|
|
147
|
+
latest_block = Decimal(latest_block)
|
|
148
|
+
current_fee = acc_rollover + \
|
|
149
|
+
(latest_block - last_rollover_block) * rollover_fee_per_block
|
|
150
|
+
return current_fee
|
|
151
|
+
|
|
152
|
+
except Exception as error:
|
|
153
|
+
raise Exception(f"Unable to compute Current Rollover Fee: {error}")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# v2 (formulae v1.3.3)
|
|
157
|
+
def GetTradeRolloverFee(
|
|
158
|
+
trade_rollover: Decimal,
|
|
159
|
+
current_rollover: Decimal,
|
|
160
|
+
collateral: Decimal,
|
|
161
|
+
leverage: Decimal
|
|
162
|
+
) -> Decimal:
|
|
163
|
+
rollover_fee = (current_rollover - trade_rollover) * collateral * leverage
|
|
164
|
+
return rollover_fee
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# Gets the funding fee (abs) for an open trade (up to this block, aka based on current_funding up till this block)
|
|
168
|
+
# v2 (formulae v1.3.3)
|
|
169
|
+
def GetTradeFundingFee(
|
|
170
|
+
trade_funding: Decimal,
|
|
171
|
+
current_funding: Decimal,
|
|
172
|
+
collateral: Decimal,
|
|
173
|
+
leverage: Decimal
|
|
174
|
+
) -> Decimal:
|
|
175
|
+
print(
|
|
176
|
+
f"======> GetTradeFundingFee: trade_funding: {trade_funding}, current_funding: {current_funding}, collateral: {collateral}, leverage: {leverage}")
|
|
177
|
+
funding_fee = (current_funding - trade_funding) * collateral * leverage
|
|
178
|
+
return funding_fee
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def GetPriceImpact(
|
|
182
|
+
mid_price: str,
|
|
183
|
+
bid_price: str,
|
|
184
|
+
ask_price: str,
|
|
185
|
+
is_open: bool,
|
|
186
|
+
is_long: bool,
|
|
187
|
+
) -> dict:
|
|
188
|
+
try:
|
|
189
|
+
mid_price = Decimal(mid_price)
|
|
190
|
+
bid_price = Decimal(bid_price)
|
|
191
|
+
ask_price = Decimal(ask_price)
|
|
192
|
+
|
|
193
|
+
if (mid_price == 0):
|
|
194
|
+
return {
|
|
195
|
+
'priceImpactP': str(0),
|
|
196
|
+
'priceAfterImpact': str(0)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
above_spot = is_open == is_long
|
|
200
|
+
used_price = ask_price if above_spot else bid_price
|
|
201
|
+
priceImpactP = 100 * (abs(mid_price - used_price) / mid_price)
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
'priceImpactP': str(priceImpactP),
|
|
205
|
+
'priceAfterImpact': str(used_price)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
except Exception as error:
|
|
209
|
+
raise Exception(f"Unable to compute Price Impact: {error}")
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# calculates the gross (without fees) profit (abs) of an open trade
|
|
213
|
+
|
|
214
|
+
# calculates the net profit (after fees) of an open trade (abs)
|
|
215
|
+
# v2 (formulae v1.3.3)
|
|
216
|
+
def CurrentTradeProfitRaw(
|
|
217
|
+
open_price: Decimal,
|
|
218
|
+
current_price: Decimal,
|
|
219
|
+
long: bool,
|
|
220
|
+
leverage: Decimal,
|
|
221
|
+
highest_leverage: Decimal,
|
|
222
|
+
collateral: Decimal
|
|
223
|
+
) -> Decimal:
|
|
224
|
+
profit_p = CurrentTradeProfitP(
|
|
225
|
+
open_price,
|
|
226
|
+
current_price,
|
|
227
|
+
long,
|
|
228
|
+
leverage,
|
|
229
|
+
highest_leverage
|
|
230
|
+
)
|
|
231
|
+
profit = (collateral * profit_p) / Decimal("100")
|
|
232
|
+
return profit
|
|
233
|
+
|
|
234
|
+
# v2 (formulae v1.3.3)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def CurrentTotalProfitRaw(
|
|
238
|
+
open_price: Decimal,
|
|
239
|
+
current_price: Decimal,
|
|
240
|
+
long: bool,
|
|
241
|
+
leverage: Decimal,
|
|
242
|
+
highest_leverage: Decimal,
|
|
243
|
+
collateral: Decimal,
|
|
244
|
+
rollover_fee: Decimal,
|
|
245
|
+
funding_fee: Decimal
|
|
246
|
+
) -> Decimal:
|
|
247
|
+
# Get trade profit
|
|
248
|
+
trade_profit = CurrentTradeProfitRaw(
|
|
249
|
+
open_price,
|
|
250
|
+
current_price,
|
|
251
|
+
long,
|
|
252
|
+
leverage,
|
|
253
|
+
highest_leverage,
|
|
254
|
+
collateral
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Subtract fees
|
|
258
|
+
total_profit = trade_profit - \
|
|
259
|
+
rollover_fee - funding_fee
|
|
260
|
+
|
|
261
|
+
return total_profit
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
# v2 (formulae v1.3.3)
|
|
265
|
+
def CurrentTotalProfitP(total_profit: Decimal, collateral: Decimal) -> Decimal:
|
|
266
|
+
profit_p = (total_profit * Decimal("100")) / collateral
|
|
267
|
+
if profit_p <= MIN_LOSS_P:
|
|
268
|
+
profit_p = MIN_LOSS_P
|
|
269
|
+
return profit_p
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def GetFundingRate(
|
|
273
|
+
accPerOiLong: str,
|
|
274
|
+
accPerOiShort: str,
|
|
275
|
+
lastFundingRate: str,
|
|
276
|
+
maxFundingFeePerBlock: str,
|
|
277
|
+
lastUpdateBlock: str,
|
|
278
|
+
latestBlock: str,
|
|
279
|
+
oiLong: str,
|
|
280
|
+
oiShort: str,
|
|
281
|
+
oiCap: str,
|
|
282
|
+
hillInflectionPoint: str,
|
|
283
|
+
hillPosScale: str,
|
|
284
|
+
hillNegScale: str,
|
|
285
|
+
springFactor: str,
|
|
286
|
+
sFactorUpScaleP: str,
|
|
287
|
+
sFactorDownScaleP: str,
|
|
288
|
+
verbose: bool = False
|
|
289
|
+
):
|
|
290
|
+
acc_funding_long, acc_funding_short, latest_funding_rate, target_fr = getPendingAccFundingFees(
|
|
291
|
+
blockNumber=Decimal(latestBlock),
|
|
292
|
+
lastUpdateBlock=Decimal(lastUpdateBlock),
|
|
293
|
+
valueLong=Decimal(accPerOiLong) / PRECISION_18,
|
|
294
|
+
valueShort=Decimal(accPerOiShort) / PRECISION_18,
|
|
295
|
+
openInterestUsdcLong=Decimal(oiLong) / PRECISION_6,
|
|
296
|
+
openInterestUsdcShort=Decimal(oiShort) / PRECISION_6,
|
|
297
|
+
OiCap=Decimal(oiCap) / PRECISION_6,
|
|
298
|
+
maxFundingFeePerBlock=Decimal(maxFundingFeePerBlock) / PRECISION_18,
|
|
299
|
+
lastFundingRate=Decimal(lastFundingRate) / PRECISION_18,
|
|
300
|
+
hillInflectionPoint=Decimal(hillInflectionPoint) / PRECISION_18,
|
|
301
|
+
hillPosScale=Decimal(hillPosScale) / PRECISION_2,
|
|
302
|
+
hillNegScale=Decimal(hillNegScale) / PRECISION_2,
|
|
303
|
+
springFactor=Decimal(springFactor) / PRECISION_18,
|
|
304
|
+
sFactorUpScale=Decimal(sFactorUpScaleP) / PRECISION_2,
|
|
305
|
+
sFactorDownScaleP=Decimal(sFactorDownScaleP) / PRECISION_2
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
'accFundingLong': acc_funding_long,
|
|
310
|
+
'accFundingShort': acc_funding_short,
|
|
311
|
+
'latestFundingRate': latest_funding_rate,
|
|
312
|
+
'targetFundingRate': target_fr
|
|
313
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
from decimal import Decimal
|
|
2
|
+
from .formulae import (PRECISION_18, PRECISION_2, PRECISION_6, GetCurrentRolloverFee, GetFundingRate,
|
|
3
|
+
GetTradeFundingFee, GetTradeLiquidationPrice, GetTradeRolloverFee,
|
|
4
|
+
GetPriceImpact, CurrentTradeProfitRaw,
|
|
5
|
+
CurrentTotalProfitRaw, CurrentTotalProfitP)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# TBD - used by SDK
|
|
9
|
+
# returns the funding_fee_long_per_block, funding_fee_short_per_block
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_funding_fee_long_short(pair_info, block_number, verbose=False):
|
|
13
|
+
funding_rate_raw = GetFundingRate(
|
|
14
|
+
pair_info['accFundingLong'],
|
|
15
|
+
pair_info['accFundingShort'],
|
|
16
|
+
pair_info['lastFundingRate'],
|
|
17
|
+
pair_info['maxFundingFeePerBlock'],
|
|
18
|
+
pair_info['lastFundingBlock'],
|
|
19
|
+
str(block_number),
|
|
20
|
+
pair_info['longOI'],
|
|
21
|
+
pair_info['shortOI'],
|
|
22
|
+
pair_info['maxOI'],
|
|
23
|
+
pair_info['hillInflectionPoint'],
|
|
24
|
+
pair_info['hillPosScale'],
|
|
25
|
+
pair_info['hillNegScale'],
|
|
26
|
+
pair_info['springFactor'],
|
|
27
|
+
pair_info['sFactorUpScaleP'],
|
|
28
|
+
pair_info['sFactorDownScaleP'],
|
|
29
|
+
verbose
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Convert latest funding rate to decimal
|
|
33
|
+
latest_rate = Decimal(
|
|
34
|
+
funding_rate_raw['latestFundingRate']) / PRECISION_18 # Fixed key name
|
|
35
|
+
|
|
36
|
+
# Convert OI values to decimal
|
|
37
|
+
long_oi = Decimal(pair_info['longOI']) / PRECISION_18
|
|
38
|
+
short_oi = Decimal(pair_info['shortOI']) / PRECISION_18
|
|
39
|
+
|
|
40
|
+
if funding_rate_raw['longsPay']:
|
|
41
|
+
# If longs pay, they get negative rate
|
|
42
|
+
long_rate = -latest_rate
|
|
43
|
+
# Shorts receive proportional to OI ratio
|
|
44
|
+
short_rate = latest_rate * long_oi / \
|
|
45
|
+
short_oi if short_oi > 0 else Decimal('0')
|
|
46
|
+
else:
|
|
47
|
+
# If shorts pay, they get negative rate
|
|
48
|
+
short_rate = -latest_rate
|
|
49
|
+
# Longs receive proportional to OI ratio
|
|
50
|
+
long_rate = latest_rate * short_oi / \
|
|
51
|
+
long_oi if long_oi > 0 else Decimal('0')
|
|
52
|
+
|
|
53
|
+
return float(long_rate), float(short_rate)
|
|
54
|
+
|
|
55
|
+
# TBD - used by SDK
|
|
56
|
+
# Gets an open trade metrics: such as the open pnl, rollover, funding, liquidation price, price impact, etc.
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def get_trade_metrics(trade_details, price_data, block_number, verbose=False):
|
|
60
|
+
"""
|
|
61
|
+
Calculate PNL and related metrics for a trade.
|
|
62
|
+
"""
|
|
63
|
+
if not trade_details or not price_data or not block_number:
|
|
64
|
+
return {
|
|
65
|
+
'pnl': 0,
|
|
66
|
+
'pnl_percent': 0,
|
|
67
|
+
'rollover': 0,
|
|
68
|
+
'funding': 0,
|
|
69
|
+
'total_profit': 0,
|
|
70
|
+
'net_pnl': 0,
|
|
71
|
+
'net_value': 0,
|
|
72
|
+
'liquidation_price': 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
pair_info = trade_details['pair']
|
|
76
|
+
# Calculate current rollover fee
|
|
77
|
+
current_rollover_raw = GetCurrentRolloverFee(
|
|
78
|
+
pair_info['accRollover'],
|
|
79
|
+
pair_info['lastRolloverBlock'],
|
|
80
|
+
pair_info['rolloverFeePerBlock'],
|
|
81
|
+
str(block_number)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
if verbose:
|
|
85
|
+
print(f"Current rollover fee: {current_rollover_raw}")
|
|
86
|
+
|
|
87
|
+
trade_rollover_fee = GetTradeRolloverFee(
|
|
88
|
+
Decimal(trade_details['rollover']) / PRECISION_18,
|
|
89
|
+
Decimal(current_rollover_raw) / PRECISION_18,
|
|
90
|
+
Decimal(trade_details['collateral']) / PRECISION_6,
|
|
91
|
+
Decimal(trade_details['leverage']) / PRECISION_2
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if verbose:
|
|
95
|
+
print(f"Trade Rollover fee: {trade_rollover_fee}")
|
|
96
|
+
if (trade_rollover_fee != 0):
|
|
97
|
+
print(f"***** Trade Rollover fee is not 0: {trade_rollover_fee}")
|
|
98
|
+
# Get funding rate
|
|
99
|
+
funding_rate_raw = GetFundingRate(
|
|
100
|
+
pair_info['accFundingLong'],
|
|
101
|
+
pair_info['accFundingShort'],
|
|
102
|
+
pair_info['lastFundingRate'],
|
|
103
|
+
pair_info['maxFundingFeePerBlock'],
|
|
104
|
+
pair_info['lastFundingBlock'],
|
|
105
|
+
str(block_number),
|
|
106
|
+
pair_info['longOI'],
|
|
107
|
+
pair_info['shortOI'],
|
|
108
|
+
pair_info['maxOI'],
|
|
109
|
+
pair_info['hillInflectionPoint'],
|
|
110
|
+
pair_info['hillPosScale'],
|
|
111
|
+
pair_info['hillNegScale'],
|
|
112
|
+
pair_info['springFactor'],
|
|
113
|
+
pair_info['sFactorUpScaleP'],
|
|
114
|
+
pair_info['sFactorDownScaleP'],
|
|
115
|
+
verbose
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if verbose:
|
|
119
|
+
print(f"Funding rate: {funding_rate_raw}")
|
|
120
|
+
|
|
121
|
+
# Calculate funding fee
|
|
122
|
+
trade_funding_fee = GetTradeFundingFee(
|
|
123
|
+
Decimal(trade_details['funding']) / PRECISION_18,
|
|
124
|
+
Decimal(funding_rate_raw['accFundingLong']) if trade_details['isBuy'] else Decimal(
|
|
125
|
+
funding_rate_raw['accFundingShort']),
|
|
126
|
+
Decimal(trade_details['collateral']) / PRECISION_6,
|
|
127
|
+
Decimal(trade_details['leverage']) / PRECISION_2
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
if verbose:
|
|
131
|
+
print(f"trade_funding_fee: {trade_funding_fee}")
|
|
132
|
+
|
|
133
|
+
# Calculate liquidation price
|
|
134
|
+
trade_liquidation_price = GetTradeLiquidationPrice(
|
|
135
|
+
Decimal(trade_details['openPrice']) / PRECISION_18,
|
|
136
|
+
trade_details['isBuy'],
|
|
137
|
+
Decimal(trade_details['collateral']) / PRECISION_6,
|
|
138
|
+
Decimal(trade_details['leverage']) / PRECISION_2,
|
|
139
|
+
Decimal(trade_rollover_fee),
|
|
140
|
+
Decimal(trade_funding_fee)
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
if verbose:
|
|
144
|
+
print(f"trade_liquidation_price: {trade_liquidation_price}")
|
|
145
|
+
|
|
146
|
+
# Calculate price impact
|
|
147
|
+
is_open = False # Get the price assuming a close
|
|
148
|
+
|
|
149
|
+
price_impact_raw = GetPriceImpact(
|
|
150
|
+
str(int(Decimal(str(price_data['mid'])) * PRECISION_18)),
|
|
151
|
+
str(int(Decimal(str(price_data['bid'])) * PRECISION_18)),
|
|
152
|
+
str(int(Decimal(str(price_data['ask'])) * PRECISION_18)),
|
|
153
|
+
is_open,
|
|
154
|
+
trade_details['isBuy']
|
|
155
|
+
)
|
|
156
|
+
price_after_impact = price_impact_raw['priceAfterImpact']
|
|
157
|
+
|
|
158
|
+
# Calculate PNL (abs)
|
|
159
|
+
pnl_raw = CurrentTradeProfitRaw(
|
|
160
|
+
Decimal(trade_details['openPrice']) / PRECISION_18,
|
|
161
|
+
Decimal(price_after_impact) / PRECISION_18,
|
|
162
|
+
Decimal(trade_details['isBuy']),
|
|
163
|
+
Decimal(trade_details['leverage']) / PRECISION_2,
|
|
164
|
+
Decimal(trade_details['highestLeverage']) / PRECISION_2,
|
|
165
|
+
Decimal(trade_details['collateral']) / PRECISION_6
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Calculate total profit (abs)
|
|
169
|
+
total_profit_raw = CurrentTotalProfitRaw(
|
|
170
|
+
Decimal(trade_details['openPrice']) / PRECISION_18,
|
|
171
|
+
Decimal(price_after_impact) / PRECISION_18,
|
|
172
|
+
Decimal(trade_details['isBuy']),
|
|
173
|
+
Decimal(trade_details['leverage']) / PRECISION_2,
|
|
174
|
+
Decimal(trade_details['highestLeverage']) / PRECISION_2,
|
|
175
|
+
Decimal(trade_details['collateral']) / PRECISION_6,
|
|
176
|
+
Decimal(trade_rollover_fee),
|
|
177
|
+
Decimal(trade_funding_fee)
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Calculate PNL percentage
|
|
181
|
+
pnl_percent_raw = CurrentTotalProfitP(
|
|
182
|
+
Decimal(total_profit_raw), Decimal(trade_details['collateral']) / PRECISION_6)
|
|
183
|
+
|
|
184
|
+
# Convert values to proper decimals
|
|
185
|
+
pnl = Decimal(pnl_raw)
|
|
186
|
+
pnl_percent = Decimal(pnl_percent_raw)
|
|
187
|
+
net_pnl = Decimal(total_profit_raw)
|
|
188
|
+
total_profit = Decimal(total_profit_raw)
|
|
189
|
+
funding = Decimal(trade_funding_fee)
|
|
190
|
+
rollover = Decimal(trade_rollover_fee)
|
|
191
|
+
net_value = net_pnl + (Decimal(trade_details['collateral']) / PRECISION_6)
|
|
192
|
+
price_impact = Decimal(price_after_impact) / PRECISION_18
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
'pnl': float(pnl),
|
|
196
|
+
'pnl_percent': float(pnl_percent),
|
|
197
|
+
'rollover': float(rollover),
|
|
198
|
+
'funding': float(funding),
|
|
199
|
+
'total_profit': float(total_profit), # same as net_pnl
|
|
200
|
+
'net_pnl': float(net_pnl), # same as total_profit
|
|
201
|
+
'net_value': float(net_value),
|
|
202
|
+
'liquidation_price': float(trade_liquidation_price),
|
|
203
|
+
'price_impact': float(price_impact)
|
|
204
|
+
}
|
|
@@ -30,7 +30,9 @@ def getTargetFundingRate(normalizedOiDelta, hillInflectionPoint, maxFundingFeePe
|
|
|
30
30
|
|
|
31
31
|
return (maxFundingFeePerBlock * targetFr).quantize(quantization_18, rounding=ROUND_DOWN)
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
#
|
|
34
|
+
# returns the accFundingLong, accFundingShort, latestFundingRate (per block), targetFr (per block)
|
|
35
|
+
#
|
|
34
36
|
def getPendingAccFundingFees(
|
|
35
37
|
blockNumber: Decimal,
|
|
36
38
|
lastUpdateBlock: Decimal,
|
|
@@ -48,7 +50,7 @@ def getPendingAccFundingFees(
|
|
|
48
50
|
sFactorUpScale: Decimal,
|
|
49
51
|
sFactorDownScaleP: Decimal,
|
|
50
52
|
):
|
|
51
|
-
|
|
53
|
+
# print(f"********* getPendingAccFundingFees *********")
|
|
52
54
|
numBlocks = blockNumber - lastUpdateBlock
|
|
53
55
|
openInterestMax = max(openInterestUsdcLong, openInterestUsdcShort)
|
|
54
56
|
normalizedOiDelta = ((openInterestUsdcLong - openInterestUsdcShort).quantize(quantization_6, rounding=ROUND_DOWN) / max(OiCap, openInterestMax).quantize(quantization_6, rounding=ROUND_DOWN)).quantize(quantization_6, rounding=ROUND_DOWN)
|