Skip to content

Commit

Permalink
Update liquidation curves to show user keys
Browse files Browse the repository at this point in the history
  • Loading branch information
SinaKhalili committed Nov 6, 2024
1 parent 8e8e6cd commit a90fb3e
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 18 deletions.
14 changes: 9 additions & 5 deletions backend/api/liquidation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
@router.get("/liquidation-curve")
def get_liquidation_curve(request: BackendRequest, market_index: int):
vat: Vat = request.state.backend_state.vat
liquidations_long: list[tuple[float, float]] = []
liquidations_short: list[tuple[float, float]] = []
liquidations_long: list[tuple[float, float, str]] = []
liquidations_short: list[tuple[float, float, str]] = []
market_price = vat.perp_oracles.get(market_index)
market_price_ui = market_price.price / PRICE_PRECISION
for user in vat.users.user_map.values():
for pubkey, user in vat.users.user_map.items():
perp_position = user.get_perp_position(market_index)
if perp_position is not None:
liquidation_price = user.get_perp_liq_price(market_index)
Expand All @@ -29,9 +29,13 @@ def get_liquidation_curve(request: BackendRequest, market_index: int):
if is_zero:
continue
if is_short and liquidation_price_ui > market_price_ui:
liquidations_short.append((liquidation_price_ui, position_notional))
liquidations_short.append(
(liquidation_price_ui, position_notional, str(pubkey))
)
elif is_long and liquidation_price_ui < market_price_ui:
liquidations_long.append((liquidation_price_ui, position_notional))
liquidations_long.append(
(liquidation_price_ui, position_notional, str(pubkey))
)
else:
pass

Expand Down
115 changes: 102 additions & 13 deletions src/page/liquidation_curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,36 @@ def filter_outliers(
):
"""Filter out liquidations based on a range multiplier of the market price."""
return [
(price, notional)
for price, notional in liquidations
(price, notional, pubkey)
for price, notional, pubkey in liquidations
if lower_bound_multiplier * market_price_ui
<= price
<= upper_bound_multiplier * market_price_ui
]

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

def prepare_data_for_plot(aggregated_data, reverse=False):
"""Prepare and sort data for plotting, optionally reversing the cumulative sum for descending plots."""
sorted_prices = sorted(aggregated_data.keys(), reverse=reverse)
cumulative_notional = np.cumsum(
[aggregated_data[price] for price in sorted_prices]
[aggregated_data[price]["notional"] for price in sorted_prices]
)
return sorted_prices, cumulative_notional
# Accumulate positions (pubkey and size) for each price point
cumulative_positions = []
current_positions = []
for price in sorted_prices:
current_positions.extend(aggregated_data[price]["positions"])
cumulative_positions.append(list(current_positions))
return sorted_prices, cumulative_notional, cumulative_positions

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

long_prices, long_cum_notional = prepare_data_for_plot(
long_prices, long_cum_notional, long_pubkeys = prepare_data_for_plot(
aggregated_long, reverse=True
)
short_prices, short_cum_notional = prepare_data_for_plot(aggregated_short)
short_prices, short_cum_notional, short_pubkeys = prepare_data_for_plot(
aggregated_short
)

if not long_prices or not short_prices:
return None
Expand All @@ -73,6 +84,8 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
mode="lines",
name="Long Positions",
line=dict(color="purple", width=2),
hovertemplate="Price: %{x}<br>Cumulative Notional: %{y}<br>Accounts: %{text}<extra></extra>",
text=[f"{len(pubkeys)} accounts" for pubkeys in long_pubkeys],
)
)
short_fig.add_trace(
Expand All @@ -82,6 +95,8 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
mode="lines",
name="Short Positions",
line=dict(color="turquoise", width=2),
hovertemplate="Price: %{x}<br>Cumulative Notional: %{y}<br>Accounts: %{text}<extra></extra>",
text=[f"{len(pubkeys)} accounts" for pubkeys in short_pubkeys],
)
)

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

return long_fig, short_fig
return long_fig, short_fig, long_pubkeys, short_pubkeys, long_prices, short_prices


def liquidation_curves_page():
Expand Down Expand Up @@ -140,12 +155,86 @@ def liquidation_curves_page():
st.write(e)
st.stop()

(long_fig, short_fig) = plot_liquidation_curves(liquidation_data)
# Unpack all returned values
(long_fig, short_fig, long_pubkeys, short_pubkeys, long_prices, short_prices) = (
plot_liquidation_curves(liquidation_data)
)

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

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

# Add accordion for long positions
with st.expander("Long Position Accounts"):
if long_pubkeys and len(long_pubkeys[-1]) > 0:
st.write(f"Total Accounts: {len(long_pubkeys[-1])}")
long_data = []
for i, positions in enumerate(long_pubkeys):
if i > 0:
new_positions = set(positions) - set(long_pubkeys[i - 1])
if new_positions:
for pubkey, size in new_positions:
long_data.append(
{
"Price": f"{long_prices[i]:.2f}",
"Size": f"{size:,.2f}",
"Account": pubkey,
"Link": f"https://app.drift.trade/overview?userAccount={pubkey}",
}
)
if long_data:
st.dataframe(
long_data,
column_config={
"Price": st.column_config.TextColumn("Price"),
"Size": st.column_config.TextColumn("Size"),
"Account": st.column_config.TextColumn(
"Account", width="large"
),
"Link": st.column_config.LinkColumn(
"Link", display_text="View"
),
},
hide_index=True,
)
else:
st.write("No long positions found")

with short_col:
st.plotly_chart(short_fig, use_container_width=True)

with st.expander("Short Position Accounts"):
if short_pubkeys and len(short_pubkeys[-1]) > 0:
st.write(f"Total Accounts: {len(short_pubkeys[-1])}")
short_data = []
for i, positions in enumerate(short_pubkeys):
if i > 0:
new_positions = set(positions) - set(short_pubkeys[i - 1])
if new_positions:
for pubkey, size in new_positions:
short_data.append(
{
"Price": f"{short_prices[i]:.2f}",
"Size": f"{size:,.2f}",
"Account": pubkey,
"Link": f"https://app.drift.trade/overview?userAccount={pubkey}",
}
)
if short_data:
st.dataframe(
short_data,
column_config={
"Price": st.column_config.TextColumn("Price"),
"Size": st.column_config.TextColumn("Size"),
"Account": st.column_config.TextColumn(
"Account", width="large"
),
"Link": st.column_config.LinkColumn(
"Link", display_text="View"
),
},
hide_index=True,
)
else:
st.write("No short positions found")

0 comments on commit a90fb3e

Please sign in to comment.