@@ -194,15 +194,21 @@ def get_largest_perp_positions(request: BackendRequest, number_of_positions: int
194
194
195
195
196
196
@router .get ("/most_levered_perp_positions_above_1m" )
197
- def get_most_levered_perp_positions_above_1m (request : BackendRequest ):
197
+ def get_most_levered_perp_positions_above_1m (request : BackendRequest , number_of_positions : int = 10 , market_index : int = None ):
198
198
"""
199
- Get the top 10 most leveraged perpetual positions with value above $1 million.
199
+ Get the most leveraged perpetual positions with value above $1 million.
200
200
201
201
This endpoint calculates the leverage of each perpetual position with a value
202
- over $1 million and returns the top 10 most leveraged positions.
202
+ over $1 million and returns the most leveraged positions, limited by number_of_positions.
203
+ Results can be filtered by market_index if provided.
204
+
205
+ Args:
206
+ request: The backend request object
207
+ number_of_positions: Maximum number of positions to return (default: 10)
208
+ market_index: Optional market index to filter by
203
209
204
210
Returns:
205
- dict: A dictionary containing lists of data for the top 10 leveraged positions:
211
+ dict: A dictionary containing lists of data for the top leveraged positions:
206
212
- Market Index (list[int]): The market indices of the top positions
207
213
- Value (list[str]): The formatted dollar values of the positions
208
214
- Base Asset Amount (list[str]): The formatted base asset amounts
@@ -223,6 +229,10 @@ def get_most_levered_perp_positions_above_1m(request: BackendRequest):
223
229
continue
224
230
if total_collateral > 0 :
225
231
for position in user .get_user_account ().perp_positions :
232
+ # Skip if filtering by market_index
233
+ if market_index is not None and position .market_index != market_index :
234
+ continue
235
+
226
236
if position .base_asset_amount > 0 :
227
237
market_price = vat .perp_oracles .get (position .market_index )
228
238
if market_price is not None :
@@ -240,7 +250,7 @@ def get_most_levered_perp_positions_above_1m(request: BackendRequest):
240
250
leverage ,
241
251
)
242
252
243
- if len (top_positions ) < 10 :
253
+ if len (top_positions ) < number_of_positions :
244
254
heapq .heappush (top_positions , heap_item )
245
255
else :
246
256
heapq .heappushpop (top_positions , heap_item )
@@ -264,15 +274,21 @@ def get_most_levered_perp_positions_above_1m(request: BackendRequest):
264
274
265
275
266
276
@router .get ("/largest_spot_borrows" )
267
- def get_largest_spot_borrows (request : BackendRequest ):
277
+ def get_largest_spot_borrows (request : BackendRequest , number_of_positions : int = 10 , market_index : int = None ):
268
278
"""
269
- Get the top 10 largest spot borrowing positions by value.
279
+ Get the largest spot borrowing positions by value.
270
280
271
281
This endpoint retrieves the largest spot borrowing positions across all users,
272
- calculated based on the current market prices.
282
+ calculated based on the current market prices. Results can be limited by
283
+ number_of_positions and filtered by market_index if provided.
284
+
285
+ Args:
286
+ request: The backend request object
287
+ number_of_positions: Maximum number of positions to return (default: 10)
288
+ market_index: Optional market index to filter by
273
289
274
290
Returns:
275
- dict: A dictionary containing lists of data for the top 10 borrowing positions:
291
+ dict: A dictionary containing lists of data for the top borrowing positions:
276
292
- Market Index (list[int]): The market indices of the top borrows
277
293
- Value (list[str]): The formatted dollar values of the borrows
278
294
- Scaled Balance (list[str]): The formatted scaled balances of the borrows
@@ -283,6 +299,10 @@ def get_largest_spot_borrows(request: BackendRequest):
283
299
284
300
for user in vat .users .values ():
285
301
for position in user .get_user_account ().spot_positions :
302
+ # Skip if filtering by market_index
303
+ if market_index is not None and position .market_index != market_index :
304
+ continue
305
+
286
306
if position .scaled_balance > 0 and is_variant (
287
307
position .balance_type , "Borrow"
288
308
):
@@ -299,7 +319,7 @@ def get_largest_spot_borrows(request: BackendRequest):
299
319
position .scaled_balance / SPOT_BALANCE_PRECISION ,
300
320
)
301
321
302
- if len (top_borrows ) < 10 :
322
+ if len (top_borrows ) < number_of_positions :
303
323
heapq .heappush (top_borrows , heap_item )
304
324
else :
305
325
heapq .heappushpop (top_borrows , heap_item )
@@ -322,73 +342,141 @@ def get_largest_spot_borrows(request: BackendRequest):
322
342
323
343
324
344
@router .get ("/most_levered_spot_borrows_above_1m" )
325
- def get_most_levered_spot_borrows_above_1m (request : BackendRequest ):
345
+ def get_most_levered_spot_borrows_above_1m (request : BackendRequest , number_of_positions : int = 10 , market_index : int = None ):
326
346
"""
327
- Get the top 10 most leveraged spot borrowing positions with value above $750,000.
347
+ Get the most leveraged spot borrowing positions with value above $750,000.
328
348
329
349
This endpoint calculates the leverage of each spot borrowing position with a value
330
- over $750,000 and returns the top 10 most leveraged positions.
350
+ over $750,000 and returns the most leveraged positions, limited by number_of_positions.
351
+ Results can be filtered by market_index if provided.
352
+
353
+ Args:
354
+ request: The backend request object
355
+ number_of_positions: Maximum number of positions to return (default: 10)
356
+ market_index: Optional market index to filter by
331
357
332
358
Returns:
333
- dict: A dictionary containing lists of data for the top 10 leveraged borrowing positions:
359
+ dict: A dictionary containing lists of data for the leveraged borrowing positions:
334
360
- Market Index (list[int]): The market indices of the top borrows
335
361
- Value (list[str]): The formatted dollar values of the borrows
336
362
- Scaled Balance (list[str]): The formatted scaled balances of the borrows
337
363
- Leverage (list[str]): The formatted leverage ratios
338
364
- Public Key (list[str]): The public keys of the borrowers
365
+ - Error (list[str]): Error details if any (empty string if no error)
339
366
"""
340
367
vat : Vat = request .state .backend_state .vat
341
- top_borrows : list [tuple [float , str , int , float , float ]] = []
368
+ top_borrows : list [tuple [float , str , int , float , float , str ]] = [] # Added error field
369
+ error_positions = [] # Track positions with errors for logging
342
370
343
371
for user in vat .users .values ():
372
+ user_collateral = 0
373
+ collateral_error = ""
374
+
344
375
try :
345
- total_collateral = user .get_total_collateral () / PRICE_PRECISION
376
+ user_collateral = user .get_total_collateral () / PRICE_PRECISION
346
377
except Exception as e :
347
- print (
348
- f"==> Error from get_most_levered_spot_borrows_above_1m [ { user . user_public_key } ] " ,
349
- e ,
378
+ collateral_error = f"Collateral error: { str ( e ) } "
379
+ logger . warning (
380
+ f"Error calculating collateral for user [ { user . user_public_key } ]: { str ( e ) } "
350
381
)
351
- raise e
352
- if total_collateral > 0 :
353
- for position in user .get_user_account ().spot_positions :
354
- if (
355
- is_variant (position .balance_type , "Borrow" )
356
- and position .scaled_balance > 0
357
- ):
358
- market_price = vat .spot_oracles .get (position .market_index )
359
- if market_price is not None :
382
+
383
+ for position in user .get_user_account ().spot_positions :
384
+ # Skip if filtering by market_index
385
+ if market_index is not None and position .market_index != market_index :
386
+ continue
387
+
388
+ if is_variant (position .balance_type , "Borrow" ) and position .scaled_balance > 0 :
389
+ position_error = collateral_error # Start with any collateral error
390
+ market_price = vat .spot_oracles .get (position .market_index )
391
+
392
+ if market_price is None :
393
+ oracle_error = f"Oracle for market { position .market_index } not found"
394
+ position_error = oracle_error if not position_error else f"{ position_error } ; { oracle_error } "
395
+ logger .warning (f"{ oracle_error } for user [{ user .user_public_key } ]" )
396
+
397
+ # Add position with error
398
+ borrow_value = 0 # Default value when price is unknown
399
+ scaled_balance = position .scaled_balance / SPOT_BALANCE_PRECISION
400
+ leverage = 0 # Default leverage when calculation is impossible
401
+
402
+ error_positions .append ({
403
+ "market_index" : position .market_index ,
404
+ "public_key" : user .user_public_key ,
405
+ "scaled_balance" : scaled_balance ,
406
+ "error" : position_error
407
+ })
408
+
409
+ # If we don't have enough items yet, add this one with error
410
+ if len (top_borrows ) < number_of_positions :
411
+ heap_item = (
412
+ borrow_value , # Will be sorted last due to 0 value
413
+ user .user_public_key ,
414
+ position .market_index ,
415
+ scaled_balance ,
416
+ leverage ,
417
+ position_error ,
418
+ )
419
+ heapq .heappush (top_borrows , heap_item )
420
+ else :
421
+ try :
360
422
market_price_ui = market_price .price / PRICE_PRECISION
361
423
borrow_value = (
362
424
position .scaled_balance / SPOT_BALANCE_PRECISION
363
425
) * market_price_ui
364
- leverage = borrow_value / total_collateral
426
+
427
+ if user_collateral > 0 :
428
+ leverage = borrow_value / user_collateral
429
+ else :
430
+ leverage = float ('inf' ) # Infinite leverage when collateral is 0
431
+ if not position_error :
432
+ position_error = "Zero collateral"
433
+
365
434
if borrow_value > 750_000 :
366
435
heap_item = (
367
436
to_financial (borrow_value ),
368
437
user .user_public_key ,
369
438
position .market_index ,
370
439
position .scaled_balance / SPOT_BALANCE_PRECISION ,
371
440
leverage ,
441
+ position_error , # Empty string if no error
372
442
)
373
443
374
- if len (top_borrows ) < 10 :
444
+ if len (top_borrows ) < number_of_positions :
375
445
heapq .heappush (top_borrows , heap_item )
376
446
else :
377
447
heapq .heappushpop (top_borrows , heap_item )
448
+ except Exception as e :
449
+ calc_error = f"Calculation error: { str (e )} "
450
+ position_error = calc_error if not position_error else f"{ position_error } ; { calc_error } "
451
+ logger .warning (
452
+ f"Error processing borrow position for user [{ user .user_public_key } ] market [{ position .market_index } ]: { str (e )} "
453
+ )
454
+
455
+ # Add error position
456
+ error_positions .append ({
457
+ "market_index" : position .market_index ,
458
+ "public_key" : user .user_public_key ,
459
+ "scaled_balance" : position .scaled_balance / SPOT_BALANCE_PRECISION ,
460
+ "error" : position_error
461
+ })
462
+
463
+ # Log all error positions for debugging
464
+ if error_positions :
465
+ logger .warning (f"Found { len (error_positions )} positions with errors: { error_positions } " )
378
466
379
- borrows = sorted (
467
+ positions = sorted (
380
468
top_borrows ,
381
- key = lambda x : x [4 ],
469
+ key = lambda x : x [4 ] if not x [5 ] else float ('-inf' ), # Sort error positions first
470
+ reverse = True ,
382
471
)
383
472
384
- borrows .reverse ()
385
-
386
473
data = {
387
- "Market Index" : [pos [2 ] for pos in borrows ],
388
- "Value" : [f"${ pos [0 ]:,.2f} " for pos in borrows ],
389
- "Scaled Balance" : [f"{ pos [3 ]:,.2f} " for pos in borrows ],
390
- "Leverage" : [f"{ pos [4 ]:,.2f} " for pos in borrows ],
391
- "Public Key" : [pos [1 ] for pos in borrows ],
474
+ "Market Index" : [pos [2 ] for pos in positions ],
475
+ "Value" : [f"${ pos [0 ]:,.2f} " if not pos [5 ] else "N/A" for pos in positions ],
476
+ "Scaled Balance" : [f"{ pos [3 ]:,.2f} " for pos in positions ],
477
+ "Leverage" : [f"{ pos [4 ]:,.2f} " if not pos [5 ] and pos [4 ] != float ('inf' ) else "∞" if pos [4 ] == float ('inf' ) else "N/A" for pos in positions ],
478
+ "Public Key" : [pos [1 ] for pos in positions ],
479
+ "Error" : [pos [5 ] for pos in positions ],
392
480
}
393
481
394
482
return data
0 commit comments