2121from vali_objects .utils .ledger_utils import LedgerUtils
2222from vali_objects .scoring .scoring import Scoring
2323from vali_objects .utils .metrics import Metrics
24+ from vali_objects .utils .asset_segmentation import AssetSegmentation
2425from vali_objects .vali_dataclasses .perf_ledger import PerfLedgerManager , TP_ID_PORTFOLIO
2526from vali_objects .utils .risk_profiling import RiskProfiling
2627from vali_objects .vali_dataclasses .perf_ledger import PerfLedger
@@ -332,6 +333,39 @@ def calculate_all_daily_returns(self, filtered_ledger: dict[str, dict[str, PerfL
332333 for hotkey , ledgers in filtered_ledger .items ()
333334 }
334335
336+ def calculate_subcategory_daily_returns (self , filtered_ledger : dict [str , dict [str , PerfLedger ]]) -> dict [str , dict [str , dict [str , float ]]]:
337+ """
338+ Calculate daily returns for each asset subcategory for all miners.
339+
340+ Args:
341+ filtered_ledger: The filtered ledger data for all miners
342+
343+ Returns:
344+ dict with structure: {hotkey: {subcategory: {date: return_value}}}
345+ """
346+ subcategory_daily_returns = {}
347+
348+ # Get asset subcategories
349+ asset_class_breakdown = ValiConfig .ASSET_CLASS_BREAKDOWN
350+ asset_subcategories = AssetSegmentation .distill_asset_subcategories (asset_class_breakdown )
351+
352+ segmentation_machine = AssetSegmentation (filtered_ledger )
353+
354+ # Calculate returns for each subcategory
355+ for subcategory in asset_subcategories :
356+ subcategory_ledger = segmentation_machine .segmentation (subcategory )
357+
358+ # Calculate daily returns for each miner in this subcategory
359+ for hotkey , aggregated_ledger in subcategory_ledger .items ():
360+ if hotkey not in subcategory_daily_returns :
361+ subcategory_daily_returns [hotkey ] = {}
362+
363+ # Use the daily_returns_by_date_json function from LedgerUtils
364+ daily_returns = LedgerUtils .daily_returns_by_date_json (aggregated_ledger )
365+ subcategory_daily_returns [hotkey ][subcategory ] = daily_returns
366+
367+ return subcategory_daily_returns
368+
335369 # -------------------------------------------
336370 # Challenge Period
337371 # -------------------------------------------
@@ -624,6 +658,7 @@ def generate_miner_statistics_data(
624658
625659 # For visualization
626660 daily_returns_dict = self .calculate_all_daily_returns (filtered_ledger )
661+ subcategory_daily_returns_dict = self .calculate_subcategory_daily_returns (filtered_ledger )
627662
628663 # Also compute penalty breakdown (for display in final "penalties" dict).
629664 penalty_breakdown = self .calculate_penalties_breakdown (miner_data )
@@ -725,6 +760,9 @@ def build_scores_dict(metric_set: Dict[str, Dict[str, ScoreResult]]) -> Dict[str
725760
726761 # Asset Subcategory Detailed Metrics
727762 asset_subcategory_metrics = self .miner_subcategory_metrics (hotkey , asset_detailed_scores )
763+
764+ # Asset Subcategory Daily Returns
765+ asset_subcategory_daily_returns = subcategory_daily_returns_dict .get (hotkey , {})
728766
729767 final_miner_dict = {
730768 "hotkey" : hotkey ,
@@ -739,6 +777,7 @@ def build_scores_dict(metric_set: Dict[str, Dict[str, ScoreResult]]) -> Dict[str
739777 "risk_profile" : risk_profile_single_dict ,
740778 "asset_subcategory_performance" : asset_subcategory_performance ,
741779 "asset_subcategory_metrics" : asset_subcategory_metrics ,
780+ "asset_subcategory_daily_returns" : asset_subcategory_daily_returns ,
742781 "penalties" : {
743782 "drawdown_threshold" : pen_break .get ("drawdown_threshold" , 1.0 ),
744783 "risk_profile" : pen_break .get ("risk_profile" , 1.0 ),
0 commit comments