Skip to content

Commit f0f928e

Browse files
authored
Merge pull request #46 from drift-labs:goldhaxx/DATA-77/fix-health-page-errors
fixed health page loading issues, refactor health page endpoints and improve logging.
2 parents 878fcc5 + f15ca3a commit f0f928e

File tree

4 files changed

+280
-308
lines changed

4 files changed

+280
-308
lines changed

backend/api/health.py

Lines changed: 38 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,13 @@ def get_account_health_distribution(request: BackendRequest):
120120

121121

122122
@router.get("/largest_perp_positions")
123-
def get_largest_perp_positions(request: BackendRequest, number_of_positions: int, market_index: int = None):
123+
def get_largest_perp_positions(request: BackendRequest, market_index: int = None):
124124
"""
125125
Get the largest perp positions by notional value across all users or for a specific market if market_index is provided.
126126
"""
127127
vat: Vat = request.state.backend_state.vat
128128
logger.info(
129-
f"==> [largest_perp_positions] Called with number_of_positions={number_of_positions}, "
129+
f"==> [largest_perp_positions] Called with "
130130
f"market_index={market_index}, current_pickle={request.state.backend_state.current_pickle_path}"
131131
)
132132
try:
@@ -168,21 +168,17 @@ def get_largest_perp_positions(request: BackendRequest, number_of_positions: int
168168
position.base_asset_amount / BASE_PRECISION, # Keep original sign for display
169169
))
170170

171-
# Sort all positions by value (descending) and take top N
172-
positions = sorted(all_positions, key=lambda x: x[0], reverse=True)[:number_of_positions]
171+
# Sort all positions by value (descending)
172+
positions = sorted(all_positions, key=lambda x: x[0], reverse=True)
173173

174+
# Add summary logging
175+
total_value_returned = sum(pos[0] for pos in positions)
174176
logger.info(
175177
f"==> [largest_perp_positions] Stats => total_checked={total_positions_checked}, "
176-
f"positions_meeting_criteria={positions_meeting_criteria}, positions_returned={len(positions)}"
178+
f"positions_meeting_criteria={positions_meeting_criteria}, positions_returned={len(positions)}, "
179+
f"total_notional_value=${total_value_returned:,.2f}"
177180
)
178181

