Skip to content

Commit a90fb3e

Browse files
committed
Update liquidation curves to show user keys
1 parent 8e8e6cd commit a90fb3e

File tree

2 files changed

+111
-18
lines changed

2 files changed

+111
-18
lines changed

backend/api/liquidation.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
@router.get("/liquidation-curve")
1212
def get_liquidation_curve(request: BackendRequest, market_index: int):
1313
vat: Vat = request.state.backend_state.vat
14-
liquidations_long: list[tuple[float, float]] = []
15-
liquidations_short: list[tuple[float, float]] = []
14+
liquidations_long: list[tuple[float, float, str]] = []
15+
liquidations_short: list[tuple[float, float, str]] = []
1616
market_price = vat.perp_oracles.get(market_index)
1717
market_price_ui = market_price.price / PRICE_PRECISION
18-
for user in vat.users.user_map.values():
18+
for pubkey, user in vat.users.user_map.items():
1919
perp_position = user.get_perp_position(market_index)
2020
if perp_position is not None:
2121
liquidation_price = user.get_perp_liq_price(market_index)
@@ -29,9 +29,13 @@ def get_liquidation_curve(request: BackendRequest, market_index: int):
2929
if is_zero:
3030
continue
3131
if is_short and liquidation_price_ui > market_price_ui:
32-
liquidations_short.append((liquidation_price_ui, position_notional))
32+
liquidations_short.append(
33+
(liquidation_price_ui, position_notional, str(pubkey))
34+
)
3335
elif is_long and liquidation_price_ui < market_price_ui:
34-
liquidations_long.append((liquidation_price_ui, position_notional))
36+
liquidations_long.append(
37+
(liquidation_price_ui, position_notional, str(pubkey))
38+
)
3539
else:
3640
pass
3741

src/page/liquidation_curves.py

Lines changed: 102 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,36 @@ def filter_outliers(
1919
):
2020
"""Filter out liquidations based on a range multiplier of the market price."""
2121
return [
22-
(price, notional)
23-
for price, notional in liquidations
22+
(price, notional, pubkey)
23+
for price, notional, pubkey in liquidations
2424
if lower_bound_multiplier * market_price_ui
2525
<= price
2626
<= upper_bound_multiplier * market_price_ui
2727
]
2828

2929
def aggregate_liquidations(liquidations):
30-
"""Aggregate liquidations to calculate cumulative notional amounts."""
31-
price_to_notional = defaultdict(float)
32-
for price, notional in liquidations:
33-
price_to_notional[price] += notional
34-
return price_to_notional
30+
"""Aggregate liquidations to calculate cumulative notional amounts and track pubkeys with their sizes."""
31+
price_to_data = defaultdict(lambda: {"notional": 0.0, "positions": []})
32+
for price, notional, pubkey in liquidations:
33+
price_to_data[price]["notional"] += notional
34+
price_to_data[price]["positions"].append(
35+
(pubkey, notional)
36+
) # Store tuple of (pubkey, size)
37+
return price_to_data
3538

3639
def prepare_data_for_plot(aggregated_data, reverse=False):
3740
"""Prepare and sort data for plotting, optionally reversing the cumulative sum for descending plots."""
3841
sorted_prices = sorted(aggregated_data.keys(), reverse=reverse)
3942
cumulative_notional = np.cumsum(
40-
[aggregated_data[price] for price in sorted_prices]
43+
[aggregated_data[price]["notional"] for price in sorted_prices]
4144
)
42-
return sorted_prices, cumulative_notional
45+
# Accumulate positions (pubkey and size) for each price point
46+
cumulative_positions = []
47+
current_positions = []
48+
for price in sorted_prices:
49+
current_positions.extend(aggregated_data[price]["positions"])
50+
cumulative_positions.append(list(current_positions))
51+
return sorted_prices, cumulative_notional, cumulative_positions
4352

