Margin Manager
The Margin Manager provides comprehensive margin calculations for your Nado Protocol subaccounts. It calculates health, margin usage, leverage, and position-level metrics to help you understand your account’s risk profile.
Overview
The margin manager calculates:
Health Metrics: Initial, maintenance, and unweighted health
Margin Usage: Percentage of margin being used (0-100%)
Position Metrics: Individual position details with health contributions
Leverage: Overall account leverage
Available Funds: How much margin is available for new positions
Key Concepts
Health Types
The system uses three levels of health/margin requirements:
Type |
Purpose |
Description |
|---|---|---|
Initial |
Open new positions |
Strictest requirement. Uses |
Maintenance |
Avoid liquidation |
Less strict. Uses |
Unweighted |
Raw asset value |
No haircuts applied (weight = 1.0). Used as reference. |
Health Calculation
For each balance:
health_contribution = amount × oracle_price × weight
Where weight depends on position direction:
Long positions (amount ≥ 0): Use
long_weight_*Short positions (amount < 0): Use
short_weight_*
For the entire subaccount:
assets = sum of positive health contributions
liabilities = sum of negative health contributions (absolute value)
health = assets - liabilities
Liquidation occurs when maintenance health < 0.
Margin Modes
- Cross Margin
Margin is shared across all positions. All balances contribute to a single health pool.
margin_usage = (unweighted_health - initial_health) / unweighted_health
- Isolated Margin
Dedicated margin per perp position. Only USDT can be used. Max 1 isolated position per market.
net_margin = quote_amount + unsettled_pnl leverage = notional_value / net_margin
Quick Start
Basic Usage
import time
from nado_protocol.client import create_nado_client, NadoClientMode
from nado_protocol.utils.margin_manager import MarginManager, print_account_summary
client = create_nado_client(NadoClientMode.TESTNET)
# Optionally override defaults (subaccount hex, timestamp, etc.)
manager = MarginManager.from_client(
client,
include_indexer_events=True,
snapshot_timestamp=int(time.time()),
)
summary = manager.calculate_account_summary()
print_account_summary(summary)
If you skip the optional indexer request (include_indexer_events=False),
CrossPositionMetrics.est_pnl remains None and the printed summary displays
N/A for Est. PnL.
Passing snapshot_active_only=True (the default) ensures the indexer only returns
balances that are live at the requested timestamp, keeping the snapshot focused on
current positions.
Manual setup (advanced)
If you need more control over the data-fetching steps, you can assemble the manager yourself:
import time
from nado_protocol.engine_client import EngineQueryClient, EngineClientOpts
from nado_protocol.indexer_client import IndexerQueryClient, IndexerClientOpts
from nado_protocol.indexer_client.types.query import IndexerAccountSnapshotsParams
from nado_protocol.utils.bytes32 import subaccount_to_hex
from nado_protocol.utils.margin_manager import MarginManager, print_account_summary
# Create read-only clients (no private key needed)
engine_client = EngineQueryClient(
EngineClientOpts(url="https://gateway.test.nado-backend.xyz/v1")
)
indexer_client = IndexerQueryClient(
IndexerClientOpts(url="https://archive.test.nado-backend.xyz/v1")
)
# Get subaccount data
wallet_address = "0x1234..."
subaccount = subaccount_to_hex(wallet_address, "default")
subaccount_info = engine_client.get_subaccount_info(subaccount)
isolated_positions = engine_client.get_isolated_positions(subaccount).isolated_positions
# Optional: fetch indexer events for Est. PnL display
current_timestamp = int(time.time())
snapshot_response = indexer_client.get_multi_subaccount_snapshots(
IndexerAccountSnapshotsParams(
subaccounts=[subaccount],
timestamps=[current_timestamp],
isolated=False,
active=True,
)
)
snapshots_map = snapshot_response.snapshots
snapshot_events = []
if snapshots_map:
snapshots_for_subaccount = snapshots_map.get(subaccount) or next(
iter(snapshots_map.values())
)
if snapshots_for_subaccount:
latest_key = max(snapshots_for_subaccount.keys(), key=int)
snapshot_events = snapshots_for_subaccount.get(latest_key, [])
indexer_events = snapshot_events
# Calculate all margin metrics
margin_manager = MarginManager(
subaccount_info,
isolated_positions,
indexer_snapshot_events=indexer_events,
)
summary = margin_manager.calculate_account_summary()
# Display formatted summary
print_account_summary(summary)
This outputs a complete margin summary:
================================================================================
MARGIN MANAGER ACCOUNT SUMMARY
================================================================================
📊 HEALTH METRICS
Initial Health: $999,543,667.24
Maintenance Health: $999,761,007.36
Unweighted Health: $1,000,086,939.32
📈 MARGIN USAGE
Initial Margin: 0.05%
Maintenance Margin: 0.03%
💰 AVAILABLE FUNDS
Available (Initial): $999,543,667.24
Until Liquidation (Maint): $999,761,007.36
📦 PORTFOLIO
Total Value: $1,000,086,939.32
Leverage: 0.11x
Tutorial
Example 1: Check Account Risk
Monitor your account’s liquidation risk:
from nado_protocol.client import create_nado_client
from nado_protocol.utils.bytes32 import subaccount_to_hex
from nado_protocol.utils.margin_manager import MarginManager
# Create client
client = create_nado_client("testnet", private_key)
subaccount = subaccount_to_hex(client.context.signer.address, "default")
# Fetch data
subaccount_info = client.context.engine_client.get_subaccount_info(subaccount)
isolated = client.context.engine_client.get_isolated_positions(subaccount).isolated_positions
# Calculate metrics
margin_manager = MarginManager(subaccount_info, isolated)
summary = margin_manager.calculate_account_summary()
# Check risk level
maint_usage = summary.maint_margin_usage_fraction * 100
if maint_usage > 90:
print("🔴 CRITICAL RISK - Near liquidation!")
elif maint_usage > 75:
print("🟠 HIGH RISK - Reduce positions")
elif maint_usage > 50:
print("🟡 MEDIUM RISK")
else:
print("🟢 LOW RISK")
print(f"Margin Usage: {maint_usage:.2f}%")
print(f"Leverage: {summary.account_leverage:.2f}x")
print(f"Available Margin: ${summary.funds_available:,.2f}")
Example 2: Analyze Individual Positions
Get detailed metrics for each position:
# ... setup margin_manager as above ...
summary = margin_manager.calculate_account_summary()
# Cross margin positions
print("\n🔄 CROSS MARGIN POSITIONS\n")
for pos in summary.cross_positions:
print(f"Product {pos.product_id}:")
print(f" Position Size: {pos.position_size:,.4f}")
print(f" Notional: ${pos.notional_value:,.2f}")
print(f" Margin Used: ${pos.margin_used:,.2f}")
print(f" Initial Health: ${pos.initial_health:,.2f}")
print(f" Maint Health: ${pos.maintenance_health:,.2f}")
# Calculate position-specific margin usage
if pos.notional_value > 0:
pos_leverage = pos.notional_value / pos.margin_used
print(f" Effective Leverage: {pos_leverage:.2f}x")
print()
# Isolated margin positions
print("\n🔒 ISOLATED MARGIN POSITIONS\n")
for pos in summary.isolated_positions:
print(f"Product {pos.product_id}:")
print(f" Position Size: {pos.position_size:,.4f}")
print(f" Notional: ${pos.notional_value:,.2f}")
print(f" Net Margin: ${pos.net_margin:,.2f}")
print(f" Leverage: {pos.leverage:.2f}x")
print()
Example 3: Calculate Maximum Position Size
Determine how large of a position you can open:
from decimal import Decimal
# ... setup margin_manager as above ...
summary = margin_manager.calculate_account_summary()
# Get oracle price for a product (e.g., BTC)
btc_product_id = 1
btc_product = next(
(p for p in subaccount_info.perp_products if p.product_id == btc_product_id),
None
)
if btc_product:
from nado_protocol.utils.math import from_x18
oracle_price = Decimal(from_x18(int(btc_product.oracle_price_x18)))
long_weight_initial = Decimal(from_x18(int(btc_product.risk.long_weight_initial_x18)))
# Calculate max position size
available_margin = summary.funds_available
leverage_factor = Decimal(1) - long_weight_initial # e.g., 0.1 for 10x
max_notional = available_margin / leverage_factor
max_size = max_notional / oracle_price
print(f"BTC Oracle Price: ${oracle_price:,.2f}")
print(f"Available Margin: ${available_margin:,.2f}")
print(f"Max Position Size: {max_size:.4f} BTC")
print(f"Max Notional: ${max_notional:,.2f}")
Example 4: Monitor Spot Deposits and Borrows
Track your spot lending activity:
# ... setup margin_manager as above ...
summary = margin_manager.calculate_account_summary()
print("💵 SPOT BALANCE SUMMARY\n")
print(f"Total Deposits: ${summary.total_spot_deposits:,.2f}")
print(f"Total Borrows: ${summary.total_spot_borrows:,.2f}")
print(f"Net Balance: ${summary.total_spot_deposits - summary.total_spot_borrows:,.2f}")
# Calculate utilization
if summary.total_spot_deposits > 0:
utilization = (summary.total_spot_borrows / summary.total_spot_deposits) * 100
print(f"Utilization: {utilization:.2f}%")
Example 5: Read-Only Access (No Private Key)
View any public subaccount’s margin metrics:
import time
from nado_protocol.engine_client import EngineQueryClient, EngineClientOpts
from nado_protocol.indexer_client import IndexerQueryClient, IndexerClientOpts
from nado_protocol.indexer_client.types.query import IndexerAccountSnapshotsParams
from nado_protocol.utils.bytes32 import subaccount_to_hex
from nado_protocol.utils.margin_manager import MarginManager
# Any wallet address (no private key needed)
wallet_to_analyze = "0x8D7d64d6cF1D4F018Dd101482Ac71Ad49e30c560"
# Create engine client
engine_client = EngineQueryClient(
EngineClientOpts(url="https://gateway.test.nado-backend.xyz/v1")
)
indexer_client = IndexerQueryClient(
IndexerClientOpts(url="https://archive.test.nado-backend.xyz/v1")
)
# Get data
subaccount = subaccount_to_hex(wallet_to_analyze, "default")
subaccount_info = engine_client.get_subaccount_info(subaccount)
isolated = engine_client.get_isolated_positions(subaccount).isolated_positions
# Fetch latest indexer snapshot for Est. PnL (optional)
timestamp = int(time.time())
snapshot = indexer_client.get_multi_subaccount_snapshots(
IndexerAccountSnapshotsParams(
subaccounts=[subaccount],
timestamps=[timestamp],
isolated=False,
active=True,
)
)
indexer_events = snapshot.snapshots.get(subaccount, {}).get(str(timestamp), [])
# Analyze
margin_manager = MarginManager(
subaccount_info,
isolated,
indexer_snapshot_events=indexer_events,
)
summary = margin_manager.calculate_account_summary()
print(f"Analyzing wallet: {wallet_to_analyze}")
print(f"Portfolio Value: ${summary.portfolio_value:,.2f}")
print(f"Leverage: {summary.account_leverage:.2f}x")
print(f"Risk Level: {summary.maint_margin_usage_fraction * 100:.2f}% margin used")
Advanced Usage
Individual Calculation Methods
Use specific calculation methods for custom analytics:
from nado_protocol.utils.margin_manager import MarginManager
from decimal import Decimal
# ... setup margin_manager ...
# Create a balance object for calculations
from nado_protocol.utils.margin_manager import BalanceWithProduct
balance = BalanceWithProduct(
product_id=1,
amount=Decimal("10"), # 10 BTC long
oracle_price=Decimal("50000"),
long_weight_initial=Decimal("0.9"),
long_weight_maintenance=Decimal("0.95"),
short_weight_initial=Decimal("1.1"),
short_weight_maintenance=Decimal("1.05"),
balance_type="perp",
v_quote_balance=Decimal("0")
)
# Calculate notional value
notional = margin_manager.calculate_perp_balance_notional_value(balance)
print(f"Notional: ${notional:,.2f}") # $500,000
# Calculate margin required (without PnL)
health_metrics = margin_manager.calculate_perp_balance_health_without_pnl(balance)
margin_required = abs(health_metrics.initial)
print(f"Margin Required: ${margin_required:,.2f}") # $50,000 (10x leverage)
# Calculate health contribution
health = margin_manager.calculate_spot_balance_health(balance)
print(f"Initial Health: ${health.initial:,.2f}")
print(f"Maint Health: ${health.maintenance:,.2f}")
Balance Value Utilities
Use the balance utility functions for quick calculations:
from nado_protocol.utils.balance import (
calculate_spot_balance_value,
calculate_perp_balance_notional_value,
calculate_perp_balance_value,
parse_spot_balance_value,
parse_perp_balance_value,
)
from decimal import Decimal
# Direct calculations
eth_value = calculate_spot_balance_value(
amount=Decimal("100"),
oracle_price=Decimal("2000")
)
print(f"ETH Value: ${eth_value:,.2f}") # $200,000
# Perp notional
btc_notional = calculate_perp_balance_notional_value(
amount=Decimal("-5"), # 5 BTC short
oracle_price=Decimal("50000")
)
print(f"BTC Notional: ${btc_notional:,.2f}") # $250,000
# Parse from SDK types
spot_value = parse_spot_balance_value(balance, product)
perp_value = parse_perp_balance_value(balance, product)
Understanding the Results
AccountSummary Fields
Field |
Description |
|---|---|
|
Health using initial weights. Must be > 0 to open new positions. |
|
Health using maintenance weights. Must be > 0 to avoid liquidation. |
|
Raw portfolio value without haircuts. |
|
Fraction [0, 1] of initial margin being used. |
|
Fraction [0, 1] of maintenance margin being used. Risk indicator. |
|
Available margin for new positions (= max(0, initial_health)). |
|
Distance to liquidation (= max(0, maintenance_health)). |
|
Total portfolio value including isolated positions. |
|
Overall leverage multiplier. |
|
List of cross margin position metrics. |
|
List of isolated margin position metrics. |
|
Total value of spot deposits. |
|
Total value of spot borrows (absolute). |
CrossPositionMetrics Fields
Field |
Description |
|---|---|
|
Product identifier. |
|
Position size (positive for long, negative for short). |
|
Absolute notional value (= abs(size × oracle_price)). |
|
Estimated PnL from indexer (amount × oracle_price - netEntryUnrealized). Requires indexer data. |
|
Full perp balance value (amount × oracle_price + v_quote_balance). This represents unrealized PnL. |
|
Margin consumed by position, excluding PnL impact. |
|
Health contribution using initial weights. |
|
Health contribution using maintenance weights. |
IsolatedPositionMetrics Fields
Field |
Description |
|---|---|
|
Product identifier. |
|
Position size. |
|
Absolute notional value. |
|
Deposited margin + unsettled PnL. |
|
Position leverage (= notional / net_margin). |
|
Health for the isolated position (initial). |
|
Health for the isolated position (maintenance). |
Does margin manager use oracle price or market price?
All margin calculations use ORACLE PRICE.
Market prices (bid/ask from the orderbook) are only used for: - Estimated exit price for unrealized PnL display - NOT for any margin or health calculations
Do I need to convert USDT to USD?
No conversion needed! All values from the engine are already in the correct denomination.
Oracle prices are denominated in the primary quote token (USDT), and all margin calculations work directly with these values. The UI displays dollar signs ($) as a convention, but no USDT→USD price conversion is applied.
Key points:
Perp tracked variables (
netEntryUnrealized,netFundingUnrealized, etc.) are already in quote (USDT) termsNo multiplication by USDT/USD rate in any margin calculation
The only oracle price multiplication is for spot interest (converting from token units to USD)
How do I calculate initial margin for a perp position?
Common Questions For a perp position, initial margin is:
notional = abs(position_size × oracle_price)
initial_margin = notional × abs(1 - weight_initial)
maintenance_margin = notional × abs(1 - weight_maintenance)
Important: Use abs(1 - weight) to handle both long and short positions:
Long positions: weight < 1, so (1 - weight) > 0
Short positions: weight > 1, so (1 - weight) < 0, need abs()
Example (Long): - Position: 10 BTC long - Oracle Price: $50,000 - Long Weight Initial: 0.9 (allows 10x leverage)
notional = abs(10 × 50,000) = $500,000
initial_margin = 500,000 × (1 - 0.9) = 500,000 × 0.1 = $50,000
Example (Short): - Position: -10 BTC short - Oracle Price: $50,000 - Short Weight Initial: 1.1 (requires 10x leverage)
notional = abs(-10 × 50,000) = $500,000
initial_margin = 500,000 × abs(1 - 1.1) = 500,000 × 0.1 = $50,000
Both positions require $50,000 initial margin (10x leverage).
Why is my margin usage 0% even though I have positions?
Margin usage is only calculated when:
Unweighted health > 0
Account has borrows OR perp positions
Zero-health products are excluded
If you only have spot deposits (no borrows, no perps), margin usage will be 0%.
What’s the difference between cross and isolated margin?
Cross Margin: - Margin shared across ALL positions - Better capital efficiency - Risk spreads across entire portfolio - Default mode
Isolated Margin: - Dedicated margin PER position - Risk limited to individual position - Only USDT can be used as margin - Max 1 isolated position per market - Useful for high-risk trades
How is leverage calculated?
Account leverage:
leverage = sum(abs(notional_values)) / unweighted_health
Where notional values include: - Spot: abs(amount × oracle_price) for non-quote products - Perp: abs(amount × oracle_price)
Quote product (USDT) is excluded from the calculation.
API Reference
MarginManager Class
- class nado_protocol.utils.margin_manager.MarginManager(subaccount_info, isolated_positions=None, indexer_snapshot_events=None)[source]
Bases:
objectComprehensive margin calculator for Nado Protocol.
Calculates all margin metrics for a subaccount including health, margin usage, leverage, and position-level details. Matches TypeScript SDK implementation.
- QUOTE_PRODUCT_ID = 0
- classmethod from_client(client, *, subaccount=None, subaccount_name='default', include_indexer_events=True, snapshot_timestamp=None, snapshot_isolated=False, snapshot_active_only=True)[source]
Initialize a MarginManager by fetching data via a NadoClient.
- Return type:
- Args:
client: Configured Nado client with engine/indexer connectivity. subaccount: Optional subaccount hex (bytes32). If omitted, derives the default
subaccount using the client’s signer and
subaccount_name.- subaccount_name: Subaccount suffix (e.g.
default) used when deriving the subaccount hex. Ignored when
subaccountis provided.- include_indexer_events: When True (default), fetch indexer snapshot balances
for estimated PnL calculations.
- snapshot_timestamp: Epoch seconds to request from the indexer. Defaults to
int(time.time())when indexer data is requested.- snapshot_isolated: Passed through to the indexer request to limit snapshots
to isolated (True), cross (False), or all (None) balances. Defaults to
Falseto match cross-margin behaviour.- snapshot_active_only: When True (default), enables the indexer’s
active filter so only live balances are returned.
- subaccount_name: Subaccount suffix (e.g.
- Returns:
MarginManager instance populated with fresh engine and optional indexer data.
- calculate_account_summary()[source]
Calculate complete account margin summary.
- Return type:
- Returns:
AccountSummary with all margin calculations
- calculate_spot_balance_value(balance)[source]
Calculate quote value of a spot balance.
Formula: amount * oracle_price
- Return type:
Decimal
- calculate_perp_balance_notional_value(balance)[source]
Calculate notional value of a perp position.
Formula: abs(amount * oracle_price)
- Return type:
Decimal
- calculate_perp_balance_value(balance)[source]
Calculate true quote value of a perp balance (unrealized PnL).
Formula: (amount * oracle_price) + v_quote_balance
- Return type:
Decimal
- calculate_spot_balance_health(balance)[source]
Calculate health contribution for a spot balance.
Formula: amount * oracle_price * weight (weight is long_weight if amount >= 0, else short_weight)
- Return type:
- calculate_perp_balance_health_without_pnl(balance)[source]
Calculate perp balance health WITHOUT the impact of unsettled PnL.
Shows “margin used” by the position, excluding PnL. Formula: -1 * abs(notional_value) * (1 - long_weight)
- Return type:
- calculate_cross_position_margin_without_pnl(balance)[source]
Calculate margin used for a cross position excluding unsettled PnL impact.
Used in margin manager “Margin Used” column. Formula: max(0, -(initial_health - perp_value))
- Return type:
Decimal
- calculate_isolated_position_net_margin(base_balance, quote_balance)[source]
Calculate net margin in an isolated position.
Formula: quote_amount + (base_amount * oracle_price + v_quote_balance)
- Return type:
Decimal
- calculate_isolated_position_leverage(base_balance, net_margin)[source]
Calculate leverage for an isolated position.
Formula: notional_value / net_margin
- Return type:
Decimal
- calculate_margin_usage_fractions(initial_health, maint_health, unweighted_health)[source]
Calculate margin usage fractions bounded to [0, 1].
Formula: (unweighted_health - health) / unweighted_health Returns 0 if no borrows/perps or unweighted_health is 0.
- Return type:
- calculate_account_leverage(balances, unweighted_health)[source]
Calculate overall account leverage.
Formula: sum(abs(unweighted health for non-quote balances)) / unweighted_health
- Return type:
Decimal
- calculate_cross_position_metrics(balance)[source]
Calculate all metrics for a cross margin position.
- Return type:
Models
- class nado_protocol.utils.margin_manager.AccountSummary(**data)[source]
Complete account margin summary.
-
initial_health:
Decimal
-
maintenance_health:
Decimal
-
unweighted_health:
Decimal
-
margin_usage_fraction:
Decimal
-
maint_margin_usage_fraction:
Decimal
-
funds_available:
Decimal
-
funds_until_liquidation:
Decimal
-
portfolio_value:
Decimal
-
account_leverage:
Decimal
-
cross_positions:
list[CrossPositionMetrics]
-
isolated_positions:
list[IsolatedPositionMetrics]
-
spot_positions:
list[BalanceWithProduct]
-
total_spot_deposits:
Decimal
-
total_spot_borrows:
Decimal
-
initial_health:
- class nado_protocol.utils.margin_manager.CrossPositionMetrics(**data)[source]
Metrics for a cross margin position.
-
product_id:
int
-
symbol:
str
-
position_size:
Decimal
-
notional_value:
Decimal
-
est_pnl:
Optional[Decimal]
-
unsettled:
Decimal
-
margin_used:
Decimal
-
initial_health:
Decimal
-
maintenance_health:
Decimal
-
long_weight_initial:
Decimal
-
long_weight_maintenance:
Decimal
-
short_weight_initial:
Decimal
-
short_weight_maintenance:
Decimal
-
product_id:
- class nado_protocol.utils.margin_manager.IsolatedPositionMetrics(**data)[source]
Metrics for an isolated margin position.
-
product_id:
int
-
symbol:
str
-
position_size:
Decimal
-
notional_value:
Decimal
-
net_margin:
Decimal
-
leverage:
Decimal
-
initial_health:
Decimal
-
maintenance_health:
Decimal
-
product_id:
Utility Functions
- nado_protocol.utils.margin_manager.print_account_summary(summary)[source]
Print formatted account summary matching UI layout.
- Return type:
None
Balance Value Calculation Utilities
- nado_protocol.utils.balance.calculate_spot_balance_value(amount, oracle_price)[source]
Calculate the quote value of a spot balance.
Formula: amount * oracle_price
This is used for: - Calculating health contributions - Determining deposits vs borrows - Portfolio value calculations
- Return type:
Decimal
- Args:
amount: Token amount (can be negative for borrows) oracle_price: Oracle price in quote currency
- Returns:
Value in quote currency (positive for deposits, negative for borrows)
- Example:
>>> calculate_spot_balance_value(100, 2000) # 100 ETH at $2000 Decimal('200000') >>> calculate_spot_balance_value(-50, 2000) # 50 ETH borrowed Decimal('-100000')
- nado_protocol.utils.balance.calculate_perp_balance_notional_value(amount, oracle_price)[source]
Calculate the notional value of a perp position.
Formula: abs(amount * oracle_price)
This represents the total size of the position in quote currency terms, regardless of direction (long or short).
- Return type:
Decimal
- Args:
amount: Position size (positive for long, negative for short) oracle_price: Oracle price in quote currency
- Returns:
Absolute notional value in quote currency
- Example:
>>> calculate_perp_balance_notional_value(10, 50000) # 10 BTC long Decimal('500000') >>> calculate_perp_balance_notional_value(-10, 50000) # 10 BTC short Decimal('500000')
- nado_protocol.utils.balance.calculate_perp_balance_value(amount, oracle_price, v_quote_balance)[source]
Calculate the true quote value of a perp balance (unrealized PnL).
Formula: (amount * oracle_price) + v_quote_balance
The v_quote_balance represents: - Unrealized PnL from price changes - Accumulated funding payments - Entry cost adjustments
This value is what would be added to your balance if the position were closed.
- Return type:
Decimal
- Args:
amount: Position size oracle_price: Oracle price in quote currency v_quote_balance: Virtual quote balance (unsettled PnL)
- Returns:
Total value in quote currency (can be positive or negative)
- Example:
>>> # Long 10 BTC at $50k, now at $51k, with funding >>> calculate_perp_balance_value(10, 51000, -500000) Decimal('10000') # $10k profit
- nado_protocol.utils.balance.parse_spot_balance_value(balance, product)[source]
Parse spot balance value from raw SDK types.
This is a convenience function that extracts values from the SDK types and calls calculate_spot_balance_value.
- Return type:
Decimal
- Args:
balance: Spot balance from subaccount info product: Spot product information
- Returns:
Balance value in quote currency
- nado_protocol.utils.balance.parse_perp_balance_notional_value(balance, product)[source]
Parse perp notional value from raw SDK types.
- Return type:
Decimal
- Args:
balance: Perp balance from subaccount info product: Perp product information
- Returns:
Notional value in quote currency
- nado_protocol.utils.balance.parse_perp_balance_value(balance, product)[source]
Parse perp balance value (unrealized PnL) from raw SDK types.
- Return type:
Decimal
- Args:
balance: Perp balance from subaccount info product: Perp product information
- Returns:
Balance value in quote currency
- nado_protocol.utils.balance.calculate_total_spot_deposits_and_borrows(balances)[source]
Calculate total spot deposits and borrows across all balances.
- Return type:
tuple[Decimal,Decimal]
- Args:
balances: List of (balance, product) tuples
- Returns:
Tuple of (total_deposits, total_borrows) in quote currency Both values are positive (borrows is absolute value)
- Example:
>>> balances = [(usdt_balance, usdt_product), (eth_balance, eth_product)] >>> deposits, borrows = calculate_total_spot_deposits_and_borrows(balances) >>> deposits # Total deposits Decimal('10000') >>> borrows # Total borrows (absolute value) Decimal('5000')
- nado_protocol.utils.balance.calculate_total_perp_notional(balances)[source]
Calculate total notional value across all perp positions.
- Return type:
Decimal
- Args:
balances: List of (balance, product) tuples
- Returns:
Total notional value in quote currency
- Example:
>>> balances = [(btc_perp_balance, btc_perp_product)] >>> total = calculate_total_perp_notional(balances) >>> total Decimal('500000') # Total position size
- nado_protocol.utils.balance.calculate_total_perp_value(balances)[source]
Calculate total unrealized PnL across all perp positions.
- Return type:
Decimal
- Args:
balances: List of (balance, product) tuples
- Returns:
Total unrealized PnL in quote currency (can be positive or negative)
- Example:
>>> balances = [(btc_perp_balance, btc_perp_product)] >>> total_pnl = calculate_total_perp_value(balances) >>> total_pnl Decimal('10000') # $10k unrealized profit
See Also
Getting started - SDK basics
user-guides - Other usage examples
api-reference - Complete API documentation