nado-protocol 0.2.8__tar.gz → 0.3.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.
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/PKG-INFO +1 -1
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/margin_manager.py +113 -17
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/pyproject.toml +1 -1
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/README.md +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/base.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/market/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/market/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/market/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/perp/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/perp/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/rewards/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/rewards/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/rewards/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/spot/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/spot/base.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/spot/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/spot/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/subaccount/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/subaccount/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/subaccount/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/context.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/Endpoint.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/FQuerier.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IAirdrop.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IClearinghouse.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IEndpoint.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IFoundationRewardsAirdrop.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IPerpEngine.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IProductEngine.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/ISpotEngine.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IStaking.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/MockERC20.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/deployments/deployment.mainnet.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/deployments/deployment.testing.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/deployments/deployment.testnet.json +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/eip712/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/eip712/domain.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/eip712/sign.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/eip712/types.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/loader.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/types.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/types/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/types/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/types/models.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/types/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/engine_client/types/stream.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/indexer_client/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/indexer_client/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/indexer_client/types/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/indexer_client/types/models.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/indexer_client/types/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/trigger_client/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/trigger_client/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/trigger_client/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/trigger_client/types/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/trigger_client/types/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/trigger_client/types/models.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/trigger_client/types/query.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/__init__.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/backend.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/balance.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/bytes32.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/enum.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/exceptions.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/execute.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/expiration.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/interest.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/math.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/model.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/nonce.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/order.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/subaccount.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/time.py +0 -0
- {nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/utils/twap.py +0 -0
|
@@ -78,6 +78,8 @@ class CrossPositionMetrics(BaseModel):
|
|
|
78
78
|
symbol: str
|
|
79
79
|
position_size: Decimal
|
|
80
80
|
notional_value: Decimal
|
|
81
|
+
avg_entry_price: Optional[Decimal] # Average entry price (requires indexer data)
|
|
82
|
+
est_liq_price: Optional[Decimal] # Estimated liquidation price
|
|
81
83
|
est_pnl: Optional[Decimal] # Estimated PnL (requires indexer data)
|
|
82
84
|
unsettled: Decimal # Unsettled quote (v_quote_balance)
|
|
83
85
|
margin_used: Decimal
|
|
@@ -520,9 +522,9 @@ class MarginManager:
|
|
|
520
522
|
# This represents the unrealized PnL
|
|
521
523
|
unsettled = self.calculate_perp_balance_value(balance)
|
|
522
524
|
|
|
523
|
-
# Calculate
|
|
524
|
-
|
|
525
|
-
|
|
525
|
+
# Calculate metrics (requires indexer data for avg_entry_price and est_pnl)
|
|
526
|
+
avg_entry_price = self._calculate_avg_entry_price(balance)
|
|
527
|
+
est_liq_price = self._calculate_est_liq_price(balance)
|
|
526
528
|
est_pnl = self._calculate_est_pnl(balance)
|
|
527
529
|
|
|
528
530
|
return CrossPositionMetrics(
|
|
@@ -530,6 +532,8 @@ class MarginManager:
|
|
|
530
532
|
symbol=f"Product_{balance.product_id}",
|
|
531
533
|
position_size=balance.amount,
|
|
532
534
|
notional_value=notional,
|
|
535
|
+
avg_entry_price=avg_entry_price,
|
|
536
|
+
est_liq_price=est_liq_price,
|
|
533
537
|
est_pnl=est_pnl,
|
|
534
538
|
unsettled=unsettled,
|
|
535
539
|
margin_used=margin_used,
|
|
@@ -541,6 +545,24 @@ class MarginManager:
|
|
|
541
545
|
short_weight_maintenance=balance.short_weight_maintenance,
|
|
542
546
|
)
|
|
543
547
|
|
|
548
|
+
def _get_indexer_event_for_product(self, product_id: int) -> Optional[IndexerEvent]:
|
|
549
|
+
"""
|
|
550
|
+
Get indexer event for a specific product (cross margin only).
|
|
551
|
+
|
|
552
|
+
Returns None if indexer data is not available or product not found.
|
|
553
|
+
"""
|
|
554
|
+
if not self.indexer_events or product_id == self.QUOTE_PRODUCT_ID:
|
|
555
|
+
return None
|
|
556
|
+
|
|
557
|
+
for event in self.indexer_events:
|
|
558
|
+
if event.product_id != product_id:
|
|
559
|
+
continue
|
|
560
|
+
if event.isolated:
|
|
561
|
+
continue
|
|
562
|
+
return event
|
|
563
|
+
|
|
564
|
+
return None
|
|
565
|
+
|
|
544
566
|
def _calculate_est_pnl(self, balance: BalanceWithProduct) -> Optional[Decimal]:
|
|
545
567
|
"""
|
|
546
568
|
Calculate estimated PnL if indexer snapshot is available.
|
|
@@ -549,26 +571,90 @@ class MarginManager:
|
|
|
549
571
|
|
|
550
572
|
Returns None if indexer data is not available.
|
|
551
573
|
"""
|
|
552
|
-
|
|
574
|
+
event = self._get_indexer_event_for_product(balance.product_id)
|
|
575
|
+
if event is None:
|
|
553
576
|
return None
|
|
554
577
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
continue
|
|
578
|
+
try:
|
|
579
|
+
net_entry_int = int(event.net_entry_unrealized)
|
|
580
|
+
except (TypeError, ValueError):
|
|
581
|
+
return None
|
|
560
582
|
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
583
|
+
net_entry_unrealized = Decimal(net_entry_int) / Decimal(10**18)
|
|
584
|
+
current_value = balance.amount * balance.oracle_price
|
|
585
|
+
return current_value - net_entry_unrealized
|
|
586
|
+
|
|
587
|
+
def _calculate_avg_entry_price(
|
|
588
|
+
self, balance: BalanceWithProduct
|
|
589
|
+
) -> Optional[Decimal]:
|
|
590
|
+
"""
|
|
591
|
+
Calculate average entry price if indexer snapshot is available.
|
|
565
592
|
|
|
566
|
-
|
|
593
|
+
Formula: abs(netEntryUnrealized / position_amount)
|
|
567
594
|
|
|
568
|
-
|
|
569
|
-
|
|
595
|
+
Returns None if indexer data is not available or position is zero.
|
|
596
|
+
"""
|
|
597
|
+
if balance.amount == 0:
|
|
598
|
+
return None
|
|
570
599
|
|
|
571
|
-
|
|
600
|
+
event = self._get_indexer_event_for_product(balance.product_id)
|
|
601
|
+
if event is None:
|
|
602
|
+
return None
|
|
603
|
+
|
|
604
|
+
try:
|
|
605
|
+
net_entry_int = int(event.net_entry_unrealized)
|
|
606
|
+
except (TypeError, ValueError):
|
|
607
|
+
return None
|
|
608
|
+
|
|
609
|
+
net_entry_unrealized = Decimal(net_entry_int) / Decimal(10**18)
|
|
610
|
+
return abs(net_entry_unrealized / balance.amount)
|
|
611
|
+
|
|
612
|
+
def _calculate_est_liq_price(
|
|
613
|
+
self, balance: BalanceWithProduct
|
|
614
|
+
) -> Optional[Decimal]:
|
|
615
|
+
"""
|
|
616
|
+
Calculate estimated liquidation price.
|
|
617
|
+
|
|
618
|
+
Formula:
|
|
619
|
+
- If long: oracle_price - (maint_health / amount / long_weight)
|
|
620
|
+
- If short: oracle_price + (maint_health / abs(amount) * short_weight)
|
|
621
|
+
|
|
622
|
+
Returns None if:
|
|
623
|
+
- Position is zero
|
|
624
|
+
- Long liq price <= 0 (prevents -infinity)
|
|
625
|
+
- Short liq price >= 10x oracle price (prevents +infinity)
|
|
626
|
+
"""
|
|
627
|
+
if balance.amount == 0:
|
|
628
|
+
return None
|
|
629
|
+
|
|
630
|
+
# Get maintenance health from subaccount info
|
|
631
|
+
maint_health = self._parse_health(self.subaccount_info.healths[1])
|
|
632
|
+
|
|
633
|
+
is_long = balance.amount > 0
|
|
634
|
+
|
|
635
|
+
if is_long:
|
|
636
|
+
# Long: oracle_price - (maint_health / amount / long_weight)
|
|
637
|
+
if balance.long_weight_maintenance == 0:
|
|
638
|
+
return None
|
|
639
|
+
|
|
640
|
+
liq_price = balance.oracle_price - (
|
|
641
|
+
maint_health / balance.amount / balance.long_weight_maintenance
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
# If liquidation price is 0 or less, return None (prevents -infinity)
|
|
645
|
+
if liq_price <= 0:
|
|
646
|
+
return None
|
|
647
|
+
else:
|
|
648
|
+
# Short: oracle_price + (maint_health / abs(amount) * short_weight)
|
|
649
|
+
liq_price = balance.oracle_price + (
|
|
650
|
+
maint_health / abs(balance.amount) * balance.short_weight_maintenance
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
# If liquidation price is 10x oracle price, return None (prevents +infinity)
|
|
654
|
+
if liq_price >= balance.oracle_price * 10:
|
|
655
|
+
return None
|
|
656
|
+
|
|
657
|
+
return liq_price
|
|
572
658
|
|
|
573
659
|
def calculate_isolated_position_metrics(
|
|
574
660
|
self, iso_pos: IsolatedPosition
|
|
@@ -783,6 +869,16 @@ def print_account_summary(summary: AccountSummary) -> None:
|
|
|
783
869
|
print(f"│ Position: {cross_pos.position_size:,.3f}")
|
|
784
870
|
print(f"│ Notional: ${cross_pos.notional_value:,.2f}")
|
|
785
871
|
|
|
872
|
+
if cross_pos.avg_entry_price is not None:
|
|
873
|
+
print(f"│ Avg Entry Price: ${cross_pos.avg_entry_price:,.2f}")
|
|
874
|
+
else:
|
|
875
|
+
print(f"│ Avg Entry Price: N/A")
|
|
876
|
+
|
|
877
|
+
if cross_pos.est_liq_price is not None:
|
|
878
|
+
print(f"│ Est. Liq Price: ${cross_pos.est_liq_price:,.2f}")
|
|
879
|
+
else:
|
|
880
|
+
print(f"│ Est. Liq Price: N/A")
|
|
881
|
+
|
|
786
882
|
if cross_pos.est_pnl is not None:
|
|
787
883
|
pnl_sign = "+" if cross_pos.est_pnl >= 0 else ""
|
|
788
884
|
print(f"│ Est. PnL: {pnl_sign}${cross_pos.est_pnl:,.2f}")
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/client/apis/subaccount/__init__.py
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
|
{nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IClearinghouse.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{nado_protocol-0.2.8 → nado_protocol-0.3.0}/nado_protocol/contracts/abis/IProductEngine.json
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
|
|
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
|
|
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
|
|
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
|
|
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
|