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.
Files changed (34) hide show
  1. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/PKG-INFO +12 -3
  2. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/README.md +11 -2
  3. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/config.py +3 -1
  4. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/constants.py +1 -0
  5. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/formulae.py +191 -28
  6. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/formulae_wrapper.py +35 -14
  7. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/ostium.py +0 -2
  8. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/sdk.py +49 -2
  9. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/subgraph.py +13 -2
  10. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/utils.py +0 -3
  11. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/PKG-INFO +12 -3
  12. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/SOURCES.txt +1 -0
  13. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/setup.py +1 -1
  14. ostium_python_sdk-0.2.101/tests/test_get_price_impact.py +60 -0
  15. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/MANIFEST.in +0 -0
  16. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/__init__.py +0 -0
  17. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/abi/__init__.py +0 -0
  18. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/abi/abi.py +0 -0
  19. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/abi/faucet_abi.py +0 -0
  20. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/balance.py +0 -0
  21. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/exceptions.py +0 -0
  22. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/faucet.py +0 -0
  23. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk/price.py +0 -0
  24. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/dependency_links.txt +0 -0
  25. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/requires.txt +0 -0
  26. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/ostium_python_sdk.egg-info/top_level.txt +0 -0
  27. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/pyproject.toml +0 -0
  28. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/requirements-dev.txt +0 -0
  29. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/requirements.txt +0 -0
  30. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/setup.cfg +0 -0
  31. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/tests/__init__.py +0 -0
  32. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/tests/test_slippage.py +0 -0
  33. {ostium_python_sdk-0.2.1 → ostium_python_sdk-0.2.101}/tests/test_trade_get_tp_price.py +0 -0
  34. {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.1
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
- ### Funding rate calculation - SDK to get breakdown of FF, RF, PnL, etc.
428
- TBD
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
- ### Funding rate calculation - SDK to get breakdown of FF, RF, PnL, etc.
389
- TBD
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
- spread_p: str,
234
- is_limit: bool,
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
- spread_p = Decimal(spread_p)
245
- trade_size = Decimal(trade_size)
246
- trade_size_ref = Decimal(trade_size_ref)
247
-
248
- # Calculate base spread
249
- base_spread = (ask_price - bid_price) / mid_price
250
-
251
- # Calculate impact spread
252
- impact_spread = base_spread * (trade_size / trade_size_ref)
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
- 'priceAfterImpact': str(price_after_impact),
266
- 'impactSpread': str(impact_spread)
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
- pair_info['spreadP'],
134
- False,
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.utils import calculate_fee_per_hours, convert_decimals, format_with_precision
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
- maxLeverage
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.1
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
- ### Funding rate calculation - SDK to get breakdown of FF, RF, PnL, etc.
428
- TBD
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
@@ -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
@@ -19,7 +19,7 @@ if changelog_path.exists():
19
19
 
20
20
  setup(
21
21
  name="ostium-python-sdk",
22
- version="0.2.1",
22
+ version="0.2.101",
23
23
  packages=find_packages(),
24
24
  install_requires=read_requirements('requirements.txt'),
25
25
  extras_require={
@@ -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']