ostium-python-sdk 0.2.1__tar.gz → 0.2.101__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.1 → ostium_python_sdk-0.2.101}/PKG-INFO +12 -3
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/README.md +11 -2
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/config.py +3 -1
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/constants.py +1 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/formulae.py +191 -28
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/formulae_wrapper.py +35 -14
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/ostium.py +0 -2
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/sdk.py +49 -2
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/subgraph.py +13 -2
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/utils.py +0 -3
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/PKG-INFO +12 -3
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/SOURCES.txt +1 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/setup.py +1 -1
- ostium_python_sdk-0.2.101/tests/test_get_price_impact.py +60 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/MANIFEST.in +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/__init__.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/abi/__init__.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/abi/abi.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/abi/faucet_abi.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/balance.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/exceptions.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/faucet.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/price.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/dependency_links.txt +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/requires.txt +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/top_level.txt +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/pyproject.toml +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/requirements-dev.txt +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/requirements.txt +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/setup.cfg +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/tests/__init__.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/tests/test_slippage.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/tests/test_trade_get_tp_price.py +0 -0
- {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/tests/test_trade_liquidation_price.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ostium-python-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.101
|
|
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
|
|
@@ -424,8 +424,17 @@ TBD
|
|
|
424
424
|
TBD
|
|
425
425
|
|
|
426
426
|
|
|
427
|
-
###
|
|
428
|
-
|
|
427
|
+
### Get a certain Pair Net Rate % (Funding Fees, Rollover)
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
### Use sdk.get_pair_net_rate_percent_per_hours()
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Open Trade Fees calculation - SDK to get breakdown of FF, RF, PnL, etc.
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
## Use sdk.get_open_trade_metrics()
|
|
437
|
+
```
|
|
429
438
|
|
|
430
439
|
|
|
431
440
|
## Example Usage Scripts
|
|
@@ -385,8 +385,17 @@ TBD
|
|
|
385
385
|
TBD
|
|
386
386
|
|
|
387
387
|
|
|
388
|
-
###
|
|
389
|
-
|
|
388
|
+
### Get a certain Pair Net Rate % (Funding Fees, Rollover)
|
|
389
|
+
|
|
390
|
+
```bash
|
|
391
|
+
### Use sdk.get_pair_net_rate_percent_per_hours()
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Open Trade Fees calculation - SDK to get breakdown of FF, RF, PnL, etc.
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
## Use sdk.get_open_trade_metrics()
|
|
398
|
+
```
|
|
390
399
|
|
|
391
400
|
|
|
392
401
|
## Example Usage Scripts
|
|
@@ -22,7 +22,9 @@ class NetworkConfig:
|
|
|
22
22
|
|
|
23
23
|
@classmethod
|
|
24
24
|
def testnet(cls) -> 'NetworkConfig':
|
|
25
|
-
return cls(
|
|
25
|
+
return cls( # tbd
|
|
26
|
+
|
|
27
|
+
|
|
26
28
|
graph_url="https://subgraph.satsuma-prod.com/391a61815d32/ostium/ost-sep-final/api",
|
|
27
29
|
contracts={
|
|
28
30
|
"usdc": "0xe73B11Fb1e3eeEe8AF2a23079A4410Fe1B370548",
|
|
@@ -10,6 +10,7 @@ PRECISION_2 = Decimal('100')
|
|
|
10
10
|
PRECISION_6 = Decimal('1000000')
|
|
11
11
|
PRECISION_9 = Decimal('1000000000')
|
|
12
12
|
PRECISION_12 = Decimal('1000000000000')
|
|
13
|
+
PRECISION_16 = Decimal('10000000000000000')
|
|
13
14
|
PRECISION_18 = Decimal('1000000000000000000')
|
|
14
15
|
|
|
15
16
|
LIQ_THRESHOLD_P = Decimal('90') # -90% (of collateral)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
|
-
from .constants import MAX_PROFIT_P, MAX_STOP_LOSS_P, PRECISION_2, PRECISION_6, PRECISION_12, PRECISION_18, LIQ_THRESHOLD_P
|
|
2
|
+
from .constants import MAX_PROFIT_P, MAX_STOP_LOSS_P, PRECISION_16, PRECISION_2, PRECISION_6, PRECISION_12, PRECISION_18, LIQ_THRESHOLD_P
|
|
3
|
+
from typing import Dict
|
|
3
4
|
|
|
4
5
|
#
|
|
5
6
|
# This is a copy-cat of formulae repo originally written in TypeScript
|
|
@@ -73,6 +74,8 @@ def CurrentTradeProfitP(open_price: str, current_price: str, long: bool, leverag
|
|
|
73
74
|
except Exception as e:
|
|
74
75
|
return str(e)
|
|
75
76
|
|
|
77
|
+
# tbd - used by SDK
|
|
78
|
+
|
|
76
79
|
|
|
77
80
|
def GetTradeLiquidationPrice(
|
|
78
81
|
open_price: str,
|
|
@@ -147,6 +150,8 @@ def GetTradeRolloverFee(
|
|
|
147
150
|
except Exception as error:
|
|
148
151
|
raise Exception(f"Unable to compute Trade Rollover Fee: {error}")
|
|
149
152
|
|
|
153
|
+
# Gets the funding fee (abs) for an open trade (up to this block, aka based on current_funding up till this block)
|
|
154
|
+
|
|
150
155
|
|
|
151
156
|
def GetTradeFundingFee(
|
|
152
157
|
trade_funding: str,
|
|
@@ -230,46 +235,34 @@ def GetPriceImpact(
|
|
|
230
235
|
mid_price: str,
|
|
231
236
|
bid_price: str,
|
|
232
237
|
ask_price: str,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
is_buy: bool,
|
|
236
|
-
is_close: bool,
|
|
237
|
-
trade_size: str,
|
|
238
|
-
trade_size_ref: str
|
|
238
|
+
is_open: bool,
|
|
239
|
+
is_long: bool, # aka is_long
|
|
239
240
|
) -> dict:
|
|
240
241
|
try:
|
|
241
242
|
mid_price = Decimal(mid_price)
|
|
242
243
|
bid_price = Decimal(bid_price)
|
|
243
244
|
ask_price = Decimal(ask_price)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
# Calculate price after impact
|
|
255
|
-
price_after_impact = mid_price
|
|
256
|
-
if not is_limit:
|
|
257
|
-
if is_buy:
|
|
258
|
-
price_after_impact = mid_price * \
|
|
259
|
-
(Decimal('1') + impact_spread / Decimal('2'))
|
|
260
|
-
else:
|
|
261
|
-
price_after_impact = mid_price * \
|
|
262
|
-
(Decimal('1') - impact_spread / Decimal('2'))
|
|
245
|
+
|
|
246
|
+
if (mid_price == 0):
|
|
247
|
+
return {
|
|
248
|
+
'priceImpactP': str(0),
|
|
249
|
+
'priceAfterImpact': str(0)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
above_spot = is_open == is_long
|
|
253
|
+
used_price = ask_price if above_spot else bid_price
|
|
254
|
+
priceImpactP = 100 * (abs(mid_price - used_price) / mid_price)
|
|
263
255
|
|
|
264
256
|
return {
|
|
265
|
-
'
|
|
266
|
-
'
|
|
257
|
+
'priceImpactP': str(priceImpactP),
|
|
258
|
+
'priceAfterImpact': str(used_price)
|
|
267
259
|
}
|
|
268
260
|
|
|
269
261
|
except Exception as error:
|
|
270
262
|
raise Exception(f"Unable to compute Price Impact: {error}")
|
|
271
263
|
|
|
272
264
|
|
|
265
|
+
# calculates the gross (without fees) profit (abs) of an open trade
|
|
273
266
|
def CurrentTradeProfitRaw(
|
|
274
267
|
open_price: str,
|
|
275
268
|
current_price: str,
|
|
@@ -298,6 +291,8 @@ def CurrentTradeProfitRaw(
|
|
|
298
291
|
except Exception as error:
|
|
299
292
|
raise Exception(f"Unable to compute Current Trade Profit Raw: {error}")
|
|
300
293
|
|
|
294
|
+
# calculates the net profit (after fees) of an open trade (abs)
|
|
295
|
+
|
|
301
296
|
|
|
302
297
|
def CurrentTotalProfitRaw(
|
|
303
298
|
open_price: str,
|
|
@@ -328,6 +323,8 @@ def CurrentTotalProfitRaw(
|
|
|
328
323
|
raise Exception(f"Unable to compute Current Total Profit Raw: {error}")
|
|
329
324
|
|
|
330
325
|
|
|
326
|
+
# TBD- used by sdk. calculates the net profit percentage of an open trade
|
|
327
|
+
# What's the diff between this and CurrentTradeProfitP?
|
|
331
328
|
def CurrentTotalProfitP(total_profit: str, collateral: str) -> str:
|
|
332
329
|
try:
|
|
333
330
|
total_profit = Decimal(total_profit)
|
|
@@ -347,3 +344,169 @@ def CurrentTotalProfitP(total_profit: str, collateral: str) -> str:
|
|
|
347
344
|
f"Unable to compute Current Total Profit Percentage: {error}")
|
|
348
345
|
|
|
349
346
|
# given desired TP percentage, like 35, 50, 75, 100, 500 and 900 which is max: gives you the TP price
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def get_target_funding_rate(
|
|
350
|
+
normalized_oi_delta: Decimal,
|
|
351
|
+
hill_inflection_point: Decimal,
|
|
352
|
+
max_fr: Decimal,
|
|
353
|
+
hill_pos_scale: Decimal,
|
|
354
|
+
hill_neg_scale: Decimal,
|
|
355
|
+
) -> Decimal:
|
|
356
|
+
a = Decimal('184')
|
|
357
|
+
k = Decimal('16')
|
|
358
|
+
|
|
359
|
+
x = a * normalized_oi_delta / PRECISION_2
|
|
360
|
+
x2 = x * x * PRECISION_6 # convert to PRECISION_18
|
|
361
|
+
hill = x2 * PRECISION_18 / (k * PRECISION_16 + x2)
|
|
362
|
+
|
|
363
|
+
if normalized_oi_delta >= 0:
|
|
364
|
+
target_fr = hill_pos_scale * hill / PRECISION_2 + hill_inflection_point
|
|
365
|
+
else:
|
|
366
|
+
target_fr = hill_neg_scale * \
|
|
367
|
+
Decimal('-1') * hill / PRECISION_2 + hill_inflection_point
|
|
368
|
+
|
|
369
|
+
if target_fr > PRECISION_18:
|
|
370
|
+
target_fr = PRECISION_18
|
|
371
|
+
elif target_fr < PRECISION_18 * Decimal('-1'):
|
|
372
|
+
target_fr = PRECISION_18 * Decimal('-1')
|
|
373
|
+
|
|
374
|
+
return target_fr * max_fr / PRECISION_18
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def exponential_approximation(x: Decimal) -> Decimal:
|
|
378
|
+
approx_threshold = Decimal('793231258909201900')
|
|
379
|
+
|
|
380
|
+
if abs(x) < approx_threshold:
|
|
381
|
+
three_with_precision = PRECISION_18 * 3
|
|
382
|
+
numerator = x + three_with_precision
|
|
383
|
+
numerator = numerator * numerator / PRECISION_18 + three_with_precision
|
|
384
|
+
denominator = x - three_with_precision
|
|
385
|
+
denominator = denominator * denominator / PRECISION_18 + three_with_precision
|
|
386
|
+
|
|
387
|
+
return numerator * PRECISION_18 / denominator
|
|
388
|
+
else:
|
|
389
|
+
k = [1648721, 1284025, 1133148, 1064494, 1031743,
|
|
390
|
+
1015748, 1007843, 1003915, 1001955, 1000977]
|
|
391
|
+
integer_part = abs(x) // PRECISION_18
|
|
392
|
+
decimal_part = abs(x) - (integer_part * PRECISION_18)
|
|
393
|
+
|
|
394
|
+
approx = PRECISION_6
|
|
395
|
+
|
|
396
|
+
for ki in k:
|
|
397
|
+
decimal_part = decimal_part * 2
|
|
398
|
+
if decimal_part >= PRECISION_18:
|
|
399
|
+
approx = approx * Decimal(str(ki)) / PRECISION_6
|
|
400
|
+
decimal_part = decimal_part - PRECISION_18
|
|
401
|
+
if decimal_part == 0:
|
|
402
|
+
break
|
|
403
|
+
|
|
404
|
+
return (PRECISION_18 * PRECISION_18 /
|
|
405
|
+
(Decimal('2') ** integer_part *
|
|
406
|
+
(approx / Decimal('1000') * Decimal('1e15'))) /
|
|
407
|
+
Decimal('1e15') * Decimal('1e15'))
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def get_funding_rate(
|
|
411
|
+
acc_per_oi_long: str,
|
|
412
|
+
acc_per_oi_short: str,
|
|
413
|
+
last_funding_rate: str,
|
|
414
|
+
max_funding_fee_per_block: str,
|
|
415
|
+
last_update_block: str,
|
|
416
|
+
latest_block: str,
|
|
417
|
+
oi_long: str,
|
|
418
|
+
oi_short: str,
|
|
419
|
+
oi_cap: str,
|
|
420
|
+
hill_inflection_point: str,
|
|
421
|
+
hill_pos_scale: str,
|
|
422
|
+
hill_neg_scale: str,
|
|
423
|
+
spring_factor: str,
|
|
424
|
+
s_factor_up_scale_p: str,
|
|
425
|
+
s_factor_down_scale_p: str,
|
|
426
|
+
verbose: bool = False
|
|
427
|
+
) -> Dict[str, str]:
|
|
428
|
+
# Convert string inputs to Decimal
|
|
429
|
+
acc_per_oi_long_dec = Decimal(acc_per_oi_long)
|
|
430
|
+
acc_per_oi_short_dec = Decimal(acc_per_oi_short)
|
|
431
|
+
last_funding_rate_dec = Decimal(last_funding_rate)
|
|
432
|
+
max_funding_fee_per_block_dec = Decimal(max_funding_fee_per_block)
|
|
433
|
+
last_update_block_dec = Decimal(last_update_block)
|
|
434
|
+
latest_block_dec = Decimal(latest_block)
|
|
435
|
+
oi_long_dec = Decimal(oi_long)
|
|
436
|
+
oi_short_dec = Decimal(oi_short)
|
|
437
|
+
oi_cap_dec = Decimal(oi_cap)
|
|
438
|
+
spring_factor_dec = Decimal(spring_factor)
|
|
439
|
+
s_factor_up_scale_p_dec = Decimal(s_factor_up_scale_p)
|
|
440
|
+
s_factor_down_scale_p_dec = Decimal(s_factor_down_scale_p)
|
|
441
|
+
hill_inflection_point_dec = Decimal(hill_inflection_point)
|
|
442
|
+
hill_pos_scale_dec = Decimal(hill_pos_scale)
|
|
443
|
+
hill_neg_scale_dec = Decimal(hill_neg_scale)
|
|
444
|
+
|
|
445
|
+
# Calculate open interest max
|
|
446
|
+
open_interest_max = max(oi_long_dec, oi_short_dec)
|
|
447
|
+
denominator = max(oi_cap_dec, open_interest_max)
|
|
448
|
+
oi_delta = (oi_long_dec - oi_short_dec) * PRECISION_6 / denominator
|
|
449
|
+
|
|
450
|
+
if verbose:
|
|
451
|
+
print(f"open_interest_max: {open_interest_max}")
|
|
452
|
+
print(f"denominator: {denominator}")
|
|
453
|
+
|
|
454
|
+
print(
|
|
455
|
+
f"oi_long_dec: {oi_long_dec} (make sure this is in notional aka usd)")
|
|
456
|
+
print(
|
|
457
|
+
f"oi_short_dec: {oi_short_dec} (make sure this is in notional aka usd)")
|
|
458
|
+
print(f"oi_cap_dec: {oi_cap_dec}")
|
|
459
|
+
print(f"oi_delta: {oi_delta}")
|
|
460
|
+
|
|
461
|
+
# Get target funding rate
|
|
462
|
+
target_fr = get_target_funding_rate(
|
|
463
|
+
oi_delta,
|
|
464
|
+
hill_inflection_point_dec,
|
|
465
|
+
max_funding_fee_per_block_dec,
|
|
466
|
+
hill_pos_scale_dec,
|
|
467
|
+
hill_neg_scale_dec,
|
|
468
|
+
)
|
|
469
|
+
|
|
470
|
+
if verbose:
|
|
471
|
+
print(f"target_fr: {target_fr}")
|
|
472
|
+
|
|
473
|
+
# Calculate spring factor
|
|
474
|
+
s_factor = Decimal('0')
|
|
475
|
+
if last_funding_rate_dec * target_fr >= 0:
|
|
476
|
+
if abs(target_fr) > abs(last_funding_rate_dec):
|
|
477
|
+
s_factor = spring_factor_dec
|
|
478
|
+
else:
|
|
479
|
+
s_factor = s_factor_down_scale_p_dec * \
|
|
480
|
+
spring_factor_dec / Decimal('10000')
|
|
481
|
+
else:
|
|
482
|
+
s_factor = s_factor_up_scale_p_dec * \
|
|
483
|
+
spring_factor_dec / Decimal('10000')
|
|
484
|
+
|
|
485
|
+
# Calculate blocks to charge and exponential
|
|
486
|
+
num_blocks_to_charge = latest_block_dec - last_update_block_dec
|
|
487
|
+
exp = exponential_approximation(
|
|
488
|
+
s_factor * num_blocks_to_charge * Decimal('-1'))
|
|
489
|
+
|
|
490
|
+
# Calculate funding rates
|
|
491
|
+
acc_funding_rate = (target_fr * num_blocks_to_charge +
|
|
492
|
+
(PRECISION_18 - exp) * (last_funding_rate_dec - target_fr) / s_factor)
|
|
493
|
+
fr = target_fr + (last_funding_rate_dec - target_fr) * exp / PRECISION_18
|
|
494
|
+
|
|
495
|
+
# Update accumulations
|
|
496
|
+
if acc_funding_rate > 0:
|
|
497
|
+
if oi_long_dec > 0:
|
|
498
|
+
acc_per_oi_long_dec += acc_funding_rate
|
|
499
|
+
acc_per_oi_short_dec -= (acc_funding_rate * oi_long_dec / oi_short_dec
|
|
500
|
+
if oi_short_dec > 0 else Decimal('0'))
|
|
501
|
+
else:
|
|
502
|
+
if oi_short_dec > 0:
|
|
503
|
+
acc_per_oi_short_dec -= acc_funding_rate
|
|
504
|
+
acc_per_oi_long_dec += (acc_funding_rate * oi_short_dec / oi_long_dec
|
|
505
|
+
if oi_long_dec > 0 else Decimal('0'))
|
|
506
|
+
|
|
507
|
+
return {
|
|
508
|
+
'accFundingLong': str(acc_per_oi_long_dec),
|
|
509
|
+
'accFundingShort': str(acc_per_oi_short_dec),
|
|
510
|
+
'latestFundingRate': str(fr),
|
|
511
|
+
'targetFr': str(target_fr)
|
|
512
|
+
}
|
|
@@ -5,6 +5,8 @@ from .formulae import (PRECISION_18, PRECISION_2, PRECISION_6, GetCurrentRollove
|
|
|
5
5
|
CurrentTotalProfitRaw, CurrentTotalProfitP)
|
|
6
6
|
from typing import Dict, Union
|
|
7
7
|
|
|
8
|
+
# TBD - Not used by SDK
|
|
9
|
+
|
|
8
10
|
|
|
9
11
|
def get_liq_price(trade_details, pair_info, block_number):
|
|
10
12
|
current_funding_fee = GetTradeFundingFee(trade_details['funding'], pair_info['accFundingLong'] if trade_details['isBuy']
|
|
@@ -21,6 +23,8 @@ def get_liq_price(trade_details, pair_info, block_number):
|
|
|
21
23
|
|
|
22
24
|
return liq_price / PRECISION_18
|
|
23
25
|
|
|
26
|
+
# TBD - used by SDK
|
|
27
|
+
|
|
24
28
|
|
|
25
29
|
def get_funding_fee_long_short(pair_info, block_number):
|
|
26
30
|
funding_rate_raw = GetFundingRate(
|
|
@@ -58,15 +62,18 @@ def get_funding_fee_long_short(pair_info, block_number):
|
|
|
58
62
|
|
|
59
63
|
return float(long_rate), float(short_rate)
|
|
60
64
|
|
|
65
|
+
# TBD - used by SDK
|
|
66
|
+
# Gets an open trade metrics: such as the open pnl, rollover, funding, liquidation price, price impact, etc.
|
|
67
|
+
|
|
61
68
|
|
|
62
|
-
def get_trade_metrics(trade_details, price_data, block_number):
|
|
69
|
+
def get_trade_metrics(trade_details, price_data, block_number, verbose=False):
|
|
63
70
|
"""
|
|
64
71
|
Calculate PNL and related metrics for a trade.
|
|
65
72
|
"""
|
|
66
73
|
if not trade_details or not price_data or not block_number:
|
|
67
74
|
return {
|
|
68
75
|
'pnl': 0,
|
|
69
|
-
'pnl_raw': '0',
|
|
76
|
+
# 'pnl_raw': '0',
|
|
70
77
|
'pnl_percent': 0,
|
|
71
78
|
'rollover': 0,
|
|
72
79
|
'funding': 0,
|
|
@@ -85,6 +92,9 @@ def get_trade_metrics(trade_details, price_data, block_number):
|
|
|
85
92
|
str(block_number)
|
|
86
93
|
)
|
|
87
94
|
|
|
95
|
+
if verbose:
|
|
96
|
+
print(f"Current rollover fee: {current_rollover_raw}")
|
|
97
|
+
|
|
88
98
|
# Calculate rollover for this trade
|
|
89
99
|
rollover_raw = GetTradeRolloverFee(
|
|
90
100
|
trade_details['rollover'],
|
|
@@ -93,6 +103,9 @@ def get_trade_metrics(trade_details, price_data, block_number):
|
|
|
93
103
|
trade_details['leverage']
|
|
94
104
|
)
|
|
95
105
|
|
|
106
|
+
if verbose:
|
|
107
|
+
print(f"Rollover fee: {rollover_raw}")
|
|
108
|
+
|
|
96
109
|
# Get funding rate
|
|
97
110
|
funding_rate_raw = GetFundingRate(
|
|
98
111
|
pair_info['accFundingLong'],
|
|
@@ -106,6 +119,9 @@ def get_trade_metrics(trade_details, price_data, block_number):
|
|
|
106
119
|
pair_info['shortOI']
|
|
107
120
|
)
|
|
108
121
|
|
|
122
|
+
if verbose:
|
|
123
|
+
print(f"Funding rate: {funding_rate_raw}")
|
|
124
|
+
|
|
109
125
|
# Calculate funding fee
|
|
110
126
|
funding_raw = GetTradeFundingFee(
|
|
111
127
|
trade_details['funding'],
|
|
@@ -114,6 +130,9 @@ def get_trade_metrics(trade_details, price_data, block_number):
|
|
|
114
130
|
trade_details['leverage']
|
|
115
131
|
)
|
|
116
132
|
|
|
133
|
+
if verbose:
|
|
134
|
+
print(f"Funding fee: {funding_raw}")
|
|
135
|
+
|
|
117
136
|
# Calculate liquidation price
|
|
118
137
|
liquidation_price = GetTradeLiquidationPrice(
|
|
119
138
|
trade_details['openPrice'],
|
|
@@ -125,22 +144,23 @@ def get_trade_metrics(trade_details, price_data, block_number):
|
|
|
125
144
|
)
|
|
126
145
|
liquidation_price = Decimal(liquidation_price) / PRECISION_18
|
|
127
146
|
|
|
147
|
+
if verbose:
|
|
148
|
+
print(
|
|
149
|
+
f"Liquidation price: {liquidation_price} with rollover {rollover_raw} and funding {funding_raw}")
|
|
150
|
+
|
|
128
151
|
# Calculate price impact
|
|
152
|
+
is_open = False # Get the price assuming a close
|
|
153
|
+
|
|
129
154
|
price_impact_raw = GetPriceImpact(
|
|
130
155
|
str(int(Decimal(str(price_data['mid'])) * PRECISION_18)),
|
|
131
156
|
str(int(Decimal(str(price_data['bid'])) * PRECISION_18)),
|
|
132
157
|
str(int(Decimal(str(price_data['ask'])) * PRECISION_18)),
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
trade_details['isBuy'],
|
|
136
|
-
True,
|
|
137
|
-
str(Decimal(trade_details['collateral']) *
|
|
138
|
-
Decimal(trade_details['leverage']) / PRECISION_2),
|
|
139
|
-
pair_info['tradeSizeRef']
|
|
158
|
+
is_open,
|
|
159
|
+
trade_details['isBuy']
|
|
140
160
|
)
|
|
141
161
|
price_after_impact = price_impact_raw['priceAfterImpact']
|
|
142
162
|
|
|
143
|
-
# Calculate PNL
|
|
163
|
+
# Calculate PNL (abs)
|
|
144
164
|
pnl_raw = CurrentTradeProfitRaw(
|
|
145
165
|
trade_details['openPrice'],
|
|
146
166
|
price_after_impact,
|
|
@@ -149,7 +169,7 @@ def get_trade_metrics(trade_details, price_data, block_number):
|
|
|
149
169
|
trade_details['collateral']
|
|
150
170
|
)
|
|
151
171
|
|
|
152
|
-
# Calculate total profit
|
|
172
|
+
# Calculate total profit (abs)
|
|
153
173
|
total_profit_raw = CurrentTotalProfitRaw(
|
|
154
174
|
trade_details['openPrice'],
|
|
155
175
|
price_after_impact,
|
|
@@ -176,13 +196,14 @@ def get_trade_metrics(trade_details, price_data, block_number):
|
|
|
176
196
|
|
|
177
197
|
return {
|
|
178
198
|
'pnl': float(pnl),
|
|
179
|
-
'pnl_raw': str(pnl_raw),
|
|
199
|
+
# 'pnl_raw': str(pnl_raw),
|
|
180
200
|
'pnl_percent': float(pnl_percent),
|
|
181
201
|
'rollover': float(rollover),
|
|
182
202
|
'funding': float(funding),
|
|
183
|
-
'funding_raw': str(funding_raw),
|
|
184
|
-
'rollover_raw': str(rollover_raw),
|
|
203
|
+
# 'funding_raw': str(funding_raw),
|
|
204
|
+
# 'rollover_raw': str(rollover_raw),
|
|
185
205
|
'total_profit': float(total_profit),
|
|
206
|
+
# 'total_profit_percent': float(total_profit_percent),
|
|
186
207
|
'net_pnl': float(net_pnl),
|
|
187
208
|
'net_value': float(net_value),
|
|
188
209
|
'liquidation_price': float(liquidation_price),
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import traceback
|
|
3
2
|
from enum import Enum
|
|
4
3
|
from ostium_python_sdk.constants import PRECISION_2
|
|
5
|
-
from ostium_python_sdk.formulae_wrapper import get_trade_metrics
|
|
6
4
|
from web3 import Web3
|
|
7
5
|
from .abi.abi import usdc_abi, ostium_trading_abi, ostium_trading_storage_abi
|
|
8
6
|
from .utils import convert_to_scaled_integer, fromErrorCodeToMessage, get_tp_sl_prices, to_base_units
|
|
@@ -2,7 +2,8 @@ from dotenv import load_dotenv
|
|
|
2
2
|
import os
|
|
3
3
|
from decimal import Decimal
|
|
4
4
|
|
|
5
|
-
from ostium_python_sdk.
|
|
5
|
+
from ostium_python_sdk.formulae import get_funding_rate
|
|
6
|
+
from ostium_python_sdk.utils import calculate_fee_per_hours, format_with_precision
|
|
6
7
|
|
|
7
8
|
from .formulae_wrapper import get_funding_fee_long_short, get_trade_metrics
|
|
8
9
|
from .constants import PRECISION_2, PRECISION_6, PRECISION_12, PRECISION_18, PRECISION_9
|
|
@@ -109,7 +110,7 @@ class OstiumSDK:
|
|
|
109
110
|
# get the block number
|
|
110
111
|
block_number = self.ostium.get_block_number()
|
|
111
112
|
self.log(f"Block number: {block_number}")
|
|
112
|
-
return get_trade_metrics(trade_details, price_data, block_number)
|
|
113
|
+
return get_trade_metrics(trade_details, price_data, block_number, verbose=self.verbose)
|
|
113
114
|
|
|
114
115
|
async def get_pair_net_rate_percent_per_hours(self, pair_id, period_hours=24):
|
|
115
116
|
pair_details = await self.subgraph.get_pair_details(pair_id)
|
|
@@ -134,6 +135,52 @@ class OstiumSDK:
|
|
|
134
135
|
ff_short-rollover_value, precision=4)
|
|
135
136
|
return net_long_percent, net_short_percent
|
|
136
137
|
|
|
138
|
+
async def get_funding_rate_for_pair_id(self, pair_id):
|
|
139
|
+
pair_details = await self.subgraph.get_pair_details(pair_id)
|
|
140
|
+
# get the block number
|
|
141
|
+
block_number = self.ostium.get_block_number()
|
|
142
|
+
|
|
143
|
+
self.log(f"Pair details: {pair_details}")
|
|
144
|
+
self.log(f"Block number: {block_number}")
|
|
145
|
+
|
|
146
|
+
# Get current price
|
|
147
|
+
price, _ = await self.price.get_price(
|
|
148
|
+
pair_details['from'],
|
|
149
|
+
pair_details['to']
|
|
150
|
+
)
|
|
151
|
+
self.log(f"Price: {price}")
|
|
152
|
+
|
|
153
|
+
notional_long_oi = int(
|
|
154
|
+
(Decimal(pair_details['longOI']) / PRECISION_18) * Decimal(price) * PRECISION_6)
|
|
155
|
+
notional_short_oi = int(
|
|
156
|
+
(Decimal(pair_details['shortOI']) / PRECISION_18) * Decimal(price) * PRECISION_6)
|
|
157
|
+
|
|
158
|
+
self.log(f"notional_long_oi: {notional_long_oi}")
|
|
159
|
+
self.log(f"notional_short_oi: {notional_short_oi}")
|
|
160
|
+
|
|
161
|
+
ret = get_funding_rate(
|
|
162
|
+
pair_details['curFundingLong'],
|
|
163
|
+
pair_details['curFundingShort'],
|
|
164
|
+
pair_details['lastFundingRate'],
|
|
165
|
+
# pair_details['lastFundingVelocity'],
|
|
166
|
+
pair_details['maxFundingFeePerBlock'],
|
|
167
|
+
pair_details['lastFundingBlock'],
|
|
168
|
+
block_number,
|
|
169
|
+
notional_long_oi, # Needs to be in notional aka usd
|
|
170
|
+
notional_short_oi, # Needs to be in notional aka usd
|
|
171
|
+
pair_details['maxOI'],
|
|
172
|
+
pair_details['hillInflectionPoint'],
|
|
173
|
+
pair_details['hillPosScale'],
|
|
174
|
+
pair_details['hillNegScale'],
|
|
175
|
+
pair_details['springFactor'],
|
|
176
|
+
pair_details['sFactorUpScaleP'],
|
|
177
|
+
pair_details['sFactorDownScaleP'],
|
|
178
|
+
self.verbose
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
self.log(f"Funding rate: {ret}")
|
|
182
|
+
return ret
|
|
183
|
+
|
|
137
184
|
async def get_formatted_pairs_details(self) -> list:
|
|
138
185
|
"""
|
|
139
186
|
Get formatted details for all trading pairs, with proper decimal conversion.
|
|
@@ -56,7 +56,8 @@ class SubgraphClient:
|
|
|
56
56
|
curFundingLong
|
|
57
57
|
curFundingShort
|
|
58
58
|
curRollover
|
|
59
|
-
|
|
59
|
+
totalOpenTrades
|
|
60
|
+
totalOpenLimitOrders
|
|
60
61
|
accRollover
|
|
61
62
|
lastRolloverBlock
|
|
62
63
|
rolloverFeePerBlock
|
|
@@ -64,8 +65,18 @@ class SubgraphClient:
|
|
|
64
65
|
accFundingShort
|
|
65
66
|
lastFundingBlock
|
|
66
67
|
maxFundingFeePerBlock
|
|
67
|
-
lastFundingVelocity
|
|
68
68
|
lastFundingRate
|
|
69
|
+
maxOI
|
|
70
|
+
hillInflectionPoint
|
|
71
|
+
hillPosScale
|
|
72
|
+
hillNegScale
|
|
73
|
+
lastOiDelta
|
|
74
|
+
springFactor
|
|
75
|
+
sFactorUpScaleP
|
|
76
|
+
sFactorDownScaleP
|
|
77
|
+
lastTradePrice
|
|
78
|
+
maxLeverage
|
|
79
|
+
lastFundingVelocity
|
|
69
80
|
group {
|
|
70
81
|
id
|
|
71
82
|
name
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from decimal import Decimal
|
|
3
|
-
import os
|
|
4
|
-
from humanize import naturaltime
|
|
5
3
|
from web3 import Web3
|
|
6
4
|
from ast import literal_eval
|
|
7
5
|
|
|
8
6
|
from .constants import MAX_PROFIT_P, MAX_STOP_LOSS_P
|
|
9
|
-
from .formulae import GetTakeProfitPrice
|
|
10
7
|
|
|
11
8
|
|
|
12
9
|
def format_with_precision(number, precision):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: ostium-python-sdk
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.101
|
|
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
|
|
@@ -424,8 +424,17 @@ TBD
|
|
|
424
424
|
TBD
|
|
425
425
|
|
|
426
426
|
|
|
427
|
-
###
|
|
428
|
-
|
|
427
|
+
### Get a certain Pair Net Rate % (Funding Fees, Rollover)
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
### Use sdk.get_pair_net_rate_percent_per_hours()
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Open Trade Fees calculation - SDK to get breakdown of FF, RF, PnL, etc.
|
|
434
|
+
|
|
435
|
+
```bash
|
|
436
|
+
## Use sdk.get_open_trade_metrics()
|
|
437
|
+
```
|
|
429
438
|
|
|
430
439
|
|
|
431
440
|
## Example Usage Scripts
|
{ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/SOURCES.txt
RENAMED
|
@@ -26,6 +26,7 @@ ostium_python_sdk/abi/__init__.py
|
|
|
26
26
|
ostium_python_sdk/abi/abi.py
|
|
27
27
|
ostium_python_sdk/abi/faucet_abi.py
|
|
28
28
|
tests/__init__.py
|
|
29
|
+
tests/test_get_price_impact.py
|
|
29
30
|
tests/test_slippage.py
|
|
30
31
|
tests/test_trade_get_tp_price.py
|
|
31
32
|
tests/test_trade_liquidation_price.py
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from ostium_python_sdk.formulae import GetPriceImpact
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@pytest.fixture
|
|
7
|
+
def test_cases():
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
'midPrice': Decimal('0'),
|
|
11
|
+
'bidPrice': Decimal('98'),
|
|
12
|
+
'askPrice': Decimal('0'),
|
|
13
|
+
'is_open': True,
|
|
14
|
+
'is_long': True,
|
|
15
|
+
'expected_price_impact_p': '0',
|
|
16
|
+
'expected_price_after_impact': '0'
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
'midPrice': Decimal('100'),
|
|
20
|
+
'bidPrice': Decimal('99'),
|
|
21
|
+
'askPrice': Decimal('101'),
|
|
22
|
+
'is_open': True,
|
|
23
|
+
'is_long': True,
|
|
24
|
+
'expected_price_impact_p': '1.00',
|
|
25
|
+
'expected_price_after_impact': '101'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
'midPrice': Decimal('100'),
|
|
29
|
+
'bidPrice': Decimal('99'),
|
|
30
|
+
'askPrice': Decimal('101'),
|
|
31
|
+
'is_open': False,
|
|
32
|
+
'is_long': True,
|
|
33
|
+
'expected_price_impact_p': '1.00',
|
|
34
|
+
'expected_price_after_impact': '99'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
'midPrice': Decimal('100'),
|
|
38
|
+
'bidPrice': Decimal('99'),
|
|
39
|
+
'askPrice': Decimal('101'),
|
|
40
|
+
'is_open': False,
|
|
41
|
+
'is_long': False,
|
|
42
|
+
'expected_price_impact_p': '1.00',
|
|
43
|
+
'expected_price_after_impact': '101'
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_get_price_impact(test_cases):
|
|
49
|
+
for case in test_cases:
|
|
50
|
+
response = GetPriceImpact(
|
|
51
|
+
case['midPrice'],
|
|
52
|
+
case['bidPrice'],
|
|
53
|
+
case['askPrice'],
|
|
54
|
+
case['is_open'],
|
|
55
|
+
case['is_long']
|
|
56
|
+
)
|
|
57
|
+
assert pytest.approx(response['priceAfterImpact'], rel=Decimal(1e-7)
|
|
58
|
+
) == case['expected_price_after_impact']
|
|
59
|
+
assert pytest.approx(response['priceImpactP'], rel=Decimal(1e-7)
|
|
60
|
+
) == case['expected_price_impact_p']
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/requires.txt
RENAMED
|
File without changes
|
{ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|