Skip to content

Commit 8784e9f

Browse files
committed
Enhance price shock cache generation and API functionality:
- Added support for filtering by pool ID in the price shock API, allowing users to specify which pool's data to retrieve. - Enhanced user metrics calculations to filter users based on the specified pool ID, improving data accuracy. - Updated the frontend to allow users to select a pool ID, with corresponding changes to cache key generation for better data management. - Modified the `generate_ucache.py` script to accept and process the new `pool_id` parameter, ensuring it is included in the API requests. - Updated `gen.sh` to include comprehensive logging for price shock cache generation, detailing asset groups, scenarios, and pool IDs. These changes improve the flexibility and usability of the price shock features, enabling more targeted analysis and reporting.
1 parent 9d25716 commit 8784e9f

File tree

8 files changed

+278
-17
lines changed

8 files changed

+278
-17
lines changed

backend/api/price_shock.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ async def _get_price_shock(
1515
oracle_distortion: float = 0.1,
1616
asset_group: str = PriceShockAssetGroup.IGNORE_STABLES.value,
1717
n_scenarios: int = 5,
18+
pool_id: int = None,
1819
) -> dict:
1920
asset_group = asset_group.replace("+", " ")
2021
price_shock_asset_group = PriceShockAssetGroup(asset_group)
@@ -26,6 +27,7 @@ async def _get_price_shock(
2627
oracle_distortion=oracle_distortion,
2728
asset_group=price_shock_asset_group,
2829
n_scenarios=n_scenarios,
30+
pool_id=pool_id,
2931
)
3032

3133

@@ -35,6 +37,7 @@ async def get_price_shock(
3537
oracle_distortion: float = 0.1,
3638
asset_group: str = PriceShockAssetGroup.IGNORE_STABLES.value,
3739
n_scenarios: int = 5,
40+
pool_id: int = None,
3841
):
3942
return await _get_price_shock(
4043
request.state.backend_state.last_oracle_slot,
@@ -43,4 +46,5 @@ async def get_price_shock(
4346
oracle_distortion,
4447
asset_group,
4548
n_scenarios,
49+
pool_id,
4650
)

backend/scripts/generate_ucache.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,23 +50,33 @@ class PriceShockEndpoint(Endpoint):
5050
asset_group: PriceShockAssetGroup
5151
oracle_distortion: float
5252
n_scenarios: int
53+
pool_id: int
5354

5455
def __init__(
5556
self,
5657
asset_group: PriceShockAssetGroup,
5758
oracle_distortion: float,
5859
n_scenarios: int,
60+
pool_id: int = None,
5961
):
6062
self.asset_group = asset_group
6163
self.oracle_distortion = oracle_distortion
6264
self.n_scenarios = n_scenarios
65+
self.pool_id = pool_id
66+
67+
params = {
68+
"asset_group": asset_group,
69+
"oracle_distortion": oracle_distortion,
70+
"n_scenarios": n_scenarios,
71+
}
72+
73+
# Only add pool_id if it's not None
74+
if pool_id is not None:
75+
params["pool_id"] = pool_id
76+
6377
super().__init__(
6478
"price-shock/usermap",
65-
{
66-
"asset_group": asset_group,
67-
"oracle_distortion": oracle_distortion,
68-
"n_scenarios": n_scenarios,
69-
},
79+
params,
7080
)
7181

7282

@@ -99,6 +109,7 @@ async def mock_call_next(request):
99109
oracle_distortion=query_params["oracle_distortion"],
100110
asset_group=query_params["asset_group"],
101111
n_scenarios=query_params["n_scenarios"],
112+
pool_id=query_params.get("pool_id"),
102113
)
103114

104115
if endpoint == "asset-liability/matrix":
@@ -167,6 +178,7 @@ async def main():
167178
ps_parser.add_argument("--asset-group", type=str, required=True)
168179
ps_parser.add_argument("--oracle-distortion", type=float, required=True)
169180
ps_parser.add_argument("--n-scenarios", type=int, required=True)
181+
ps_parser.add_argument("--pool-id", type=int, help="Filter by pool ID (optional)")
170182

171183
args = parser.parse_args()
172184

