@@ -97,6 +97,18 @@ def extract_asset_recommendations(analysis_text: str) -> List[Dict[str, Any]]:
9797 'rationale' : f"{ percentage } % allocation"
9898 })
9999
100+ # If no recommendations found with patterns, try to extract any mentioned symbols
101+ if not recommendations :
102+ # Look for any mentioned symbols in the text
103+ for symbol in asset_symbols :
104+ if symbol in analysis_text :
105+ recommendations .append ({
106+ 'symbol' : symbol ,
107+ 'action' : 'MENTIONED' ,
108+ 'confidence' : 'low' ,
109+ 'rationale' : f"Symbol { symbol } mentioned in analysis"
110+ })
111+
100112 return recommendations
101113
102114
@@ -179,7 +191,7 @@ def get_historical_analysis(self, md_resource: MotherDuckResource, as_of_date: s
179191 query = """
180192 SELECT analysis_content, analysis_type, analysis_timestamp
181193 FROM economic_cycle_analysis
182- WHERE analysis_timestamp <= ?
194+ WHERE DATE( analysis_timestamp) <= ?
183195 ORDER BY analysis_timestamp DESC
184196 LIMIT 2
185197 """
@@ -203,7 +215,7 @@ def get_asset_allocation_analysis(self, md_resource: MotherDuckResource, as_of_d
203215 query = """
204216 SELECT analysis_content
205217 FROM asset_allocation_recommendations
206- WHERE analysis_timestamp <= ?
218+ WHERE DATE( analysis_timestamp) <= ?
207219 ORDER BY analysis_timestamp DESC
208220 LIMIT 1
209221 """
@@ -307,20 +319,30 @@ def run_backtest(self,
307319 if context :
308320 context .log .info (f"Running backtest for prediction date: { prediction_date } " )
309321
310- # Calculate end date
311- pred_dt = datetime .strptime (prediction_date , '%Y-%m-%d' )
322+ # Calculate end date - handle both string and date inputs
323+ if isinstance (prediction_date , str ):
324+ pred_dt = datetime .strptime (prediction_date , '%Y-%m-%d' )
325+ elif isinstance (prediction_date , datetime .date ):
326+ pred_dt = datetime .combine (prediction_date , datetime .min .time ())
327+ else :
328+ # Convert to string first, then parse
329+ pred_dt = datetime .strptime (str (prediction_date ), '%Y-%m-%d' )
330+
312331 end_dt = pred_dt + timedelta (days = prediction_horizon_days )
313332 end_date = end_dt .strftime ('%Y-%m-%d' )
314333
334+ # Convert prediction_date to string for the database queries
335+ prediction_date_str = pred_dt .strftime ('%Y-%m-%d' )
336+
315337 # Get historical analysis
316- analysis = self .get_historical_analysis (md_resource , prediction_date )
338+ analysis = self .get_historical_analysis (md_resource , prediction_date_str )
317339 if not analysis :
318- raise ValueError (f"No analysis found for date { prediction_date } " )
340+ raise ValueError (f"No analysis found for date { prediction_date_str } " )
319341
320342 # Get asset allocation recommendations
321- allocation_analysis = self .get_asset_allocation_analysis (md_resource , prediction_date )
343+ allocation_analysis = self .get_asset_allocation_analysis (md_resource , prediction_date_str )
322344 if not allocation_analysis :
323- raise ValueError (f"No asset allocation analysis found for date { prediction_date } " )
345+ raise ValueError (f"No asset allocation analysis found for date { prediction_date_str } " )
324346
325347 # Extract predictions
326348 extractor = PredictionExtractor ()
@@ -337,15 +359,15 @@ def run_backtest(self,
337359
338360 # Get historical prices
339361 price_data = self .get_historical_prices (
340- md_resource , symbols , prediction_date , end_date
362+ md_resource , symbols , prediction_date_str , end_date
341363 )
342364
343365 if price_data .is_empty ():
344366 raise ValueError (f"No price data found for symbols: { symbols } " )
345367
346368 # Calculate actual returns
347369 calculator = PerformanceCalculator ()
348- actual_returns = calculator .calculate_returns (price_data , prediction_date , end_date )
370+ actual_returns = calculator .calculate_returns (price_data , prediction_date_str , end_date )
349371
350372 # Calculate performance metrics
351373 returns_list = list (actual_returns .values ())
@@ -356,7 +378,7 @@ def run_backtest(self,
356378
357379 # Create backtest result
358380 result = BacktestResult (
359- prediction_date = prediction_date ,
381+ prediction_date = prediction_date_str ,
360382 prediction_horizon_days = prediction_horizon_days ,
361383 predicted_assets = predicted_assets ,
362384 actual_returns = actual_returns ,
@@ -575,10 +597,53 @@ def batch_backtest_analysis(
575597 if df .is_empty ():
576598 raise ValueError ("No historical analysis found for batch backtesting" )
577599
578- prediction_dates = [row [0 ] for row in df .iter_rows ()]
600+ prediction_dates = [str ( row [0 ]) for row in df .iter_rows ()]
579601
580602 context .log .info (f"Found { len (prediction_dates )} prediction dates for batch backtesting" )
581603
604+ context .log .info (f"Sample prediction dates: { prediction_dates [:3 ]} " )
605+
606+ # Test the first prediction date
607+ if prediction_dates :
608+ test_date = prediction_dates [0 ]
609+ context .log .info (f"Testing data availability for date: { test_date } " )
610+
611+ # Test analysis data
612+ test_analysis = backtesting_engine .get_historical_analysis (md , test_date )
613+ context .log .info (f"Analysis data available: { test_analysis is not None } " )
614+
615+ # Test allocation data
616+ test_allocation = backtesting_engine .get_asset_allocation_analysis (md , test_date )
617+ context .log .info (f"Allocation data available: { test_allocation is not None } " )
618+
619+ if test_allocation :
620+ context .log .info (f"Sample allocation text: { test_allocation [:200 ]} ..." )
621+ # Test extraction
622+ extractor = PredictionExtractor ()
623+ test_recommendations = extractor .extract_asset_recommendations (test_allocation )
624+ context .log .info (f"Extracted recommendations: { len (test_recommendations )} " )
625+ if test_recommendations :
626+ context .log .info (f"Sample recommendation: { test_recommendations [0 ]} " )
627+
628+ # Add this right after context.log.info("Starting batch backtesting analysis...")
629+ # Around line 582
630+
631+ # Validate that all required tables exist with data
632+ validation_query = """
633+ SELECT
634+ (SELECT COUNT(*) FROM economic_cycle_analysis) as cycle_count,
635+ (SELECT COUNT(*) FROM asset_allocation_recommendations) as allocation_count,
636+ (SELECT COUNT(*) FROM us_sector_etfs_raw) as sectors_count,
637+ (SELECT COUNT(*) FROM major_indices_raw) as indices_count
638+ """
639+ validation_df = md .execute_query (validation_query , read_only = True )
640+ if not validation_df .is_empty ():
641+ row = validation_df [0 ]
642+ context .log .info (f"Data availability - Cycle: { row [0 ]} , Allocation: { row [1 ]} , Sectors: { row [2 ]} , Indices: { row [3 ]} " )
643+
644+ if row [0 ] == 0 or row [1 ] == 0 :
645+ raise ValueError (f"Missing required analysis data - Cycle: { row [0 ]} , Allocation: { row [1 ]} " )
646+
582647 # Run backtests for each date
583648 batch_results = []
584649 successful_backtests = 0
@@ -604,6 +669,8 @@ def batch_backtest_analysis(
604669
605670 except Exception as e :
606671 context .log .warning (f"Failed to backtest { pred_date } : { str (e )} " )
672+ context .log .warning (f"Exception type: { type (e ).__name__ } " )
673+ context .log .warning (f"Exception details: { str (e )} " )
607674 continue
608675
609676 if not batch_results :
0 commit comments