1
1
import streamlit as st
2
2
import pandas as pd
3
+ import time # Added import
3
4
from lib .api import fetch_api_data
4
5
from utils import get_current_slot
5
6
7
+ RETRY_DELAY_SECONDS = 5 # Define delay for auto-refresh
8
+
9
+ def is_processing (result ):
10
+ """Checks if the API result indicates backend processing."""
11
+ # Check if result is a dictionary and has the specific processing structure
12
+ return isinstance (result , dict ) and result .get ("result" ) == "processing"
13
+
14
+ def has_error (result ):
15
+ """Checks if the API result indicates an error."""
16
+ # Check if result is None or a dictionary with an error message
17
+ return result is None or (isinstance (result , dict ) and result .get ("result" ) == "error" )
18
+
6
19
def open_interest_page ():
7
- st .title ("Open Interest per Authority " )
20
+ st .title ("Open Interest on Drift " )
8
21
9
22
try :
10
- # Fetch data from the API endpoint directly
11
- result = fetch_api_data (
23
+ # --- 1. Fetch all data ---
24
+ result_authority = fetch_api_data (
12
25
section = "open-interest" ,
13
26
path = "per-authority" ,
14
- retry = True # Enable retry for cache misses/processing states
27
+ retry = False # Let the page handle retries via rerun
28
+ )
29
+ result_account = fetch_api_data (
30
+ section = "open-interest" ,
31
+ path = "per-account" ,
32
+ retry = False
33
+ )
34
+ result_detailed = fetch_api_data (
35
+ section = "open-interest" ,
36
+ path = "detailed-positions" ,
37
+ retry = False
15
38
)
39
+
40
+ # --- 2. Check for processing state ---
41
+ if is_processing (result_authority ) or is_processing (result_account ) or is_processing (result_detailed ):
42
+ st .info (f"Backend is processing open interest data. Auto-refreshing in { RETRY_DELAY_SECONDS } seconds..." )
43
+ # Optionally add more details about which endpoint is processing if needed
44
+ # e.g., if is_processing(result_authority): st.caption("Authority data processing...")
45
+ with st .spinner ("Please wait..." ):
46
+ time .sleep (RETRY_DELAY_SECONDS )
47
+ st .rerun () # Rerun the script to fetch again
48
+ return # Stop further execution in this run
49
+
50
+ # --- 3. Check for errors ---
51
+ # Check critical data source first (e.g., authority for total OI metric)
52
+ if has_error (result_authority ):
53
+ error_msg = result_authority ['message' ] if isinstance (result_authority , dict ) else "Could not connect or fetch data."
54
+ st .error (f"Failed to fetch essential data (OI by Authority): { error_msg } " )
55
+ if st .button ("Retry Fetch" ):
56
+ # Optionally clear streamlit cache if fetch_api_data uses it
57
+ # st.cache_data.clear()
58
+ st .rerun ()
59
+ return # Stop execution
60
+
61
+ # Handle potential errors in other non-critical fetches (optional: display warnings instead of stopping)
62
+ if has_error (result_account ):
63
+ st .warning ("Could not fetch OI by Account data. This table will be empty." )
64
+ # Proceed without account data
16
65
17
- if result is None :
18
- st .error ( "Failed to fetch data from the API ." )
19
- return
66
+ if has_error ( result_detailed ) :
67
+ st .warning ( "Could not fetch Detailed Open Positions data. This table will be empty ." )
68
+ # Proceed without detailed data
20
69
21
- data = result .get ("data" , [])
22
- slot = result .get ("slot" , "N/A" )
23
- current_slot = get_current_slot ()
70
+ # --- 4. Extract data and slot (if all checks passed) ---
71
+ # We know result_authority is valid here
72
+ data_authority = result_authority .get ("data" , [])
73
+ slot = result_authority .get ("slot" , "N/A" )
74
+
75
+ # Extract data for others, defaulting to empty list if fetch failed (handled by warnings above)
76
+ data_account = result_account .get ("data" , []) if isinstance (result_account , dict ) else []
77
+ data_detailed = result_detailed .get ("data" , []) if isinstance (result_detailed , dict ) else []
24
78
79
+ # --- 5. Display Slot Info ---
80
+ current_slot = get_current_slot ()
25
81
if slot != "N/A" and current_slot :
26
82
try :
27
83
slot_age = int (current_slot ) - int (slot )
@@ -31,31 +87,84 @@ def open_interest_page():
31
87
else :
32
88
st .info (f"Slot information unavailable. Current slot: { current_slot } " )
33
89
34
- if not data :
35
- st .warning ("No open interest data found." )
36
- return
37
-
38
- df = pd .DataFrame (data )
39
-
40
- if df .empty :
41
- st .warning ("No open interest data to display." )
42
- return
43
-
44
- # Rename columns for better readability
45
- df .rename (columns = {
46
- 'authority' : 'User Authority' ,
47
- 'total_open_interest_usd' : 'Total Open Interest (USD)'
48
- }, inplace = True )
49
- # Reorder columns
50
- df = df [["User Authority" , "Total Open Interest (USD)" ]]
51
- # Format USD column with dollar sign and commas
52
- df ["Total Open Interest (USD)" ] = df ["Total Open Interest (USD)" ].apply (lambda x : f"${ x :,.2f} " )
90
+ # --- 6. Process and Prepare DataFrames ---
91
+ df_authority = pd .DataFrame ()
92
+ if data_authority :
93
+ df_authority = pd .DataFrame (data_authority )
94
+ if not df_authority .empty :
95
+ df_authority .rename (columns = {
96
+ 'authority' : 'User Authority' ,
97
+ 'total_open_interest_usd' : 'Total Open Interest (USD)'
98
+ }, inplace = True )
99
+ df_authority = df_authority [["User Authority" , "Total Open Interest (USD)" ]]
100
+ df_authority ["Total Open Interest (USD)" ] = df_authority ["Total Open Interest (USD)" ].apply (lambda x : f"${ x :,.2f} " )
53
101
54
- st .metric ("Total Authorities with Open Interest" , len (df ))
55
- st .metric ("Total Open Interest (USD)" , f"{ df ['Total Open Interest (USD)' ].str .replace ('$' ,'' ).str .replace (',' ,'' ).astype (float ).sum ():,.2f} " )
102
+ df_account = pd .DataFrame ()
103
+ if data_account :
104
+ df_account = pd .DataFrame (data_account )
105
+ if not df_account .empty :
106
+ df_account .rename (columns = {
107
+ 'user_public_key' : 'User Account' ,
108
+ 'authority' : 'Authority' ,
109
+ 'total_open_interest_usd' : 'Total Open Interest (USD)'
110
+ }, inplace = True )
111
+ df_account = df_account [['User Account' , 'Authority' , 'Total Open Interest (USD)' ]]
112
+ df_account ["Total Open Interest (USD)" ] = df_account ["Total Open Interest (USD)" ].apply (lambda x : f"${ x :,.2f} " )
113
+
114
+ df_detailed = pd .DataFrame ()
115
+ if data_detailed :
116
+ df_detailed = pd .DataFrame (data_detailed )
117
+ if not df_detailed .empty :
118
+ df_detailed .rename (columns = {
119
+ 'market_index' : 'Market Index' ,
120
+ 'market_symbol' : 'Market Symbol' ,
121
+ 'base_asset_amount_ui' : 'Base Asset Amount' ,
122
+ 'position_value_usd' : 'Notional Value (USD)' ,
123
+ 'user_public_key' : 'User Account' ,
124
+ 'authority' : 'Authority'
125
+ }, inplace = True )
126
+ df_detailed = df_detailed [[
127
+ 'Market Index' , 'Market Symbol' , 'Base Asset Amount' , 'Notional Value (USD)' ,
128
+ 'User Account' , 'Authority'
129
+ ]]
130
+ df_detailed ['Base Asset Amount' ] = df_detailed ['Base Asset Amount' ].apply (lambda x : f"{ x :,.4f} " )
131
+ df_detailed ['Notional Value (USD)' ] = df_detailed ['Notional Value (USD)' ].apply (lambda x : f"${ x :,.2f} " )
132
+
133
+ # --- 7. Display Layout (Metrics and DataFrames) ---
134
+ total_oi_usd = 0.0
135
+ if not df_authority .empty :
136
+ try :
137
+ # Calculate metric only if df_authority is valid and processed
138
+ total_oi_usd = df_authority ['Total Open Interest (USD)' ].str .replace ('[$,]' ,'' , regex = True ).astype (float ).sum ()
139
+ except Exception as calc_e :
140
+ st .warning (f"Could not calculate Total OI metric: { calc_e } " )
56
141
57
- st .subheader ("Open Interest Details" )
58
- st .dataframe (df , hide_index = True )
142
+ col1 , col2 = st .columns (2 )
143
+ with col1 :
144
+ st .metric ("Total Authorities with Open Interest" , len (df_authority ) if not df_authority .empty else 0 )
145
+ with col2 :
146
+ st .metric ("Total Open Interest (USD)" , f"${ total_oi_usd :,.2f} " )
147
+
148
+ col3 , col4 = st .columns (2 )
149
+ with col3 :
150
+ st .subheader ("OI by Authority" )
151
+ if not df_authority .empty :
152
+ st .dataframe (df_authority , hide_index = True )
153
+ else :
154
+ st .info ("Authority data not available or empty." )
155
+
156
+ with col4 :
157
+ st .subheader ("OI by Account" )
158
+ if not df_account .empty :
159
+ st .dataframe (df_account , hide_index = True )
160
+ else :
161
+ st .info ("Account data not available or empty." )
162
+
163
+ st .subheader ("Detailed Open Positions" )
164
+ if not df_detailed .empty :
165
+ st .dataframe (df_detailed , hide_index = True , use_container_width = True )
166
+ else :
167
+ st .info ("Detailed position data not available or empty." )
59
168
60
169
except Exception as e :
61
170
st .error (f"An error occurred while displaying the page: { e } " )
0 commit comments