@@ -191,6 +203,7 @@ async def main():
191203
asset_group=args.asset_group,
192204
oracle_distortion=args.oracle_distortion,
193205
n_scenarios=args.n_scenarios,
206+
pool_id=getattr(args, 'pool_id', None),
194207
)
195208
)
196209

@@ -203,3 +216,4 @@ async def main():
203216
# Usage example:
204217
# python -m backend.scripts.generate_ucache --use-snapshot asset-liability --mode 0 --perp-market-index 0
205218
# python -m backend.scripts.generate_ucache --use-snapshot price-shock --asset-group "ignore+stables" --oracle-distortion 0.05 --n-scenarios 5
219+
# python -m backend.scripts.generate_ucache --use-snapshot price-shock --asset-group "ignore+stables" --oracle-distortion 0.05 --n-scenarios 5 --pool-id 0

backend/utils/price_shock.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,19 @@ def create_dataframes(leverages):
2121

2222

2323
def calculate_spot_bankruptcies(df):
24+
if df.empty or 'spot_asset' not in df.columns or 'spot_liability' not in df.columns or 'net_usd_value' not in df.columns:
25+
return 0.0
26+
2427
spot_bankrupt = df[
2528
(df["spot_asset"] < df["spot_liability"]) & (df["net_usd_value"] < 0)
2629
]
2730
return (spot_bankrupt["spot_liability"] - spot_bankrupt["spot_asset"]).sum()
2831

2932

3033
def calculate_total_bankruptcies(df):
34+
if df.empty or 'net_usd_value' not in df.columns:
35+
return 0.0
36+
3137
return -df[df["net_usd_value"] < 0]["net_usd_value"].sum()
3238

3339

