Skip to content

Commit 75763bb

Browse files
committed
Update script, format with ruff
1 parent dfef15a commit 75763bb

18 files changed

+253
-806
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ venv
66
pickles/*
77
cache
88
ignore
9+
ucache

backend/scripts/generate_ucache.py

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import argparse
12
import asyncio
23
import glob
34
import json
@@ -29,6 +30,35 @@ class Endpoint:
2930
params: dict
3031

3132

33+
@dataclass
34+
class AssetLiabilityEndpoint(Endpoint):
35+
mode: int
36+
perp_market_index: int
37+
38+
def __init__(self, mode: int, perp_market_index: int):
39+
super().__init__(
40+
"asset-liability/matrix",
41+
{"mode": mode, "perp_market_index": perp_market_index},
42+
)
43+
44+
45+
@dataclass
46+
class PriceShockEndpoint(Endpoint):
47+
asset_group: str
48+
oracle_distortion: float
49+
n_scenarios: int
50+
51+
def __init__(self, asset_group: str, oracle_distortion: float, n_scenarios: int):
52+
super().__init__(
53+
"price-shock/usermap",
54+
{
55+
"asset_group": asset_group,
56+
"oracle_distortion": oracle_distortion,
57+
"n_scenarios": n_scenarios,
58+
},
59+
)
60+
61+
3262
async def process_multiple_endpoints(state_pickle_path: str, endpoints: list[Endpoint]):
3363
"""Process a single endpoint in its own process"""
3464
state = BackendState()
@@ -102,7 +132,6 @@ async def run_request():
102132
async def generate_ucache(endpoints: list[Endpoint]):
103133
"""Generate ucache files by splitting endpoints across processes"""
104134
ucache_dir = "ucache"
105-
use_snapshot = os.getenv("USE_SNAPSHOT", "false").lower() == "true"
106135

107136
print("Generating ucache")
108137
if not os.path.exists(ucache_dir):
@@ -112,33 +141,49 @@ async def generate_ucache(endpoints: list[Endpoint]):
112141

113142

114143
async def main():
115-
load_dotenv()
144+
parser = argparse.ArgumentParser(description="Generate ucache files")
145+
parser.add_argument(
146+
"--use-snapshot", action="store_true", help="Use existing snapshot"
147+
)
148+
149+
subparsers = parser.add_subparsers(dest="command", required=True)
116150

117-
use_snapshot = os.getenv("USE_SNAPSHOT", "false").lower() == "true"
118-
print(f"use_snapshot: {use_snapshot}")
151+
# Asset Liability parser
152+
al_parser = subparsers.add_parser("asset-liability")
153+
al_parser.add_argument("--mode", type=int, required=True)
154+
al_parser.add_argument("--perp-market-index", type=int, required=True)
119155

120-
if not use_snapshot:
156+
# Price Shock parser
157+
ps_parser = subparsers.add_parser("price-shock")
158+
ps_parser.add_argument("--asset-group", type=str, required=True)
159+
ps_parser.add_argument("--oracle-distortion", type=float, required=True)
160+
ps_parser.add_argument("--n-scenarios", type=int, required=True)
161+
162+
args = parser.parse_args()
163+
164+
if not args.use_snapshot:
121165
state = BackendState()
122166
state.initialize(os.getenv("RPC_URL") or "")
123167
print("Taking snapshot")
124168
await state.bootstrap()
125169
await state.take_pickle_snapshot()
126170
await state.close()
127171

128-
endpoints = [
129-
Endpoint(
130-
endpoint="asset-liability/matrix",
131-
params={"mode": 0, "perp_market_index": 0},
132-
),
133-
Endpoint(
134-
endpoint="price-shock/usermap",
135-
params={
136-
"asset_group": "ignore+stables",
137-
"oracle_distortion": 0.05,
138-
"n_scenarios": 5,
139-
},
140-
),
141-
]
172+
endpoints = []
173+
if args.command == "asset-liability":
174+
endpoints.append(
175+
AssetLiabilityEndpoint(
176+
mode=args.mode, perp_market_index=args.perp_market_index
177+
)
178+
)
179+
elif args.command == "price-shock":
180+
endpoints.append(
181+
PriceShockEndpoint(
182+
asset_group=args.asset_group,
183+
oracle_distortion=args.oracle_distortion,
184+
n_scenarios=args.n_scenarios,
185+
)
186+
)
142187

143188
await generate_ucache(endpoints)
144189

src/lib/user_metrics.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import copy
22
from typing import List, Optional
33

4-
from driftpy.constants.numeric_constants import MARGIN_PRECISION
5-
from driftpy.constants.numeric_constants import QUOTE_PRECISION
4+
from driftpy.constants.numeric_constants import MARGIN_PRECISION, QUOTE_PRECISION
65
from driftpy.constants.perp_markets import mainnet_perp_market_configs
76
from driftpy.constants.spot_markets import mainnet_spot_market_configs
87
from driftpy.drift_client import DriftClient

src/main.py

Lines changed: 6 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44
from dotenv import load_dotenv
55

66
from lib.page import header, needs_backend, sidebar
7-
from page.asset_liability_cached import asset_liab_matrix_cached_page
7+
from page.asset_liability import asset_liab_matrix_cached_page
88
from page.backend import backend_page
99
from page.health import health_page
10-
from page.health_cached import health_cached_page
1110
from page.liquidation_curves import liquidation_curves_page
1211
from page.orderbook import orderbook_page
13-
from page.price_shock_cached import price_shock_cached_page
14-
from sections.welcome import welcome_page
12+
from page.price_shock import price_shock_cached_page
13+
from page.welcome import welcome_page
1514

1615
load_dotenv()
1716

@@ -51,27 +50,15 @@ def apply_custom_css(css):
5150
title="Health",
5251
icon="🏥",
5352
),
54-
# st.Page(
55-
# needs_backend(price_shock_page),
56-
# url_path="price-shock",
57-
# title="Price Shock",
58-
# icon="💸",
59-
# ),
60-
# st.Page(
61-
# needs_backend(asset_liab_matrix_page),
62-
# url_path="asset-liability-matrix",
63-
# title="Asset-Liability Matrix",
64-
# icon="📊",
65-
# ),
6653
st.Page(
6754
price_shock_cached_page,
68-
url_path="price-shock-cached",
55+
url_path="price-shock",
6956
title="Price Shock",
7057
icon="💸",
7158
),
7259
st.Page(
7360
asset_liab_matrix_cached_page,
74-
url_path="asset-liability-matrix-cached",
61+
url_path="asset-liability-matrix",
7562
title="Asset-Liability Matrix",
7663
icon="📊",
7764
),
@@ -82,14 +69,7 @@ def apply_custom_css(css):
8269
icon="🌊",
8370
),
8471
]
85-
cached_pages = [
86-
st.Page(
87-
health_cached_page,
88-
url_path="health-cached",
89-
title="Health (Cached)",
90-
icon="🏥",
91-
),
92-
]
72+
9373
if os.getenv("DEV"):
9474
main_pages.append(
9575
st.Page(
@@ -103,7 +83,6 @@ def apply_custom_css(css):
10383
pg = st.navigation(
10484
{
10585
"Main": main_pages,
106-
# "Cached": cached_pages,
10786
}
10887
)
10988
pg.run()

src/page/asset_liability.py

Lines changed: 103 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
import json
2-
3-
from driftpy.constants.perp_markets import mainnet_perp_market_configs
4-
from driftpy.constants.spot_markets import mainnet_spot_market_configs
5-
from lib.api import api
61
import pandas as pd
7-
from requests.exceptions import JSONDecodeError
82
import streamlit as st
3+
from driftpy.constants.perp_markets import mainnet_perp_market_configs
4+
from driftpy.constants.spot_markets import mainnet_spot_market_configs
95

6+
from lib.api import api2
7+
from utils import get_current_slot
108

119
options = [0, 1, 2, 3]
1210
labels = [
@@ -17,77 +15,133 @@
1715
]
1816

1917

20-
def asset_liab_matrix_page():
18+
def calculate_effective_leverage(assets: float, liabilities: float) -> float:
19+
return liabilities / assets if assets != 0 else 0
20+
21+
22+
def format_metric(
23+
value: float, should_highlight: bool, mode: int, financial: bool = False
24+
) -> str:
25+
formatted = f"{value:,.2f}" if financial else f"{value:.2f}"
26+
return f"{formatted} ✅" if should_highlight and mode > 0 else formatted
27+
28+
29+
def generate_summary_data(
30+
df: pd.DataFrame, mode: int, perp_market_index: int
31+
) -> pd.DataFrame:
32+
summary_data = {}
33+
for i in range(len(mainnet_spot_market_configs)):
34+
prefix = f"spot_{i}"
35+
assets = df[f"{prefix}_all_assets"].sum()
36+
liabilities = df[f"{prefix}_all"].sum()
37+
38+
summary_data[f"spot{i}"] = {
39+
"all_assets": assets,
40+
"all_liabilities": format_metric(
41+
liabilities, 0 < liabilities < 1_000_000, mode, financial=True
42+
),
43+
"effective_leverage": format_metric(
44+
calculate_effective_leverage(assets, liabilities),
45+
0 < calculate_effective_leverage(assets, liabilities) < 2,
46+
mode,
47+
),
48+
"all_spot": df[f"{prefix}_all_spot"].sum(),
49+
"all_perp": df[f"{prefix}_all_perp"].sum(),
50+
f"perp_{perp_market_index}_long": df[
51+
f"{prefix}_perp_{perp_market_index}_long"
52+
].sum(),
53+
f"perp_{perp_market_index}_short": df[
54+
f"{prefix}_perp_{perp_market_index}_short"
55+
].sum(),
56+
}
57+
return pd.DataFrame(summary_data).T
58+
59+
60+
def asset_liab_matrix_cached_page():
61+
if "min_leverage" not in st.session_state:
62+
st.session_state.min_leverage = 0.0
63+
if "only_high_leverage_mode_users" not in st.session_state:
64+
st.session_state.only_high_leverage_mode_users = False
65+
2166
params = st.query_params
2267
mode = int(params.get("mode", 0))
2368
perp_market_index = int(params.get("perp_market_index", 0))
2469

2570
mode = st.selectbox(
2671
"Options", options, format_func=lambda x: labels[x], index=options.index(mode)
2772
)
28-
st.query_params.update({"mode": mode})
73+
st.query_params.update({"mode": str(mode)})
2974

3075
perp_market_index = st.selectbox(
3176
"Market index",
3277
[x.market_index for x in mainnet_perp_market_configs],
3378
index=[x.market_index for x in mainnet_perp_market_configs].index(
3479
perp_market_index
3580
),
81+
format_func=lambda x: f"{x} ({mainnet_perp_market_configs[int(x)].symbol})",
3682
)
37-
st.query_params.update({"perp_market_index": perp_market_index})
83+
st.query_params.update({"perp_market_index": str(perp_market_index)})
3884

39-
min_leverage = st.slider(
40-
"Filter by minimum leverage", 0.0, 50.0, 0.0, 0.5, key="min_leverage"
85+
result = api2(
86+
"asset-liability/matrix",
87+
_params={"mode": mode, "perp_market_index": perp_market_index},
88+
key=f"asset-liability/matrix_{mode}_{perp_market_index}",
4189
)
90+
df = pd.DataFrame(result["df"])
4291

43-
try:
44-
result = api(
45-
"asset-liability",
46-
"matrix",
47-
params=params,
48-
as_json=True,
49-
)
50-
if "result" in result and result["result"] == "miss":
51-
st.write("Fetching data for the first time...")
52-
st.image(
53-
"https://i.gifer.com/origin/8a/8a47f769c400b0b7d81a8f6f8e09a44a_w200.gif"
54-
)
55-
st.write("Check again in one minute!")
56-
st.stop()
57-
58-
except Exception as e:
59-
if type(e) == JSONDecodeError:
60-
print("HIT A JSONDecodeError...", e)
61-
st.write("Fetching data for the first time...")
62-
st.image(
63-
"https://i.gifer.com/origin/8a/8a47f769c400b0b7d81a8f6f8e09a44a_w200.gif"
64-
)
65-
st.write("Check again in one minute!")
66-
st.stop()
67-
else:
68-
st.write(e)
69-
st.stop()
92+
if st.session_state.only_high_leverage_mode_users:
93+
df = df[df["is_high_leverage"]]
7094

71-
res = pd.DataFrame(result["res"])
72-
df = pd.DataFrame(result["df"])
95+
filtered_df = df[df["leverage"] >= st.session_state.min_leverage].sort_values(
96+
"leverage", ascending=False
97+
)
7398

74-
st.write(f"{df.shape[0]} users for scenario")
75-
st.write(res)
99+
summary_df = generate_summary_data(filtered_df, mode, perp_market_index)
100+
slot = result["slot"]
101+
current_slot = get_current_slot()
102+
103+
st.info(
104+
f"This data is for slot {slot}, which is now {int(current_slot) - int(slot)} slots old"
105+
)
106+
st.write(f"{df.shape[0]} users")
107+
st.checkbox(
108+
"Only show high leverage mode users", key="only_high_leverage_mode_users"
109+
)
110+
st.slider(
111+
"Filter by minimum leverage",
112+
0.0,
113+
110.0,
114+
0.0,
115+
key="min_leverage",
116+
)
117+
st.write(summary_df)
76118

77119
tabs = st.tabs(["FULL"] + [x.symbol for x in mainnet_spot_market_configs])
78120

79-
# Add leverage filter to FULL tab
80121
with tabs[0]:
81-
filtered_df = df[df["leverage"] >= min_leverage].sort_values(
82-
"leverage", ascending=False
83-
)
122+
if st.session_state.only_high_leverage_mode_users:
123+
st.write(
124+
f"There are **{len(filtered_df)}** users with high leverage mode and {st.session_state.min_leverage}x leverage or more"
125+
)
126+
else:
127+
st.write(
128+
f"There are **{len(filtered_df)}** users with this **{st.session_state.min_leverage}x** leverage or more"
129+
)
130+
st.write(f"Total USD value: **{filtered_df['net_usd_value'].sum():,.2f}**")
131+
st.write(f"Total collateral: **{filtered_df['spot_asset'].sum():,.2f}**")
132+
st.write(f"Total liabilities: **{filtered_df['spot_liability'].sum():,.2f}**")
84133
st.dataframe(filtered_df, hide_index=True)
85134

86135
for idx, tab in enumerate(tabs[1:]):
87-
important_cols = [x for x in df.columns if "spot_" + str(idx) in x]
88-
toshow = df[["spot_asset", "net_usd_value"] + important_cols]
136+
important_cols = [x for x in filtered_df.columns if "spot_" + str(idx) in x]
137+
138+
toshow = filtered_df[
139+
["user_key", "spot_asset", "net_usd_value"] + important_cols
140+
]
89141
toshow = toshow[toshow[important_cols].abs().sum(axis=1) != 0].sort_values(
90142
by="spot_" + str(idx) + "_all", ascending=False
91143
)
92-
tab.write(f"{ len(toshow)} users with this asset to cover liabilities")
144+
tab.write(
145+
f"{len(toshow)} users with this asset to cover liabilities (with {st.session_state.min_leverage}x leverage or more)"
146+
)
93147
tab.dataframe(toshow, hide_index=True)

0 commit comments

Comments
 (0)