Backtesting with AI Predictions
This guide shows you how to evaluate FinBrain AI predictions through backtesting. You’ll learn how to measure prediction accuracy, build signal-based strategies, and analyze historical performance.
What You’ll Learn
Section titled “What You’ll Learn”- How to fetch historical prediction data
- Measuring prediction accuracy
- Building a simple backtesting framework
- Combining predictions with other signals
- Analyzing results and optimizing parameters
Prerequisites
Section titled “Prerequisites”- Python 3.7+
- FinBrain API key
- Basic understanding of pandas
pip install finbrain-python pandas numpyStep 1: Fetch Historical Predictions
Section titled “Step 1: Fetch Historical Predictions”First, let’s retrieve historical predictions for analysis:
from finbrain import FinBrainClientimport pandas as pdfrom datetime import datetime, timedelta
fb = FinBrainClient(api_key="YOUR_API_KEY")
def get_historical_predictions(ticker, days=365): """Fetch historical predictions for backtesting""" date_to = datetime.now().strftime("%Y-%m-%d") date_from = (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
predictions = fb.predictions.ticker( ticker, prediction_type="daily", date_from=date_from, date_to=date_to )
# Extract prediction data pred = predictions.get("prediction", {}) df = pd.DataFrame([{ "ticker": predictions.get("ticker"), "expectedShort": float(pred.get("expectedShort", 0)), "expectedMid": float(pred.get("expectedMid", 0)), "expectedLong": float(pred.get("expectedLong", 0)), "lastUpdate": pred.get("lastUpdate") }])
return df
# Get predictions for Applepredictions = get_historical_predictions("AAPL")print(predictions)print(f"\nPrediction data retrieved")Step 2: Get Actual Price Data
Section titled “Step 2: Get Actual Price Data”To evaluate predictions, we need actual price data. You can use any price data source:
import yfinance as yf
def get_price_data(ticker, start_date, end_date): """Fetch actual price data for comparison""" stock = yf.Ticker(ticker) df = stock.history(start=start_date, end=end_date) df.index = df.index.tz_localize(None) # Remove timezone return df[["Close"]].rename(columns={"Close": "actual_price"})
# Get actual pricesprices = get_price_data("AAPL", "2023-01-01", "2024-01-31")print(prices.head())Step 3: Measure Prediction Accuracy
Section titled “Step 3: Measure Prediction Accuracy”Let’s evaluate how accurate the predictions are:
def analyze_prediction_signal(prediction_data, actual_return): """Analyze if prediction signal was correct""" expected_short = float(prediction_data.get("expectedShort", 0))
# Check if expected short-term move aligned with direction direction_correct = ( (expected_short > 0 and actual_return > 0) or (expected_short < 0 and actual_return < 0) or (expected_short == 0) )
# Strong signal check (threshold of 1%) strong_signal_correct = ( (expected_short > 1 and actual_return > 0) or (expected_short < -1 and actual_return < 0) )
return { "direction_correct": direction_correct, "strong_signal_correct": strong_signal_correct, "expected_short": expected_short, "actual_return": actual_return }
# Example usage with actual price datafb = FinBrainClient(api_key="YOUR_API_KEY")prediction = fb.predictions.ticker("AAPL", prediction_type="daily")pred = prediction.get("prediction", {})
print("\n=== Prediction Analysis ===")print(f"Expected Short: {pred.get('expectedShort')}%")print(f"Expected Mid: {pred.get('expectedMid')}%")print(f"Expected Long: {pred.get('expectedLong')}%")Step 4: Build a Simple Backtest
Section titled “Step 4: Build a Simple Backtest”Now let’s build a simple backtesting framework:
class SimpleBacktest: def __init__(self, api_key, initial_capital=100000): self.fb = FinBrainClient(api_key=api_key) self.initial_capital = initial_capital
def get_signal(self, ticker): """Get current trading signal from prediction""" prediction = self.fb.predictions.ticker(ticker, prediction_type="daily") pred = prediction.get("prediction", {})
expected_short = float(pred.get("expectedShort", 0))
# Generate signal based on expected move thresholds if expected_short > 1: return "buy", expected_short elif expected_short < -1: return "sell", expected_short else: return "hold", expected_short
def analyze_ticker(self, ticker): """Analyze a ticker for trading signals""" prediction = self.fb.predictions.ticker(ticker, prediction_type="daily") pred = prediction.get("prediction", {})
return { "ticker": ticker, "expected_short": pred.get("expectedShort"), "expected_mid": pred.get("expectedMid"), "expected_long": pred.get("expectedLong"), "last_update": pred.get("lastUpdate") }
# Use the backtest frameworkbacktest = SimpleBacktest(api_key="YOUR_API_KEY")
# Analyze multiple tickerstickers = ["AAPL", "MSFT", "NVDA"]for ticker in tickers: analysis = backtest.analyze_ticker(ticker) signal, expected = backtest.get_signal(ticker) print(f"\n{ticker}:") print(f" Signal: {signal}") print(f" Expected Short: {analysis['expected_short']}%") print(f" Expected Mid: {analysis['expected_mid']}%")Step 5: Advanced Backtest with Multiple Signals
Section titled “Step 5: Advanced Backtest with Multiple Signals”Combine AI predictions with other signals for better results:
class AdvancedBacktest: def __init__(self, api_key, initial_capital=100000): self.fb = FinBrainClient(api_key=api_key) self.initial_capital = initial_capital
def get_combined_signal(self, market, ticker): """Combine multiple signals for better accuracy""" signals = {"prediction": 0, "sentiment": 0, "insider": 0}
# AI Prediction signal try: pred_data = self.fb.predictions.ticker(ticker, prediction_type="daily") pred = pred_data.get("prediction", {}) if pred: expected_short = float(pred.get("expectedShort", 0)) if expected_short > 1: signals["prediction"] = 1 elif expected_short < -1: signals["prediction"] = -1 except: pass
# Sentiment signal try: sent = self.fb.sentiments.ticker(market, ticker) sentiment_data = sent.get("sentimentAnalysis", {}) if sentiment_data: dates = sorted(sentiment_data.keys(), reverse=True) score = float(sentiment_data[dates[0]]) if dates else 0 if score > 0.5: signals["sentiment"] = 1 elif score < -0.5: signals["sentiment"] = -1 except: pass
# Insider signal try: insider = self.fb.insider_transactions.ticker(market, ticker) transactions = insider.get("insiderTransactions", []) if transactions: recent = transactions[:5] buys = sum(1 for t in recent if "Buy" in t.get("transaction", "")) sells = sum(1 for t in recent if "Sale" in t.get("transaction", "")) if buys > sells + 1: signals["insider"] = 1 elif sells > buys + 1: signals["insider"] = -1 except: pass
# Combined score combined = ( signals["prediction"] * 0.5 + signals["sentiment"] * 0.3 + signals["insider"] * 0.2 )
return combined, signals
# Example usagebacktest = AdvancedBacktest(api_key="YOUR_API_KEY")combined_signal, breakdown = backtest.get_combined_signal("S&P 500", "AAPL")
print(f"\nCombined Signal: {combined_signal:.2f}")print(f"Breakdown: {breakdown}")Step 6: Market Screening with DataFrames
Section titled “Step 6: Market Screening with DataFrames”Use as_dataframe=True to get structured DataFrames directly from the SDK:
from finbrain import FinBrainClient
fb = FinBrainClient(api_key="YOUR_API_KEY")
# Get all S&P 500 predictions as a DataFrame (one API call!)df = fb.predictions.market("S&P 500", prediction_type="daily", as_dataframe=True)
print(df.head())# expectedShort expectedMid expectedLong sentimentScore# ticker# AAPL 0.22 0.58 0.25 0.186# MSFT 1.15 1.82 2.10 0.342# NVDA 2.31 3.45 4.12 0.521
# Filter for bullish signals (expected long-term gain > 2%)bullish = df[df["expectedLong"] > 2.0]print(f"\nStrong Bullish Signals: {len(bullish)}")print(bullish.sort_values("expectedLong", ascending=False))
# Filter for bearish signalsbearish = df[df["expectedShort"] < -1.0]print(f"\nBearish Signals: {len(bearish)}")
# Combine with sentiment for high-conviction pickshigh_conviction = df[(df["expectedLong"] > 1.5) & (df["sentimentScore"] > 0.3)]print(f"\nHigh Conviction Bullish: {len(high_conviction)}")Step 6b: Single Ticker Price Forecasts
Section titled “Step 6b: Single Ticker Price Forecasts”Get price forecast time series for a single ticker:
# Get price forecasts as DataFrameforecasts = fb.predictions.ticker("AAPL", prediction_type="daily", as_dataframe=True)
print(forecasts)# main lower upper# date# 2024-11-04 201.33 197.21 205.45# 2024-11-05 202.77 196.92 208.61# 2024-11-06 203.99 196.90 211.08
# Calculate expected return from current pricecurrent_price = 200.0 # Exampleforecasts["expected_return"] = (forecasts["main"] - current_price) / current_price * 100print(forecasts)Step 7: Complete Backtesting Script
Section titled “Step 7: Complete Backtesting Script”Here’s a complete script using DataFrames for clean, efficient analysis:
from finbrain import FinBrainClientimport pandas as pd
fb = FinBrainClient(api_key="YOUR_API_KEY")
def screen_market(market="S&P 500"): """Screen entire market using DataFrame output""" # Get all predictions in one call df = fb.predictions.market(market, prediction_type="daily", as_dataframe=True)
# Generate signal scores df["score"] = 0
# Prediction signals df.loc[df["expectedShort"] > 2, "score"] += 2 df.loc[(df["expectedShort"] > 1) & (df["expectedShort"] <= 2), "score"] += 1 df.loc[df["expectedShort"] < -2, "score"] -= 2 df.loc[(df["expectedShort"] < -1) & (df["expectedShort"] >= -2), "score"] -= 1
# Sentiment signals df.loc[df["sentimentScore"] > 0.3, "score"] += 1 df.loc[df["sentimentScore"] < -0.3, "score"] -= 1
# Generate signal labels df["signal"] = "hold" df.loc[df["score"] >= 3, "signal"] = "strong_buy" df.loc[(df["score"] >= 1) & (df["score"] < 3), "signal"] = "buy" df.loc[df["score"] <= -3, "signal"] = "strong_sell" df.loc[(df["score"] <= -1) & (df["score"] > -3), "signal"] = "sell"
return df
# Run the screenerresults = screen_market("S&P 500")
print("=== Market Screening Results ===")print(f"Total tickers: {len(results)}")print(f"Strong Buy: {len(results[results['signal'] == 'strong_buy'])}")print(f"Buy: {len(results[results['signal'] == 'buy'])}")print(f"Hold: {len(results[results['signal'] == 'hold'])}")print(f"Sell: {len(results[results['signal'] == 'sell'])}")print(f"Strong Sell: {len(results[results['signal'] == 'strong_sell'])}")
# Top picksprint("\n=== Top 10 Bullish Picks ===")top_picks = results.nlargest(10, "score")print(top_picks[["expectedShort", "expectedLong", "sentimentScore", "score", "signal"]])
# Bottom picks (potential shorts)print("\n=== Top 10 Bearish Picks ===")bottom_picks = results.nsmallest(10, "score")print(bottom_picks[["expectedShort", "expectedLong", "sentimentScore", "score", "signal"]])Combining Multiple Data Sources
Section titled “Combining Multiple Data Sources”def comprehensive_analysis(market, ticker): """Combine predictions, sentiment, and insider data""" fb = FinBrainClient(api_key="YOUR_API_KEY")
# Get all data as DataFrames predictions = fb.predictions.ticker(ticker, prediction_type="daily", as_dataframe=True) sentiment = fb.sentiments.ticker(market, ticker, as_dataframe=True) insiders = fb.insider_transactions.ticker(market, ticker, as_dataframe=True)
print(f"=== {ticker} Analysis ===")
# Price forecasts print("\nPrice Forecasts:") print(predictions)
# Sentiment trend print("\nSentiment Trend:") print(sentiment.tail(5)) print(f"Avg Sentiment (5d): {sentiment['sentiment'].tail(5).mean():.3f}")
# Recent insider activity print("\nRecent Insider Transactions:") if len(insiders) > 0: print(insiders[["insiderTradings", "transaction", "shares", "USDValue"]].head(5))
return predictions, sentiment, insiders
# Analyze a specific tickerpreds, sent, ins = comprehensive_analysis("S&P 500", "AAPL")Best Practices
Section titled “Best Practices”- Use sufficient historical data - At least 1 year for meaningful results
- Account for transaction costs - Include commissions and slippage
- Avoid overfitting - Don’t optimize parameters on the same data you test
- Consider market conditions - Results may vary in bull vs bear markets
- Combine multiple signals - AI predictions + sentiment + insider data