179-
# Log each position with value and sign
180-
for idx, (value, pubkey, market_idx, amt) in enumerate(positions, 1):
181-
logger.info(
182-
f"==> [largest_perp_positions] Position {idx}: Market Index={market_idx}, "
183-
f"Value=${value:,.2f}, Base Asset Amount={amt:,.2f}, Public Key={pubkey}"
184-
)
185-
186182
data = {
187183
"Market Index": [pos[2] for pos in positions],
188184
"Value": [f"${pos[0]:,.2f}" for pos in positions],
@@ -194,17 +190,16 @@ def get_largest_perp_positions(request: BackendRequest, number_of_positions: int
194190

195191

196192
@router.get("/most_levered_perp_positions_above_1m")
197-
def get_most_levered_perp_positions_above_1m(request: BackendRequest, number_of_positions: int = 10, market_index: int = None):
193+
def get_most_levered_perp_positions_above_1m(request: BackendRequest, market_index: int = None):
198194
"""
199195
Get the most leveraged perpetual positions with value above $1 million.
200196
201197
This endpoint calculates the leverage of each perpetual position with a value
202-
over $1 million and returns the most leveraged positions, limited by number_of_positions.
198+
over $1 million and returns the most leveraged positions.
203199
Results can be filtered by market_index if provided.
204200
205201
Args:
206202
request: The backend request object
207-
number_of_positions: Maximum number of positions to return (default: 10)
208203
market_index: Optional market index to filter by
209204
210205
Returns:
@@ -216,7 +211,7 @@ def get_most_levered_perp_positions_above_1m(request: BackendRequest, number_of_
216211
- Public Key (list[str]): The public keys of the position holders
217212
"""
218213
vat: Vat = request.state.backend_state.vat
219-
top_positions: list[tuple[float, str, int, float, float]] = []
214+
all_positions: list[tuple[float, str, int, float, float]] = []
220215

221216
for user in vat.users.values():
222217
try:
@@ -242,26 +237,21 @@ def get_most_levered_perp_positions_above_1m(request: BackendRequest, number_of_
242237
) * market_price_ui
243238
leverage = base_asset_value / total_collateral
244239
if base_asset_value > 1_000_000:
245-
heap_item = (
240+
item = (
246241
to_financial(base_asset_value),
247242
user.user_public_key,
248243
position.market_index,
249244
position.base_asset_amount / BASE_PRECISION,
250245
leverage,
251246
)
252-
253-
if len(top_positions) < number_of_positions:
254-
heapq.heappush(top_positions, heap_item)
255-
else:
256-
heapq.heappushpop(top_positions, heap_item)
247+
all_positions.append(item)
257248

258249
positions = sorted(
259-
top_positions,
250+
all_positions,
260251
key=lambda x: x[4],
252+
reverse=True
261253
)
262254

263-
positions.reverse()
264-
265255
data = {
266256
"Market Index": [pos[2] for pos in positions],
267257
"Value": [f"${pos[0]:,.2f}" for pos in positions],
@@ -274,17 +264,16 @@ def get_most_levered_perp_positions_above_1m(request: BackendRequest, number_of_
274264

275265

276266
@router.get("/largest_spot_borrows")
277-
def get_largest_spot_borrows(request: BackendRequest, number_of_positions: int = 10, market_index: int = None):
267+
def get_largest_spot_borrows(request: BackendRequest, market_index: int = None):
278268
"""
279269
Get the largest spot borrowing positions by value.
280270
281271
This endpoint retrieves the largest spot borrowing positions across all users,
282-
calculated based on the current market prices. Results can be limited by
283-
number_of_positions and filtered by market_index if provided.
272+
calculated based on the current market prices. Results can be filtered by
273+
market_index if provided.
284274
285275
Args:
286276
request: The backend request object
287-
number_of_positions: Maximum number of positions to return (default: 10)
288277
market_index: Optional market index to filter by
289278
290279
Returns:
@@ -295,7 +284,7 @@ def get_largest_spot_borrows(request: BackendRequest, number_of_positions: int =
295284
- Public Key (list[str]): The public keys of the borrowers
296285
"""
297286
vat: Vat = request.state.backend_state.vat
298-
top_borrows: list[tuple[float, str, int, float]] = []
287+
all_borrows: list[tuple[float, str, int, float]] = []
299288

300289
for user in vat.users.values():
301290
for position in user.get_user_account().spot_positions:
@@ -312,24 +301,15 @@ def get_largest_spot_borrows(request: BackendRequest, number_of_positions: int =
312301
borrow_value = (
313302
position.scaled_balance / SPOT_BALANCE_PRECISION
314303
) * market_price_ui
315-
heap_item = (
304+
item = (
316305
to_financial(borrow_value),
317306
user.user_public_key,
318307
position.market_index,
319308
position.scaled_balance / SPOT_BALANCE_PRECISION,
320309
)
310+
all_borrows.append(item)
321311

322-
if len(top_borrows) < number_of_positions:
323-
heapq.heappush(top_borrows, heap_item)
324-
else:
325-
heapq.heappushpop(top_borrows, heap_item)
326-
327-
borrows = sorted(
328-
(value, pubkey, market_idx, amt)
329-
for value, pubkey, market_idx, amt in top_borrows
330-
)
331-
332-
borrows.reverse()
312+
borrows = sorted(all_borrows, key=lambda x: x[0], reverse=True)
333313

334314
data = {
335315
"Market Index": [pos[2] for pos in borrows],
@@ -342,17 +322,16 @@ def get_largest_spot_borrows(request: BackendRequest, number_of_positions: int =
342322

343323

344324
@router.get("/most_levered_spot_borrows_above_1m")
345-
def get_most_levered_spot_borrows_above_1m(request: BackendRequest, number_of_positions: int = 10, market_index: int = None):
325+
def get_most_levered_spot_borrows_above_1m(request: BackendRequest, market_index: int = None):
346326
"""
347327
Get the most leveraged spot borrowing positions with value above $750,000.
348328
349329
This endpoint calculates the leverage of each spot borrowing position with a value
350-
over $750,000 and returns the most leveraged positions, limited by number_of_positions.
330+
over $750,000 and returns the most leveraged positions.
351331
Results can be filtered by market_index if provided.
352332
353333
Args:
354334
request: The backend request object
355-
number_of_positions: Maximum number of positions to return (default: 10)
356335
market_index: Optional market index to filter by
357336
358337
Returns:
@@ -365,7 +344,7 @@ def get_most_levered_spot_borrows_above_1m(request: BackendRequest, number_of_po
365344
- Error (list[str]): Error details if any (empty string if no error)
366345
"""
367346
vat: Vat = request.state.backend_state.vat
368-
top_borrows: list[tuple[float, str, int, float, float, str]] = [] # Added error field
347+
all_borrows: list[tuple[float, str, int, float, float, str]] = [] # Added error field
369348
error_positions = [] # Track positions with errors for logging
370349

371350
for user in vat.users.values():
@@ -406,17 +385,16 @@ def get_most_levered_spot_borrows_above_1m(request: BackendRequest, number_of_po
406385
"error": position_error
407386
})
408387

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)
388+
# Add this one with error
389+
item = (
390+
borrow_value, # Will be sorted last due to 0 value
391+
user.user_public_key,
392+
position.market_index,
393+
scaled_balance,
394+
leverage,
395+
position_error,
396+
)
397+
all_borrows.append(item)
420398
else:
421399
try:
422400
market_price_ui = market_price.price / PRICE_PRECISION
@@ -432,19 +410,15 @@ def get_most_levered_spot_borrows_above_1m(request: BackendRequest, number_of_po
432410
position_error = "Zero collateral"
433411

434412
if borrow_value > 750_000:
435-
heap_item = (
413+
item = (
436414
to_financial(borrow_value),
437415
user.user_public_key,
438416
position.market_index,
439417
position.scaled_balance / SPOT_BALANCE_PRECISION,
440418
leverage,
441419
position_error, # Empty string if no error
442420
)
443-
444-
if len(top_borrows) < number_of_positions:
445-
heapq.heappush(top_borrows, heap_item)
446-
else:
447-
heapq.heappushpop(top_borrows, heap_item)
421+
all_borrows.append(item)
448422
except Exception as e:
449423
calc_error = f"Calculation error: {str(e)}"
450424
position_error = calc_error if not position_error else f"{position_error}; {calc_error}"
@@ -465,7 +439,7 @@ def get_most_levered_spot_borrows_above_1m(request: BackendRequest, number_of_po
465439
logger.warning(f"Found {len(error_positions)} positions with errors: {error_positions}")
466440

467441
positions = sorted(
468-
top_borrows,
442+
all_borrows,
469443
key=lambda x: x[4] if not x[5] else float('-inf'), # Sort error positions first
470444
reverse=True,
471445
)

backend/middleware/cache_middleware.py

Lines changed: 13 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -310,78 +310,23 @@ def _serve_cached_response(self, cache_file: str, cache_status: str, request_pat
310310

311311
def _log_perp_positions_details(self, data, cache_status, cache_file, request_path):
312312
"""Helper method to log details specifically for the largest_perp_positions endpoint."""
313-
# Direct print statements guaranteed to show in the terminal
314-
print(f"\n======== CACHED POSITIONS ({cache_status}) ========")
315-
print(f"Cache file: {cache_file}")
316-
317313
positions_returned = 0
318-
# Handle potential variations in response structure (list vs dict)
319-
if isinstance(data, list) and len(data) > 0 and isinstance(data[0], dict):
314+
if isinstance(data, list):
320315
positions_returned = len(data)
321-
print(f"Total positions returned (list format): {positions_returned}")
322-
for idx, pos in enumerate(data):
323-
position_num = idx + 1
324-
print(f"Position {position_num}: {pos}")
325-
326316
elif isinstance(data, dict) and "Market Index" in data:
327317
positions_returned = len(data.get("Market Index", []))
328-
print(f"Total positions returned (dict format): {positions_returned}")
329-
330-
if positions_returned > 0:
331-
market_indices = data.get("Market Index", [])
332-
values = data.get("Value", [])
333-
base_amounts = data.get("Base Asset Amount", [])
334-
public_keys = data.get("Public Key", [])
335-
336-
# Log positions in the correct order (1 to N)
337-
for idx in range(positions_returned):
338-
market_idx = market_indices[idx] if idx < len(market_indices) else "N/A"
339-
value = values[idx] if idx < len(values) else "N/A"
340-
base_amt = base_amounts[idx] if idx < len(base_amounts) else "N/A"
341-
pub_key = public_keys[idx] if idx < len(public_keys) else "N/A"
342-
343-
# Positions are already sorted, so we can log them as is
344-
position_num = idx + 1
345-
print(f"Position {position_num}:")
346-
print(f" Market Index: {market_idx}")
347-
print(f" Value: {value}")
348-
print(f" Base Asset Amount: {base_amt}")
349-
print(f" Public Key: {pub_key}")
350-
else:
351-
print("No positions found in cached dictionary response")
352-
else:
353-
print(f"Warning: Unrecognized format for position data or empty data. Data: {data}")
354-
355-
print("======== END CACHED POSITIONS ========\n")
356-
357-
# Also log to standard logger for Loki
358-
log = logging.getLogger("backend.api.health") # Use appropriate logger name if different
359-
log.info(f"[CACHED {cache_status}] BEGIN POSITION DETAILS ({request_path})----------------------------------------")
360-
if isinstance(data, list) and positions_returned > 0:
361-
for idx, pos in enumerate(data):
362-
log.info(f"Position {idx + 1}: {pos}")
363-
elif isinstance(data, dict) and positions_returned > 0:
364-
market_indices = data.get("Market Index", [])
365-
values = data.get("Value", [])
366-
base_amounts = data.get("Base Asset Amount", [])
367-
public_keys = data.get("Public Key", [])
368-
for idx in range(positions_returned):
369-
market_idx = market_indices[idx] if idx < len(market_indices) else "N/A"
370-
value = values[idx] if idx < len(values) else "N/A"
371-
base_amt = base_amounts[idx] if idx < len(base_amounts) else "N/A"
372-
pub_key = public_keys[idx] if idx < len(public_keys) else "N/A"
373-
374-
position_num = idx + 1
375-
log.info(
376-
f"Position {position_num}:\n"
377-
f" Market Index: {market_idx}\n"
378-
f" Value: {value}\n"
379-
f" Base Asset Amount: {base_amt}\n"
380-
f" Public Key: {pub_key}"
381-
)
382-
else:
383-
log.info("No positions logged.")
384-
log.info(f"[CACHED {cache_status}] END POSITION DETAILS ({request_path}) ----------------------------------------")
318+
319+
summary_message = f"[{cache_status}] ({request_path}): Found {positions_returned} cached positions. Details suppressed to reduce log noise."
320+
321+
# Log summary to console
322+
print(f"\n======== CACHED POSITIONS SUMMARY ({cache_status}) ========")
323+
print(summary_message)
324+
print(f"Cache file: {cache_file}")
325+
print("======== END CACHED POSITIONS SUMMARY ========\n")
326+
327+
# Log summary to standard logger
328+
log = logging.getLogger("backend.api.health")
329+
log.info(summary_message)
385330

386331

387332
async def _fetch_and_cache(

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.56
15+
driftpy==0.8.57
1616
ccxt==4.2.17
1717
rich>=10.14.0
1818
aiofiles==24.1.0

0 commit comments

Comments
 (0)