@@ -46,6 +52,7 @@ def get_price_shock_df(
4652
oracle_distortion: float,
4753
asset_group: PriceShockAssetGroup,
4854
n_scenarios: int,
55+
pool_id: int = None,
4956
):
5057
user_leverages = get_user_leverages_for_price_shock(
5158
slot,
@@ -54,8 +61,35 @@ def get_price_shock_df(
5461
oracle_distortion,
5562
asset_group,
5663
n_scenarios,
64+
pool_id,
5765
)
5866
levs = user_leverages
67+
68+
# Handle case where no users are found after filtering
69+
if not levs["user_keys"] or len(levs["leverages_none"]) == 0:
70+
print(f"No users found for the specified filters. Returning empty result.")
71+
oracle_moves = generate_oracle_moves(n_scenarios, oracle_distortion)
72+
73+
# Create empty DataFrame with all zeros
74+
df_plot = pd.DataFrame(
75+
{
76+
"Oracle Move (%)": oracle_moves,
77+
"Total Bankruptcy ($)": [0.0] * len(oracle_moves),
78+
"Spot Bankruptcy ($)": [0.0] * len(oracle_moves),
79+
"Perpetual Bankruptcy ($)": [0.0] * len(oracle_moves),
80+
}
81+
)
82+
83+
df_plot = df_plot.sort_values("Oracle Move (%)")
84+
85+
return {
86+
"slot": slot,
87+
"result": df_plot.to_json(),
88+
"distorted_oracles": levs["distorted_oracles"],
89+
"oracle_down_max": pd.DataFrame().to_json(),
90+
"oracle_up_max": pd.DataFrame().to_json(),
91+
}
92+
5993
dfs = (
6094
create_dataframes(levs["leverages_down"])
6195
+ [pd.DataFrame(levs["leverages_none"])]

backend/utils/user_metrics.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,9 +260,39 @@ def get_user_leverages_for_price_shock(
260260
oracle_distortion: float = 0.1,
261261
asset_group: PriceShockAssetGroup = PriceShockAssetGroup.IGNORE_STABLES,
262262
scenarios: int = 5,
263+
pool_id: int = None,
263264
):
264265
user_keys = list(user_map.user_map.keys())
265266
user_vals = list(user_map.values())
267+
268+
# Filter users by pool_id if specified
269+
if pool_id is not None:
270+
filtered_user_vals = []
271+
filtered_user_keys = []
272+
pool_id_counts = {} # Track distribution of pool_ids
273+
274+
for user_key, user in zip(user_keys, user_vals):
275+
try:
276+
user_account = user.get_user_account()
277+
actual_pool_id = user_account.pool_id
278+
279+
# Track pool_id distribution for debugging
280+
pool_id_counts[actual_pool_id] = pool_id_counts.get(actual_pool_id, 0) + 1
281+
282+
if actual_pool_id == pool_id:
283+
filtered_user_vals.append(user)
284+
filtered_user_keys.append(user_key)
285+
except Exception as e:
286+
print(f"Error checking pool_id for user {user_key}: {e}")
287+
continue
288+
289+
print(f"Pool ID distribution: {dict(sorted(pool_id_counts.items()))}")
290+
print(f"Requested pool_id: {pool_id}")
291+
print(f"Filtered to {len(filtered_user_vals)} users with pool_id {pool_id} (out of {len(user_vals)} total users)")
292+
293+
user_vals = filtered_user_vals
294+
user_keys = filtered_user_keys
295+
266296
all_configs = mainnet_spot_market_configs + mainnet_perp_market_configs
267297

268298
print(f"User keys : {len(user_keys)}")

gen.sh

Lines changed: 140 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,184 @@
11
#!/bin/bash
22
set -e
33

4-
54
# Run the first one sync, this will generate a fresh pickle
65
python -m backend.scripts.generate_ucache \
76
asset-liability \
87
--mode 0 \
98
--perp-market-index 0
109

11-
1210
# The next ones will use the --use-snapshot flag, so they will reuse the pickle
1311
# We can run all commands in parallel by adding & at the end
12+
13+
echo "Generating comprehensive price shock cache matrix..."
14+
echo "Asset groups: ignore+stables, jlp+only"
15+
echo "Scenarios: 5 (0.05 distortion), 10 (0.1 distortion)"
16+
echo "Pool IDs: all, 0, 1, 3"
17+
echo "Total combinations: 16"
18+
19+
# =============================================================================
20+
# ALL POOLS (no pool filter) - 4 combinations
21+
# =============================================================================
22+
23+
# ignore+stables, 5 scenarios, 0.05 distortion, all pools
1424
python -m backend.scripts.generate_ucache \
1525
--use-snapshot \
1626
price-shock \
1727
--asset-group "ignore+stables" \
1828
--oracle-distortion 0.05 \
1929
--n-scenarios 5 &
2030

31+
# ignore+stables, 10 scenarios, 0.1 distortion, all pools
32+
python -m backend.scripts.generate_ucache \
33+
--use-snapshot \
34+
price-shock \
35+
--asset-group "ignore+stables" \
36+
--oracle-distortion 0.1 \
37+
--n-scenarios 10 &
38+
39+
# jlp+only, 5 scenarios, 0.05 distortion, all pools
2140
python -m backend.scripts.generate_ucache \
2241
--use-snapshot \
2342
price-shock \
2443
--asset-group "jlp+only" \
2544
--oracle-distortion 0.05 \
2645
--n-scenarios 5 &
2746

47+
# jlp+only, 10 scenarios, 0.1 distortion, all pools
2848
python -m backend.scripts.generate_ucache \
2949
--use-snapshot \
3050
price-shock \
3151
--asset-group "jlp+only" \
3252
--oracle-distortion 0.1 \
3353
--n-scenarios 10 &
3454

55+
# =============================================================================
56+
# MAIN POOL (pool_id = 0) - 4 combinations
57+
# =============================================================================
58+
59+
# ignore+stables, 5 scenarios, 0.05 distortion, pool 0
60+
python -m backend.scripts.generate_ucache \
61+
--use-snapshot \
62+
price-shock \
63+
--asset-group "ignore+stables" \
64+
--oracle-distortion 0.05 \
65+
--n-scenarios 5 \
66+
--pool-id 0 &
3567

68+
# ignore+stables, 10 scenarios, 0.1 distortion, pool 0
3669
python -m backend.scripts.generate_ucache \
3770
--use-snapshot \
3871
price-shock \
3972
--asset-group "ignore+stables" \
4073
--oracle-distortion 0.1 \
41-
--n-scenarios 10 &
74+
--n-scenarios 10 \
75+
--pool-id 0 &
76+
77+
# jlp+only, 5 scenarios, 0.05 distortion, pool 0
78+
python -m backend.scripts.generate_ucache \
79+
--use-snapshot \
80+
price-shock \
81+
--asset-group "jlp+only" \
82+
--oracle-distortion 0.05 \
83+
--n-scenarios 5 \
84+
--pool-id 0 &
85+
86+
# jlp+only, 10 scenarios, 0.1 distortion, pool 0
87+
python -m backend.scripts.generate_ucache \
88+
--use-snapshot \
89+
price-shock \
90+
--asset-group "jlp+only" \
91+
--oracle-distortion 0.1 \
92+
--n-scenarios 10 \
93+
--pool-id 0 &
94+
95+
# =============================================================================
96+
# ISOLATED POOL 1 (pool_id = 1) - 4 combinations
97+
# =============================================================================
98+
99+
# ignore+stables, 5 scenarios, 0.05 distortion, pool 1
100+
python -m backend.scripts.generate_ucache \
101+
--use-snapshot \
102+
price-shock \
103+
--asset-group "ignore+stables" \
104+
--oracle-distortion 0.05 \
105+
--n-scenarios 5 \
106+
--pool-id 1 &
107+
108+
# ignore+stables, 10 scenarios, 0.1 distortion, pool 1
109+
python -m backend.scripts.generate_ucache \
110+
--use-snapshot \
111+
price-shock \
112+
--asset-group "ignore+stables" \
113+
--oracle-distortion 0.1 \
114+
--n-scenarios 10 \
115+
--pool-id 1 &
42116

117+
# jlp+only, 5 scenarios, 0.05 distortion, pool 1
118+
python -m backend.scripts.generate_ucache \
119+
--use-snapshot \
120+
price-shock \
121+
--asset-group "jlp+only" \
122+
--oracle-distortion 0.05 \
123+
--n-scenarios 5 \
124+
--pool-id 1 &
125+
126+
# jlp+only, 10 scenarios, 0.1 distortion, pool 1
127+
python -m backend.scripts.generate_ucache \
128+
--use-snapshot \
129+
price-shock \
130+
--asset-group "jlp+only" \
131+
--oracle-distortion 0.1 \
132+
--n-scenarios 10 \
133+
--pool-id 1 &
134+
135+
# =============================================================================
136+
# ISOLATED POOL 3 (pool_id = 3) - 4 combinations
137+
# =============================================================================
138+
139+
# ignore+stables, 5 scenarios, 0.05 distortion, pool 3
140+
python -m backend.scripts.generate_ucache \
141+
--use-snapshot \
142+
price-shock \
143+
--asset-group "ignore+stables" \
144+
--oracle-distortion 0.05 \
145+
--n-scenarios 5 \
146+
--pool-id 3 &
147+
148+
# ignore+stables, 10 scenarios, 0.1 distortion, pool 3
149+
python -m backend.scripts.generate_ucache \
150+
--use-snapshot \
151+
price-shock \
152+
--asset-group "ignore+stables" \
153+
--oracle-distortion 0.1 \
154+
--n-scenarios 10 \
155+
--pool-id 3 &
156+
157+
# jlp+only, 5 scenarios, 0.05 distortion, pool 3
158+
python -m backend.scripts.generate_ucache \
159+
--use-snapshot \
160+
price-shock \
161+
--asset-group "jlp+only" \
162+
--oracle-distortion 0.05 \
163+
--n-scenarios 5 \
164+
--pool-id 3 &
165+
166+
# jlp+only, 10 scenarios, 0.1 distortion, pool 3
167+
python -m backend.scripts.generate_ucache \
168+
--use-snapshot \
169+
price-shock \
170+
--asset-group "jlp+only" \
171+
--oracle-distortion 0.1 \
172+
--n-scenarios 10 \
173+
--pool-id 3 &
174+
175+
# =============================================================================
43176
# Wait for all background processes to complete
177+
# =============================================================================
178+
echo "Waiting for all 16 price shock cache generation processes to complete..."
44179
wait
45180

181+
echo "✅ All cache generation completed successfully!"
182+
46183
# Delete old pickles
47184
cd pickles && ls -t | tail -n +4 | xargs rm -rf

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ uvicorn==0.34.0
1212
requests==2.32.3
1313
plotly==6.0.0
1414
anchorpy==0.21.0
15-
driftpy>=0.8.40
15+
driftpy>=0.8.56
1616
ccxt==4.2.17
1717
rich>=10.14.0
1818
aiofiles==24.1.0

0 commit comments

Comments
 (0)