Skip to content

Commit 07f6b25

Browse files
authored
Merge pull request #40 from drift-labs:goldhaxx/DATA-73/add-filter-for-each-isolated-pool-on-price-shock-page
goldhaxx/DATA-73/add-filter-for-each-isolated-pool-on-price-shock-page resolved some package dependency issues
2 parents 66e8b91 + 579655f commit 07f6b25

File tree

4 files changed

+185
-39
lines changed

4 files changed

+185
-39
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,5 @@ supervisord.pid
4444
# Exchange data
4545
exchange_data/*
4646
list_recommender.log
47+
48+
.testvenv

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
aiohttp==3.8.3
1+
aiohttp>=3.9.1,<4.0.0
22
fastapi==0.115.8
33
gunicorn==23.0.0
44
matplotlib==3.10.0

src/page/market_inspector.py

Lines changed: 98 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -340,12 +340,35 @@ def market_inspector_page():
340340
available_attrs = get_perp_market_attributes()
341341

342342
# 3) Let user select which market
343-
selected_market = st.selectbox(
343+
market_indices = [m.data.market_index for m in markets]
344+
345+
if not market_indices:
346+
st.warning(f"No {market_type_choice} markets available to display.")
347+
# Ensure selected_attrs is cleared if no markets are available for the chosen type
348+
if "selected_attrs" in st.session_state: # Reset selected attributes if market type changes or no markets
349+
st.session_state.selected_attrs = []
350+
return # Stop further processing for this render if no markets
351+
352+
market_lookup = {m.data.market_index: m for m in markets}
353+
354+
# Use a unique key for the selectbox based on market_type_choice
355+
# to ensure it resets if the options change significantly (e.g., switching market types)
356+
selectbox_key = f"market_selectbox_{market_type_choice}"
357+
358+
selected_market_index = st.selectbox(
344359
"Select Market:",
345-
markets,
346-
format_func=display_name,
360+
market_indices,
361+
format_func=lambda index: display_name(market_lookup[index]),
362+
key=selectbox_key
347363
)
348364

365+
selected_market = market_lookup.get(selected_market_index) # Reassign selected_market
366+
367+
if not selected_market:
368+
# This case should ideally not be hit if market_indices is not empty and selectbox works as expected
369+
st.error("Failed to retrieve selected market data. Please ensure a market is selected or try refreshing.")
370+
return
371+
349372
# 4) Let user pick which attributes to show (multi-select)
350373
st.write("Select which attributes you would like to see:")
351374

@@ -378,18 +401,44 @@ def on_attribute_selection_change():
378401
# 5) Display results
379402
if not selected_attrs:
380403
st.info("Please select at least one attribute to display.")
381-
with st.expander("All markets"):
404+
with st.expander("All markets (Serialized Data)"): # Clarified title
382405
st.write("Perp markets:")
383-
perp_markets = sorted(perp_market_map.values(), key=lambda m: m.data.market_index)
384-
perps = pd.concat([pd.DataFrame(serialize_perp_market(x.data)).T for x in perp_markets], axis=1)
385-
perps.columns = [m.data.market_index for m in perp_markets]
386-
st.write(perps)
406+
perp_markets_list = sorted(perp_market_map.values(), key=lambda m: m.data.market_index)
407+
if perp_markets_list:
408+
try:
409+
perp_data_for_df = [serialize_perp_market(m.data) for m in perp_markets_list]
410+
if perp_data_for_df: # Ensure list is not empty before concat
411+
df_perp = pd.concat(perp_data_for_df, axis=0).reset_index(drop=True)
412+
if 'market_index' in df_perp.columns:
413+
# Ensure market_index is the first column for better readability
414+
df_perp = df_perp[['market_index'] + [col for col in df_perp.columns if col != 'market_index']]
415+
st.dataframe(df_perp)
416+
else:
417+
st.write("No data to display for perp markets.")
418+
except Exception as e:
419+
st.error(f"Error displaying perp markets table: {e}")
420+
st.caption("Raw data might contain non-serializable fields or other issues.")
421+
else:
422+
st.write("No perp markets found.")
387423

388424
st.write("Spot markets:")
389-
spot_markets = sorted(spot_market_map.values(), key=lambda m: m.data.market_index)
390-
spots = pd.concat([pd.DataFrame(serialize_spot_market(x.data)).T for x in spot_markets], axis=1)
391-
spots.columns = [m.data.market_index for m in spot_markets]
392-
st.write(spots)
425+
spot_markets_list = sorted(spot_market_map.values(), key=lambda m: m.data.market_index)
426+
if spot_markets_list:
427+
try:
428+
spot_data_for_df = [serialize_spot_market(m.data) for m in spot_markets_list]
429+
if spot_data_for_df: # Ensure list is not empty before concat
430+
df_spot = pd.concat(spot_data_for_df, axis=0).reset_index(drop=True)
431+
if 'market_index' in df_spot.columns:
432+
# Ensure market_index is the first column for better readability
433+
df_spot = df_spot[['market_index'] + [col for col in df_spot.columns if col != 'market_index']]
434+
st.dataframe(df_spot)
435+
else:
436+
st.write("No data to display for spot markets.")
437+
except Exception as e:
438+
st.error(f"Error displaying spot markets table: {e}")
439+
st.caption("Raw data might contain non-serializable fields or other issues.")
440+
else:
441+
st.write("No spot markets found.")
393442
return
394443

395444
st.write(f"**Market Index:** {selected_market.data.market_index}")
@@ -440,8 +489,42 @@ def on_attribute_selection_change():
440489
# Wrap in code block
441490
st.markdown(f"```\n{formatted_line}\n```")
442491

443-
with st.expander("All markets"):
492+
with st.expander("All markets (Serialized Data)"): # Clarified title and reused logic
444493
st.write("Perp markets:")
445-
st.write(pd.DataFrame(sorted(perp_market_map.values(), key=lambda m: m.data.market_index)))
494+
# Use a different variable name to avoid conflicts if any part of the script is re-run in a weird way
495+
perp_markets_list_bottom = sorted(perp_market_map.values(), key=lambda m: m.data.market_index)
496+
if perp_markets_list_bottom:
497+
try:
498+
perp_data_for_df_bottom = [serialize_perp_market(m.data) for m in perp_markets_list_bottom]
499+
if perp_data_for_df_bottom: # Ensure list is not empty before concat
500+
df_perp_bottom = pd.concat(perp_data_for_df_bottom, axis=0).reset_index(drop=True)
501+
if 'market_index' in df_perp_bottom.columns:
502+
# Ensure market_index is the first column
503+
df_perp_bottom = df_perp_bottom[['market_index'] + [col for col in df_perp_bottom.columns if col != 'market_index']]
504+
st.dataframe(df_perp_bottom)
505+
else:
506+
st.write("No data to display for perp markets.")
507+
except Exception as e:
508+
st.error(f"Error displaying perp markets table (bottom): {e}")
509+
st.caption("Raw data might contain non-serializable fields or other issues.")
510+
else:
511+
st.write("No perp markets found.")
512+
446513
st.write("Spot markets:")
447-
st.write(pd.DataFrame(sorted(spot_market_map.values(), key=lambda m: m.data.market_index)))
514+
spot_markets_list_bottom = sorted(spot_market_map.values(), key=lambda m: m.data.market_index)
515+
if spot_markets_list_bottom:
516+
try:
517+
spot_data_for_df_bottom = [serialize_spot_market(m.data) for m in spot_markets_list_bottom]
518+
if spot_data_for_df_bottom: # Ensure list is not empty before concat
519+
df_spot_bottom = pd.concat(spot_data_for_df_bottom, axis=0).reset_index(drop=True)
520+
if 'market_index' in df_spot_bottom.columns:
521+
# Ensure market_index is the first column
522+
df_spot_bottom = df_spot_bottom[['market_index'] + [col for col in df_spot_bottom.columns if col != 'market_index']]
523+
st.dataframe(df_spot_bottom)
524+
else:
525+
st.write("No data to display for spot markets.")
526+
except Exception as e:
527+
st.error(f"Error displaying spot markets table (bottom): {e}")
528+
st.caption("Raw data might contain non-serializable fields or other issues.")
529+
else:
530+
st.write("No spot markets found.")

src/utils.py

Lines changed: 84 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import datetime
22
import os
3-
from typing import Optional
3+
from typing import Optional, Any, Union
44
import pandas as pd
5+
import copy # Added for deepcopy
56

67
import requests
78
from driftpy.decode.utils import decode_name
@@ -19,10 +20,37 @@
1920
WebsocketConfig as UserMapWebsocketConfig,
2021
)
2122
from driftpy.user_map.userstats_map import UserStatsMap
23+
from solders.pubkey import Pubkey # Import Pubkey
2224

2325
from dotenv import load_dotenv
2426
load_dotenv()
2527

28+
# Helper function to stringify sumtypes, Pubkeys, and other problematic types
29+
def _stringify_value(value: Any) -> Any:
30+
if hasattr(value, 'kind') and isinstance(value.kind, str):
31+
# Common pattern for driftpy sumtypes (e.g., MarketType, OracleSource)
32+
return value.kind
33+
elif (hasattr(value, '__class__') and
34+
hasattr(value.__class__, '__module__') and
35+
'sumtypes' in value.__class__.__module__):
36+
# Another pattern for sumtypes (e.g., MarketStatus)
37+
return value.__class__.__name__
38+
elif isinstance(value, Pubkey):
39+
return str(value)
40+
elif isinstance(value, list):
41+
return [_stringify_value(item) for item in value]
42+
elif isinstance(value, dict):
43+
return {k: _stringify_value(v) for k, v in value.items()}
44+
# Add other specific type checks if needed, e.g., for specific complex objects
45+
# that are not sumtypes but still cause issues with Arrow.
46+
return value
47+
48+
def _prepare_for_serialization(obj_dict: dict) -> dict:
49+
prepared_dict = {}
50+
for key, value in obj_dict.items():
51+
prepared_dict[key] = _stringify_value(value)
52+
return prepared_dict
53+
2654
def to_financial(num):
2755
num_str = str(num)
2856
decimal_pos = num_str.find(".")
@@ -260,61 +288,94 @@ def human_amm_df(df):
260288

261289

262290
def serialize_perp_market(market: PerpMarketAccount):
263-
264-
market_df = pd.json_normalize(market.__dict__).drop(['amm', 'insurance_claim', 'pnl_pool'],axis=1).pipe(human_market_df)
265-
market_df['pubkey'] = str(market.pubkey)
266-
market_df['name'] = decode_name(market.name)
291+
# Prepare market data by stringifying sumtypes and Pubkeys
292+
market_dict_prepared = _prepare_for_serialization(copy.deepcopy(market.__dict__))
293+
amm_dict_prepared = _prepare_for_serialization(copy.deepcopy(market.amm.__dict__))
294+
hist_oracle_data_prepared = _prepare_for_serialization(copy.deepcopy(market.amm.historical_oracle_data.__dict__))
295+
fee_pool_prepared = _prepare_for_serialization(copy.deepcopy(market.amm.fee_pool.__dict__))
296+
insurance_claim_prepared = _prepare_for_serialization(copy.deepcopy(market.insurance_claim.__dict__))
297+
pnl_pool_prepared = _prepare_for_serialization(copy.deepcopy(market.pnl_pool.__dict__))
298+
299+
market_df = pd.json_normalize(market_dict_prepared).drop(['amm', 'insurance_claim', 'pnl_pool'],axis=1, errors='ignore').pipe(human_market_df)
300+
# 'name' is bytes, decode_name handles it; 'pubkey' is already stringified by _prepare_for_serialization if it was a Pubkey object
301+
if 'name' in market_df.columns and market_dict_prepared.get('name'): # Check if name exists before decoding
302+
market_df['name'] = decode_name(market_dict_prepared['name']) # Use original bytes for decode_name
267303
market_df.columns = ['market.'+col for col in market_df.columns]
268304

269-
amm_df= pd.json_normalize(market.amm.__dict__).drop(['historical_oracle_data', 'fee_pool'],axis=1).pipe(human_amm_df)
305+
amm_df= pd.json_normalize(amm_dict_prepared).drop(['historical_oracle_data', 'fee_pool'],axis=1, errors='ignore').pipe(human_amm_df)
270306
amm_df.columns = ['market.amm.'+col for col in amm_df.columns]
271307

272-
amm_hist_oracle_df= pd.json_normalize(market.amm.historical_oracle_data.__dict__).pipe(human_amm_df)
308+
amm_hist_oracle_df= pd.json_normalize(hist_oracle_data_prepared).pipe(human_amm_df)
273309
amm_hist_oracle_df.columns = ['market.amm.historical_oracle_data.'+col for col in amm_hist_oracle_df.columns]
274310

275-
market_amm_pool_df = pd.json_normalize(market.amm.fee_pool.__dict__).pipe(human_amm_df)
311+
market_amm_pool_df = pd.json_normalize(fee_pool_prepared).pipe(human_amm_df)
276312
market_amm_pool_df.columns = ['market.amm.fee_pool.'+col for col in market_amm_pool_df.columns]
277313

278-
market_if_df = pd.json_normalize(market.insurance_claim.__dict__).pipe(human_market_df)
314+
market_if_df = pd.json_normalize(insurance_claim_prepared).pipe(human_market_df)
279315
market_if_df.columns = ['market.insurance_claim.'+col for col in market_if_df.columns]
280316

281-
market_pool_df = pd.json_normalize(market.pnl_pool.__dict__).pipe(human_amm_df)
317+
market_pool_df = pd.json_normalize(pnl_pool_prepared).pipe(human_amm_df)
282318
market_pool_df.columns = ['market.pnl_pool.'+col for col in market_pool_df.columns]
283319

284320
result_df = pd.concat([market_df, amm_df, amm_hist_oracle_df, market_amm_pool_df, market_if_df, market_pool_df],axis=1)
321+
322+
# Final conversion of object columns to string for Arrow compatibility
323+
for col in result_df.columns:
324+
if result_df[col].dtype == 'object':
325+
try:
326+
result_df[col] = result_df[col].astype(str)
327+
except Exception:
328+
# Fallback if astype(str) fails for any reason on a column
329+
result_df[col] = result_df[col].apply(lambda x: str(x) if pd.notnull(x) else x)
285330
return result_df
286331

287332

288333
def serialize_spot_market(spot_market: SpotMarketAccount):
289-
spot_market_df = pd.json_normalize(spot_market.__dict__).drop([
334+
# Prepare spot_market data
335+
spot_market_dict_prepared = _prepare_for_serialization(copy.deepcopy(spot_market.__dict__))
336+
insurance_fund_prepared = _prepare_for_serialization(copy.deepcopy(spot_market.insurance_fund.__dict__))
337+
hist_oracle_data_prepared = _prepare_for_serialization(copy.deepcopy(spot_market.historical_oracle_data.__dict__))
338+
hist_index_data_prepared = _prepare_for_serialization(copy.deepcopy(spot_market.historical_index_data.__dict__))
339+
revenue_pool_prepared = _prepare_for_serialization(copy.deepcopy(spot_market.revenue_pool.__dict__))
340+
spot_fee_pool_prepared = _prepare_for_serialization(copy.deepcopy(spot_market.spot_fee_pool.__dict__))
341+
342+
spot_market_df = pd.json_normalize(spot_market_dict_prepared).drop([
290343
'historical_oracle_data', 'historical_index_data',
291-
'insurance_fund', # todo
344+
'insurance_fund',
292345
'spot_fee_pool', 'revenue_pool'
293-
], axis=1).pipe(human_amm_df)
294-
spot_market_df['name'] = decode_name(spot_market.name)
295-
spot_market_df['pubkey'] = str(spot_market.pubkey)
296-
spot_market_df['oracle'] = str(spot_market.oracle)
297-
spot_market_df['mint'] = str(spot_market.mint)
298-
spot_market_df['vault'] = str(spot_market.vault)
346+
], axis=1, errors='ignore').pipe(human_amm_df) # Note: using human_amm_df as per original
347+
348+
# 'name' is bytes, decode_name handles it. Other Pubkey fields are stringified.
349+
if 'name' in spot_market_df.columns and spot_market_dict_prepared.get('name'):
350+
spot_market_df['name'] = decode_name(spot_market_dict_prepared['name']) # Use original bytes
299351

300352
spot_market_df.columns = ['spot_market.'+col for col in spot_market_df.columns]
301353

302-
if_df= pd.json_normalize(spot_market.insurance_fund.__dict__).pipe(human_amm_df)
354+
if_df= pd.json_normalize(insurance_fund_prepared).pipe(human_amm_df)
303355
if_df.columns = ['spot_market.insurance_fund.'+col for col in if_df.columns]
304356

305-
hist_oracle_df= pd.json_normalize(spot_market.historical_oracle_data.__dict__).pipe(human_amm_df)
357+
hist_oracle_df= pd.json_normalize(hist_oracle_data_prepared).pipe(human_amm_df)
306358
hist_oracle_df.columns = ['spot_market.historical_oracle_data.'+col for col in hist_oracle_df.columns]
307359

308-
hist_index_df= pd.json_normalize(spot_market.historical_index_data.__dict__).pipe(human_amm_df)
360+
hist_index_df= pd.json_normalize(hist_index_data_prepared).pipe(human_amm_df)
309361
hist_index_df.columns = ['spot_market.historical_index_data.'+col for col in hist_index_df.columns]
310362

311363

312-
market_pool_df = pd.json_normalize(spot_market.revenue_pool.__dict__).pipe(human_amm_df)
364+
market_pool_df = pd.json_normalize(revenue_pool_prepared).pipe(human_amm_df)
313365
market_pool_df.columns = ['spot_market.revenue_pool.'+col for col in market_pool_df.columns]
314366

315367

316-
market_fee_df = pd.json_normalize(spot_market.spot_fee_pool.__dict__).pipe(human_amm_df)
368+
market_fee_df = pd.json_normalize(spot_fee_pool_prepared).pipe(human_amm_df)
317369
market_fee_df.columns = ['spot_market.spot_fee_pool.'+col for col in market_fee_df.columns]
318370

319371
result_df = pd.concat([spot_market_df, if_df, hist_oracle_df, hist_index_df, market_pool_df, market_fee_df],axis=1)
372+
373+
# Final conversion of object columns to string for Arrow compatibility
374+
for col in result_df.columns:
375+
if result_df[col].dtype == 'object':
376+
try:
377+
result_df[col] = result_df[col].astype(str)
378+
except Exception:
379+
# Fallback if astype(str) fails for any reason on a column
380+
result_df[col] = result_df[col].apply(lambda x: str(x) if pd.notnull(x) else x)
320381
return result_df

0 commit comments

Comments
 (0)