1
- # This is the new version of the gecko market recommender which makes use of the /backend/utils/coingecko_api.py utility script.
2
-
3
- """
4
- Follow this high level structure when writing the new script.
5
- Only focus on one section at a time.
6
- I will instruct you which section we are working on.
7
- If I do not instruct you, ask me which section you are working on.
8
- This script will use synchronous code instead of asynchronous code in the previous version.
9
-
10
- def fetch_market_data():
11
- # Minimal working data retrieval with ccxt
12
- pass
13
-
14
- def score_assets(market_data):
15
- # Single simplified scoring method for all assets
16
- pass
17
-
18
- def categorize_assets(scored_assets):
19
- # Clearly separate lists: to_list, to_delist, leverage_up, leverage_down
20
- pass
21
-
22
- def main():
23
- data = fetch_market_data()
24
- scored = score_assets(data)
25
- categorized = categorize_assets(scored)
26
- # output simple DataFrames for Streamlit
27
- return categorized
28
-
29
- """
1
+ # This is the new version of the market recommender which makes use of the coingecko API via the /backend/utils/coingecko_api.py utility script.
30
2
31
3
import logging
32
4
from typing import Dict , List
@@ -227,6 +199,10 @@ def fetch_driftpy_data(vat: Vat) -> Dict:
227
199
base_oi_readable = base_oi / (10 ** base_decimals )
228
200
oi_usd = base_oi_readable * oracle_price
229
201
202
+ # Calculate max leverage from margin ratio initial
203
+ initial_margin_ratio = market .data .margin_ratio_initial / 10000
204
+ max_leverage = int (1 / initial_margin_ratio ) if initial_margin_ratio > 0 else 0
205
+
230
206
# Initialize symbol entry if not exists
231
207
if normalized_symbol not in drift_markets :
232
208
drift_markets [normalized_symbol ] = {
@@ -236,7 +212,7 @@ def fetch_driftpy_data(vat: Vat) -> Dict:
236
212
"drift_spot_markets" : {},
237
213
"drift_total_quote_volume_30d" : 0.0 ,
238
214
"drift_total_base_volume_30d" : 0.0 ,
239
- "drift_max_leverage" : float ( market . data . margin_ratio_initial ) ,
215
+ "drift_max_leverage" : max_leverage ,
240
216
"drift_open_interest" : oi_usd ,
241
217
"drift_funding_rate_1h" : float (market .data .amm .last_funding_rate ) / 1e6 * 100
242
218
}
@@ -361,7 +337,6 @@ def fetch_api_page(url: str, retries: int = 5) -> Dict:
361
337
362
338
if not DRIFT_DATA_API_AUTH :
363
339
logger .error ("DRIFT_DATA_API_AUTH environment variable not set" )
364
- return {"success" : False , "records" : [], "meta" : {"totalRecords" : 0 }}
365
340
366
341
last_request_time = getattr (fetch_api_page , 'last_request_time' , 0 )
367
342
@@ -563,54 +538,153 @@ def calculate_market_volume(symbol: str) -> dict:
563
538
564
539
return drift_markets
565
540
566
- def calculate_volume_score (volume_30d : float ) -> float :
541
+ def calculate_drift_volume_score (volume_30d : float ) -> float :
567
542
"""
568
- Calculate a score based on 30-day trading volume.
543
+ Calculate a score based on Drift Protocol 30-day trading volume.
569
544
570
545
Args:
571
546
volume_30d (float): 30-day trading volume in USD
572
547
573
548
Returns:
574
- float: Volume score between 0 and 50
549
+ float: Volume score between 0 and 25
575
550
"""
576
- # Score based on volume tiers (adjust thresholds as needed)
577
- if volume_30d >= 1_000_000_000 : # $1B+
578
- return 50.0
579
- elif volume_30d >= 500_000_000 : # $500M+
580
- return 40.0
581
- elif volume_30d >= 100_000_000 : # $100M+
582
- return 30.0
583
- elif volume_30d >= 50_000_000 : # $50M+
551
+ # Score based on volume tiers according to the scoring breakdown
552
+ if volume_30d >= 500_000_000 : # $500M+
553
+ return 25.0
554
+ elif volume_30d >= 100_000_000 : # $100M - $499M
584
555
return 20.0
585
- elif volume_30d >= 10_000_000 : # $10M+
556
+ elif volume_30d >= 25_000_000 : # $25M - $99M
557
+ return 15.0
558
+ elif volume_30d >= 1_000_000 : # $1M - $24M
586
559
return 10.0
587
- else :
588
- return max (0.0 , min (10.0 , volume_30d / 1_000_000 )) # Linear score up to $10M
560
+ elif volume_30d >= 100_000 : # $100K - $999K
561
+ return 5.0
562
+ else : # < $100K
563
+ return 0.0
589
564
590
- def calculate_leverage_score ( max_leverage : float ) -> float :
565
+ def calculate_open_interest_score ( open_interest : float ) -> float :
591
566
"""
592
- Calculate a score based on maximum leverage offered .
567
+ Calculate a score based on Drift Protocol open interest .
593
568
594
569
Args:
595
- max_leverage (float): Maximum leverage offered
570
+ open_interest (float): Open interest in USD
596
571
597
572
Returns:
598
- float: Leverage score between 0 and 50
573
+ float: Open interest score between 0 and 25
599
574
"""
600
- # Score based on leverage tiers
601
- if max_leverage >= 20 :
602
- return 50.0
603
- elif max_leverage >= 15 :
575
+ # Score based on OI tiers according to the scoring breakdown
576
+ if open_interest >= 5_000_000 : # $5M+
577
+ return 25.0
578
+ elif open_interest >= 1_000_000 : # $1M - $4.9M
579
+ return 20.0
580
+ elif open_interest >= 250_000 : # $250K - $999K
581
+ return 15.0
582
+ elif open_interest >= 50_000 : # $50K - $249K
583
+ return 10.0
584
+ elif open_interest >= 5_000 : # $5K - $49K
585
+ return 5.0
586
+ else : # < $5K
587
+ return 0.0
588
+
589
+ def calculate_global_volume_score (daily_volume : float ) -> float :
590
+ """
591
+ Calculate a score based on global trading volume from CoinGecko.
592
+
593
+ Args:
594
+ daily_volume (float): Daily trading volume in USD
595
+
596
+ Returns:
597
+ float: Global volume score between 0 and 40
598
+ """
599
+ # Score based on global volume tiers according to the scoring breakdown
600
+ if daily_volume >= 500_000_000 : # $500M+
604
601
return 40.0
605
- elif max_leverage >= 10 :
602
+ elif daily_volume >= 250_000_000 : # $250M - $499M
606
603
return 30.0
607
- elif max_leverage >= 5 :
604
+ elif daily_volume >= 100_000_000 : # $100M - $249M
608
605
return 20.0
609
- elif max_leverage > 0 :
606
+ elif daily_volume >= 25_000_000 : # $25M - $99M
610
607
return 10.0
611
- else :
608
+ elif daily_volume >= 5_000_000 : # $5M - $24M
609
+ return 5.0
610
+ else : # < $5M
611
+ return 0.0
612
+
613
+ def calculate_fdv_score (fdv : float ) -> float :
614
+ """
615
+ Calculate a score based on Fully Diluted Valuation (FDV).
616
+
617
+ Args:
618
+ fdv (float): Fully Diluted Valuation in USD
619
+
620
+ Returns:
621
+ float: FDV score between 0 and 10
622
+ """
623
+ # Score based on FDV tiers according to the scoring breakdown
624
+ if fdv >= 10_000_000_000 : # $10B+
625
+ return 10.0
626
+ elif fdv >= 1_000_000_000 : # $1B - $9.9B
627
+ return 8.0
628
+ elif fdv >= 500_000_000 : # $500M - $999M
629
+ return 6.0
630
+ elif fdv >= 100_000_000 : # $100M - $499M
631
+ return 2.0
632
+ else : # < $100M
612
633
return 0.0
613
634
635
+ def get_market_recommendation (total_score : float , current_leverage : float ) -> str :
636
+ """
637
+ Determine market recommendation based on total score and current leverage.
638
+
639
+ Args:
640
+ total_score (float): Total score from all criteria (0-100)
641
+ current_leverage (float): Current maximum leverage offered
642
+
643
+ Returns:
644
+ str: Recommendation (List, Increase Leverage, Decrease Leverage, Delist, or No Action)
645
+ """
646
+ # Define upper and lower bound thresholds for different leverage levels
647
+ SCORE_UB = {
648
+ 0 : 45 , # Unlisted
649
+ 2 : float ('inf' ), # No upper bound for 2x (cannot increase further)
650
+ 4 : 75 ,
651
+ 5 : 80 ,
652
+ 10 : 90 ,
653
+ 20 : 95
654
+ }
655
+
656
+ SCORE_LB = {
657
+ 0 : 0 , # Not applicable for unlisted
658
+ 2 : 40 , # Delist threshold for 2x
659
+ 4 : 50 ,
660
+ 5 : 60 ,
661
+ 10 : 70 ,
662
+ 20 : 75
663
+ }
664
+
665
+ # Find the closest leverage level for thresholds
666
+ leverage_levels = sorted (SCORE_UB .keys ())
667
+ closest_leverage = leverage_levels [0 ]
668
+
669
+ for level in leverage_levels :
670
+ if level <= current_leverage :
671
+ closest_leverage = level
672
+
673
+ # Apply decision logic
674
+ if current_leverage == 0 : # Unlisted
675
+ if total_score >= SCORE_UB [0 ]:
676
+ return "List"
677
+ else :
678
+ return "Do Nothing"
679
+ elif current_leverage == 2 and total_score <= SCORE_LB [2 ]:
680
+ return "Delist"
681
+ elif total_score >= SCORE_UB .get (closest_leverage , float ('inf' )):
682
+ return "Increase Leverage"
683
+ elif total_score <= SCORE_LB .get (closest_leverage , 0 ) and closest_leverage > 2 :
684
+ return "Decrease Leverage"
685
+ else :
686
+ return "No Action"
687
+
614
688
def score_assets (assets : List [Dict ], drift_data : Dict ) -> List [Dict ]:
615
689
"""
616
690
Score assets based on Drift market data and other metrics.
@@ -630,23 +704,41 @@ def score_assets(assets: List[Dict], drift_data: Dict) -> List[Dict]:
630
704
# Get Drift market data if available
631
705
market_info = drift_data .get (symbol , {})
632
706
633
- # Calculate total volumes from all markets
634
- total_quote_volume = market_info .get ('drift_total_quote_volume_30d' , 0.0 )
635
- total_base_volume = market_info .get ('drift_total_base_volume_30d' , 0.0 )
636
-
637
- # Calculate scores using helper functions
638
- volume_score = calculate_volume_score (total_quote_volume )
639
- leverage_score = calculate_leverage_score (market_info .get ('drift_max_leverage' , 0.0 ))
707
+ # Get raw metrics
708
+ drift_volume_30d = market_info .get ('drift_total_quote_volume_30d' , 0.0 )
709
+ drift_open_interest = market_info .get ('drift_open_interest' , 0.0 )
710
+ global_daily_volume = asset .get ('coingecko_data' , {}).get ('coingecko_total_volume_24h' , 0.0 )
711
+ fdv = asset .get ('coingecko_data' , {}).get ('coingecko_fully_diluted_valuation' , 0.0 )
712
+ current_leverage = market_info .get ('drift_max_leverage' , 0.0 )
640
713
641
- # Combine scores (equal weighting for now)
642
- total_score = volume_score + leverage_score
714
+ # Calculate component scores
715
+ drift_volume_score = calculate_drift_volume_score (drift_volume_30d )
716
+ open_interest_score = calculate_open_interest_score (drift_open_interest )
717
+ global_volume_score = calculate_global_volume_score (global_daily_volume )
718
+ fdv_score = calculate_fdv_score (fdv )
719
+
720
+ # Calculate total score
721
+ total_score = drift_volume_score + open_interest_score + global_volume_score + fdv_score
722
+
723
+ # Get recommendation
724
+ recommendation = get_market_recommendation (total_score , current_leverage )
643
725
644
726
# Create scored asset dictionary with nested drift_data
645
727
scored_asset = {
646
728
** asset , # Include all original asset data
647
- 'volume_score' : volume_score ,
648
- 'leverage_score' : leverage_score ,
649
- 'total_score' : total_score
729
+ 'drift_volume_score' : drift_volume_score ,
730
+ 'open_interest_score' : open_interest_score ,
731
+ 'global_volume_score' : global_volume_score ,
732
+ 'fdv_score' : fdv_score ,
733
+ 'total_score' : total_score ,
734
+ 'recommendation' : recommendation ,
735
+ 'raw_metrics' : {
736
+ 'drift_volume_30d' : drift_volume_30d ,
737
+ 'drift_open_interest' : drift_open_interest ,
738
+ 'global_daily_volume' : global_daily_volume ,
739
+ 'fdv' : fdv ,
740
+ 'current_max_leverage' : current_leverage
741
+ }
650
742
}
651
743
652
744
# If there's no drift data, initialize with default structure
0 commit comments