Skip to content

Commit 8f3cb8a

Browse files
committed
fix test case description
1 parent 16f91e9 commit 8f3cb8a

2 files changed

Lines changed: 96 additions & 34 deletions

File tree

examples/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,10 @@ Paper trading lets you simulate trades against real order book data without subm
166166

167167
- `paper_trading_health.py`
168168
- opens positions across multiple markets
169+
- compares conservative vs aggressive leverage on the same two-market portfolio
169170
- inspects account health, margin usage, leverage, and liquidation prices
170171

171172
## Setup steps for mainnet
172173
- deposit money on Lighter to create an account first
173174
- change the URL to `mainnet.zklighter.elliot.ai`
174-
- repeat setup step
175+
- repeat setup step

examples/paper_trading_health.py

Lines changed: 94 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,43 @@
1-
"""Paper trading — health & liquidation inspection.
1+
"""Paper trading — cross-market health & liquidation inspection.
22
3-
Demonstrates how account health and liquidation prices change
4-
depending on leverage. Runs two scenarios:
5-
1. Conservative — large collateral, small position, no liquidation risk
6-
2. Aggressive — small collateral, large position, tight liquidation price
3+
Demonstrates how account health and liquidation prices change for the
4+
same two-market portfolio under different collateral levels. Runs two
5+
cross-margin scenarios:
6+
1. Conservative — large collateral, modest ETH + BTC exposure
7+
2. Aggressive — smaller collateral, larger ETH + BTC exposure
78
"""
89

910
import asyncio
1011
import lighter
1112

13+
ETH_MARKET_ID = 0
14+
BTC_MARKET_ID = 1
15+
MARKETS = [
16+
(ETH_MARKET_ID, "ETH-PERP"),
17+
(BTC_MARKET_ID, "BTC-PERP"),
18+
]
1219

13-
def print_health(paper: lighter.PaperClient, markets: list):
20+
21+
def round_size(size: float, decimals: int) -> float:
22+
return round(size, decimals)
23+
24+
25+
def size_for_notional(
26+
paper: lighter.PaperClient,
27+
market_id: int,
28+
notional_usdc: float,
29+
) -> float:
30+
config = paper.market_configs[market_id]
31+
raw_size = notional_usdc / config.last_trade_price
32+
return max(round_size(raw_size, config.size_decimals), config.min_base_amount)
33+
34+
35+
async def track_markets(paper: lighter.PaperClient, markets: list[tuple[int, str]]) -> None:
36+
for market_id, _ in markets:
37+
await paper.track_market_snapshot(market_id)
38+
39+
40+
def print_health(paper: lighter.PaperClient, markets: list[tuple[int, str]]) -> None:
1441
health = paper.get_health()
1542
print(f" Health status: {health.status.name}")
1643
print(f" Total account value: {health.total_account_value:.2f} USDC")
@@ -26,7 +53,7 @@ def print_health(paper: lighter.PaperClient, markets: list):
2653
liq_str = f"${liq_price:.2f}" if liq_price > 0 else "n/a (fully collateralized)"
2754
side = "LONG" if position.size > 0 else "SHORT"
2855
print(
29-
f" {label} {side} {abs(position.size)}"
56+
f" {label} {side} {abs(position.size):g}"
3057
f" entry=${position.avg_entry_price:.2f}"
3158
f" mark=${position.mark_price:.2f}"
3259
f" unrealized_pnl={position.unrealized_pnl:.2f}"
@@ -45,59 +72,93 @@ async def main():
4572
)
4673

4774
# ── Scenario 1: Conservative (low leverage) ──────────────────────
48-
# $10,000 collateral backing a 0.5 ETH long → ~0.12x leverage.
49-
# The position is so over-collateralized that no liquidation price
50-
# exists (ETH would have to go below $0).
75+
# $10,000 collateral backing a modest ETH long + BTC short.
76+
# The portfolio stays lightly levered with wide or nonexistent
77+
# liquidation thresholds.
5178
print("=" * 60)
52-
print("SCENARIO 1: Conservative — $10,000 collateral, 0.5 ETH long")
79+
print("SCENARIO 1: Conservative — $10,000 collateral, ETH + BTC portfolio")
5380
print("=" * 60)
5481

5582
conservative = lighter.PaperClient(api_client, initial_collateral_usdc=10_000)
56-
await conservative.track_market_snapshot(market_id=0)
83+
await track_markets(conservative, MARKETS)
84+
85+
conservative_eth_size = size_for_notional(conservative, ETH_MARKET_ID, 1_500)
86+
conservative_btc_size = size_for_notional(conservative, BTC_MARKET_ID, 1_000)
5787

5888
await conservative.create_paper_order(
5989
lighter.PaperOrderRequest(
60-
market_id=0,
90+
market_id=ETH_MARKET_ID,
6191
side=lighter.PaperOrderSide.BUY,
62-
base_amount=0.5,
92+
base_amount=conservative_eth_size,
6393
)
6494
)
65-
print_health(conservative, [(0, "ETH-PERP")])
95+
await conservative.create_paper_order(
96+
lighter.PaperOrderRequest(
97+
market_id=BTC_MARKET_ID,
98+
side=lighter.PaperOrderSide.SELL,
99+
base_amount=conservative_btc_size,
100+
)
101+
)
102+
print(
103+
f"Opened {conservative_eth_size:g} ETH long and "
104+
f"{conservative_btc_size:g} BTC short."
105+
)
106+
print_health(conservative, MARKETS)
66107

67108
# ── Scenario 2: Aggressive (high leverage) ───────────────────────
68-
# $600 collateral backing a 4 ETH long → ~15x leverage.
69-
# Liquidation price will be tight — a ~5% drop would trigger it.
109+
# $1,500 collateral backing the same market mix at much larger size.
110+
# Cross-margin still shares collateral across both markets, but
111+
# liquidation prices should move much closer to the current marks.
70112
print()
71113
print("=" * 60)
72-
print("SCENARIO 2: Aggressive — $600 collateral, 4 ETH long")
114+
print("SCENARIO 2: Aggressive — $1,500 collateral, same markets at larger size")
73115
print("=" * 60)
74116

75-
aggressive = lighter.PaperClient(api_client, initial_collateral_usdc=600)
76-
await aggressive.track_market_snapshot(market_id=0)
117+
aggressive = lighter.PaperClient(api_client, initial_collateral_usdc=1_500)
118+
await track_markets(aggressive, MARKETS)
119+
120+
aggressive_eth_size = size_for_notional(aggressive, ETH_MARKET_ID, 6_000)
121+
aggressive_btc_size = size_for_notional(aggressive, BTC_MARKET_ID, 3_000)
77122

78123
await aggressive.create_paper_order(
79124
lighter.PaperOrderRequest(
80-
market_id=0,
125+
market_id=ETH_MARKET_ID,
81126
side=lighter.PaperOrderSide.BUY,
82-
base_amount=4.0,
127+
base_amount=aggressive_eth_size,
128+
)
129+
)
130+
await aggressive.create_paper_order(
131+
lighter.PaperOrderRequest(
132+
market_id=BTC_MARKET_ID,
133+
side=lighter.PaperOrderSide.SELL,
134+
base_amount=aggressive_btc_size,
83135
)
84136
)
85-
print_health(aggressive, [(0, "ETH-PERP")])
137+
print(
138+
f"Opened {aggressive_eth_size:g} ETH long and "
139+
f"{aggressive_btc_size:g} BTC short."
140+
)
141+
print_health(aggressive, MARKETS)
86142

87143
# Show the contrast
88-
cons_liq = conservative.get_liquidation_price(0)
89-
aggr_liq = aggressive.get_liquidation_price(0)
90-
aggr_pos = aggressive.get_position(0)
91144
print()
92145
print("-" * 60)
93146
print("COMPARISON")
94-
cons_liq_str = "n/a (can't be liquidated)" if cons_liq == 0 else f"${cons_liq:.2f}"
95-
print(f" Conservative liq price: {cons_liq_str}")
96-
if aggr_pos is None:
97-
reason = "already liquidated" if aggressive.get_health().has_been_liquidated else "no open position"
98-
print(f" Aggressive position: {reason}")
99-
else:
100-
print(f" Aggressive liq price: ${aggr_liq:.2f} (${aggr_pos.mark_price - aggr_liq:.2f} below mark)")
147+
for market_id, label in MARKETS:
148+
cons_liq = conservative.get_liquidation_price(market_id)
149+
aggr_liq = aggressive.get_liquidation_price(market_id)
150+
aggr_pos = aggressive.get_position(market_id)
151+
cons_liq_str = "n/a (can't be liquidated)" if cons_liq == 0 else f"${cons_liq:.2f}"
152+
print(f" {label} conservative liq: {cons_liq_str}")
153+
if aggr_pos is None:
154+
reason = "already liquidated" if aggressive.get_health().has_been_liquidated else "no open position"
155+
print(f" {label} aggressive: {reason}")
156+
else:
157+
distance = abs(aggr_pos.mark_price - aggr_liq)
158+
print(
159+
f" {label} aggressive liq: ${aggr_liq:.2f} "
160+
f"(${distance:.2f} from mark)"
161+
)
101162
print("-" * 60)
102163

103164
await api_client.close()

0 commit comments

Comments
 (0)