4453
# Filter outliers based on defined criteria
4554
liquidations_long = filter_outliers(
@@ -53,10 +62,12 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
5362
aggregated_long = aggregate_liquidations(liquidations_long)
5463
aggregated_short = aggregate_liquidations(liquidations_short)
5564

56-
long_prices, long_cum_notional = prepare_data_for_plot(
65+
long_prices, long_cum_notional, long_pubkeys = prepare_data_for_plot(
5766
aggregated_long, reverse=True
5867
)
59-
short_prices, short_cum_notional = prepare_data_for_plot(aggregated_short)
68+
short_prices, short_cum_notional, short_pubkeys = prepare_data_for_plot(
69+
aggregated_short
70+
)
6071

6172
if not long_prices or not short_prices:
6273
return None
@@ -73,6 +84,8 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
7384
mode="lines",
7485
name="Long Positions",
7586
line=dict(color="purple", width=2),
87+
hovertemplate="Price: %{x}<br>Cumulative Notional: %{y}<br>Accounts: %{text}<extra></extra>",
88+
text=[f"{len(pubkeys)} accounts" for pubkeys in long_pubkeys],
7689
)
7790
)
7891
short_fig.add_trace(
@@ -82,6 +95,8 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
8295
mode="lines",
8396
name="Short Positions",
8497
line=dict(color="turquoise", width=2),
98+
hovertemplate="Price: %{x}<br>Cumulative Notional: %{y}<br>Accounts: %{text}<extra></extra>",
99+
text=[f"{len(pubkeys)} accounts" for pubkeys in short_pubkeys],
85100
)
86101
)
87102

@@ -102,7 +117,7 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
102117
yaxis=dict(showgrid=True),
103118
)
104119

105-
return long_fig, short_fig
120+
return long_fig, short_fig, long_pubkeys, short_pubkeys, long_prices, short_prices
106121

107122

108123
def liquidation_curves_page():
@@ -140,12 +155,86 @@ def liquidation_curves_page():
140155
st.write(e)
141156
st.stop()
142157

143-
(long_fig, short_fig) = plot_liquidation_curves(liquidation_data)
158+
# Unpack all returned values
159+
(long_fig, short_fig, long_pubkeys, short_pubkeys, long_prices, short_prices) = (
160+
plot_liquidation_curves(liquidation_data)
161+
)
144162

145163
long_col, short_col = st.columns([1, 1])
146164

147165
with long_col:
148166
st.plotly_chart(long_fig, use_container_width=True)
149167

168+
# Add accordion for long positions
169+
with st.expander("Long Position Accounts"):
170+
if long_pubkeys and len(long_pubkeys[-1]) > 0:
171+
st.write(f"Total Accounts: {len(long_pubkeys[-1])}")
172+
long_data = []
173+
for i, positions in enumerate(long_pubkeys):
174+
if i > 0:
175+
new_positions = set(positions) - set(long_pubkeys[i - 1])
176+
if new_positions:
177+
for pubkey, size in new_positions:
178+
long_data.append(
179+
{
180+
"Price": f"{long_prices[i]:.2f}",
181+
"Size": f"{size:,.2f}",
182+
"Account": pubkey,
183+
"Link": f"https://app.drift.trade/overview?userAccount={pubkey}",
184+
}
185+
)
186+
if long_data:
187+
st.dataframe(
188+
long_data,
189+
column_config={
190+
"Price": st.column_config.TextColumn("Price"),
191+
"Size": st.column_config.TextColumn("Size"),
192+
"Account": st.column_config.TextColumn(
193+
"Account", width="large"
194+
),
195+
"Link": st.column_config.LinkColumn(
196+
"Link", display_text="View"
197+
),
198+
},
199+
hide_index=True,
200+
)
201+
else:
202+
st.write("No long positions found")
203+
150204
with short_col:
151205
st.plotly_chart(short_fig, use_container_width=True)
206+
207+
with st.expander("Short Position Accounts"):
208+
if short_pubkeys and len(short_pubkeys[-1]) > 0:
209+
st.write(f"Total Accounts: {len(short_pubkeys[-1])}")
210+
short_data = []
211+
for i, positions in enumerate(short_pubkeys):
212+
if i > 0:
213+
new_positions = set(positions) - set(short_pubkeys[i - 1])
214+
if new_positions:
215+
for pubkey, size in new_positions:
216+
short_data.append(
217+
{
218+
"Price": f"{short_prices[i]:.2f}",
219+
"Size": f"{size:,.2f}",
220+
"Account": pubkey,
221+
"Link": f"https://app.drift.trade/overview?userAccount={pubkey}",
222+
}
223+
)
224+
if short_data:
225+
st.dataframe(
226+
short_data,
227+
column_config={
228+
"Price": st.column_config.TextColumn("Price"),
229+
"Size": st.column_config.TextColumn("Size"),
230+
"Account": st.column_config.TextColumn(
231+
"Account", width="large"
232+
),
233+
"Link": st.column_config.LinkColumn(
234+
"Link", display_text="View"
235+
),
236+
},
237+
hide_index=True,
238+
)
239+
else:
240+
st.write("No short positions found")

0 commit comments

Comments
 (0)