@@ -19,27 +19,36 @@ def filter_outliers(
19
19
):
20
20
"""Filter out liquidations based on a range multiplier of the market price."""
21
21
return [
22
- (price , notional )
23
- for price , notional in liquidations
22
+ (price , notional , pubkey )
23
+ for price , notional , pubkey in liquidations
24
24
if lower_bound_multiplier * market_price_ui
25
25
<= price
26
26
<= upper_bound_multiplier * market_price_ui
27
27
]
28
28
29
29
def aggregate_liquidations (liquidations ):
30
- """Aggregate liquidations to calculate cumulative notional amounts."""
31
- price_to_notional = defaultdict (float )
32
- for price , notional in liquidations :
33
- price_to_notional [price ] += notional
34
- return price_to_notional
30
+ """Aggregate liquidations to calculate cumulative notional amounts and track pubkeys with their sizes."""
31
+ price_to_data = defaultdict (lambda : {"notional" : 0.0 , "positions" : []})
32
+ for price , notional , pubkey in liquidations :
33
+ price_to_data [price ]["notional" ] += notional
34
+ price_to_data [price ]["positions" ].append (
35
+ (pubkey , notional )
36
+ ) # Store tuple of (pubkey, size)
37
+ return price_to_data
35
38
36
39
def prepare_data_for_plot (aggregated_data , reverse = False ):
37
40
"""Prepare and sort data for plotting, optionally reversing the cumulative sum for descending plots."""
38
41
sorted_prices = sorted (aggregated_data .keys (), reverse = reverse )
39
42
cumulative_notional = np .cumsum (
40
- [aggregated_data [price ] for price in sorted_prices ]
43
+ [aggregated_data [price ][ "notional" ] for price in sorted_prices ]
41
44
)
42
- return sorted_prices , cumulative_notional
45
+ # Accumulate positions (pubkey and size) for each price point
46
+ cumulative_positions = []
47
+ current_positions = []
48
+ for price in sorted_prices :
49
+ current_positions .extend (aggregated_data [price ]["positions" ])
50
+ cumulative_positions .append (list (current_positions ))
51
+ return sorted_prices , cumulative_notional , cumulative_positions
43
52
44
53
# Filter outliers based on defined criteria
45
54
liquidations_long = filter_outliers (
@@ -53,10 +62,12 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
53
62
aggregated_long = aggregate_liquidations (liquidations_long )
54
63
aggregated_short = aggregate_liquidations (liquidations_short )
55
64
56
- long_prices , long_cum_notional = prepare_data_for_plot (
65
+ long_prices , long_cum_notional , long_pubkeys = prepare_data_for_plot (
57
66
aggregated_long , reverse = True
58
67
)
59
- short_prices , short_cum_notional = prepare_data_for_plot (aggregated_short )
68
+ short_prices , short_cum_notional , short_pubkeys = prepare_data_for_plot (
69
+ aggregated_short
70
+ )
60
71
61
72
if not long_prices or not short_prices :
62
73
return None
@@ -73,6 +84,8 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
73
84
mode = "lines" ,
74
85
name = "Long Positions" ,
75
86
line = dict (color = "purple" , width = 2 ),
87
+ hovertemplate = "Price: %{x}<br>Cumulative Notional: %{y}<br>Accounts: %{text}<extra></extra>" ,
88
+ text = [f"{ len (pubkeys )} accounts" for pubkeys in long_pubkeys ],
76
89
)
77
90
)
78
91
short_fig .add_trace (
@@ -82,6 +95,8 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
82
95
mode = "lines" ,
83
96
name = "Short Positions" ,
84
97
line = dict (color = "turquoise" , width = 2 ),
98
+ hovertemplate = "Price: %{x}<br>Cumulative Notional: %{y}<br>Accounts: %{text}<extra></extra>" ,
99
+ text = [f"{ len (pubkeys )} accounts" for pubkeys in short_pubkeys ],
85
100
)
86
101
)
87
102
@@ -102,7 +117,7 @@ def prepare_data_for_plot(aggregated_data, reverse=False):
102
117
yaxis = dict (showgrid = True ),
103
118
)
104
119
105
- return long_fig , short_fig
120
+ return long_fig , short_fig , long_pubkeys , short_pubkeys , long_prices , short_prices
106
121
107
122
108
123
def liquidation_curves_page ():
@@ -140,12 +155,86 @@ def liquidation_curves_page():
140
155
st .write (e )
141
156
st .stop ()
142
157
143
- (long_fig , short_fig ) = plot_liquidation_curves (liquidation_data )
158
+ # Unpack all returned values
159
+ (long_fig , short_fig , long_pubkeys , short_pubkeys , long_prices , short_prices ) = (
160
+ plot_liquidation_curves (liquidation_data )
161
+ )
144
162
145
163
long_col , short_col = st .columns ([1 , 1 ])
146
164
147
165
with long_col :
148
166
st .plotly_chart (long_fig , use_container_width = True )
149
167
168
+ # Add accordion for long positions
169
+ with st .expander ("Long Position Accounts" ):
170
+ if long_pubkeys and len (long_pubkeys [- 1 ]) > 0 :
171
+ st .write (f"Total Accounts: { len (long_pubkeys [- 1 ])} " )
172
+ long_data = []
173
+ for i , positions in enumerate (long_pubkeys ):
174
+ if i > 0 :
175
+ new_positions = set (positions ) - set (long_pubkeys [i - 1 ])
176
+ if new_positions :
177
+ for pubkey , size in new_positions :
178
+ long_data .append (
179
+ {
180
+ "Price" : f"{ long_prices [i ]:.2f} " ,
181
+ "Size" : f"{ size :,.2f} " ,
182
+ "Account" : pubkey ,
183
+ "Link" : f"https://app.drift.trade/overview?userAccount={ pubkey } " ,
184
+ }
185
+ )
186
+ if long_data :
187
+ st .dataframe (
188
+ long_data ,
189
+ column_config = {
190
+ "Price" : st .column_config .TextColumn ("Price" ),
191
+ "Size" : st .column_config .TextColumn ("Size" ),
192
+ "Account" : st .column_config .TextColumn (
193
+ "Account" , width = "large"
194
+ ),
195
+ "Link" : st .column_config .LinkColumn (
196
+ "Link" , display_text = "View"
197
+ ),
198
+ },
199
+ hide_index = True ,
200
+ )
201
+ else :
202
+ st .write ("No long positions found" )
203
+
150
204
with short_col :
151
205
st .plotly_chart (short_fig , use_container_width = True )
206
+
207
+ with st .expander ("Short Position Accounts" ):
208
+ if short_pubkeys and len (short_pubkeys [- 1 ]) > 0 :
209
+ st .write (f"Total Accounts: { len (short_pubkeys [- 1 ])} " )
210
+ short_data = []
211
+ for i , positions in enumerate (short_pubkeys ):
212
+ if i > 0 :
213
+ new_positions = set (positions ) - set (short_pubkeys [i - 1 ])
214
+ if new_positions :
215
+ for pubkey , size in new_positions :
216
+ short_data .append (
217
+ {
218
+ "Price" : f"{ short_prices [i ]:.2f} " ,
219
+ "Size" : f"{ size :,.2f} " ,
220
+ "Account" : pubkey ,
221
+ "Link" : f"https://app.drift.trade/overview?userAccount={ pubkey } " ,
222
+ }
223
+ )
224
+ if short_data :
225
+ st .dataframe (
226
+ short_data ,
227
+ column_config = {
228
+ "Price" : st .column_config .TextColumn ("Price" ),
229
+ "Size" : st .column_config .TextColumn ("Size" ),
230
+ "Account" : st .column_config .TextColumn (
231
+ "Account" , width = "large"
232
+ ),
233
+ "Link" : st .column_config .LinkColumn (
234
+ "Link" , display_text = "View"
235
+ ),
236
+ },
237
+ hide_index = True ,
238
+ )
239
+ else :
240
+ st .write ("No short positions found" )
0 commit comments