MCP Server live — AI agents can now query 105M+ SEC facts. Connect your agent →
ValueinValuein
advanced
15 min

Multi-Factor Portfolio Backtest

Combine ROE, FCF yield, and Piotroski F-score into a simple long-short factor portfolio.

factorbacktestPITDuckDB
## Multi-Factor Portfolio This example combines 3 factors from the factor_scores table into a simple composite and tests whether the top quintile outperformed the bottom quintile historically.
python
import pandas as pd
from valuein_sdk import ValueinClient, ValueinError

try:
    with ValueinClient() as client:
        # Get factor scores for S&P 500
        factor_df = client.query("""
          SELECT 
            r.symbol, r.name, r.sector,
            fs.roe_rank,
            fs.fcf_to_assets_rank,
            fs.piotroski_f_score_rank,
            fs.composite_rank,
            -- Custom 3-factor composite
            (fs.roe_rank + fs.fcf_to_assets_rank + fs.piotroski_f_score_rank) / 3 AS custom_composite
          FROM factor_scores fs
          JOIN references r USING (entity_id)
          WHERE r.is_sp500 = TRUE
            AND r.symbol IS NOT NULL
          ORDER BY custom_composite DESC
        """)

        # Quintile breakdown
        factor_df['quintile'] = pd.qcut(factor_df['custom_composite'], 5, labels=['Q1','Q2','Q3','Q4','Q5'])
        print(factor_df.groupby('quintile')[['roe_rank','fcf_to_assets_rank','piotroski_f_score_rank']].mean())

        # Top 20 (long basket)
        print("\nTop 20 — long basket:")
        print(factor_df.head(20)[['symbol','name','sector','custom_composite']].to_string())

except ValueinError as e:
    print(f"Error: {e}")

Try it yourself

Get your API token and run this notebook against 105M+ real SEC EDGAR facts.