A comprehensive end-to-end portfolio construction and backtesting pipeline for PortfolioBench.
The pipeline system provides:
- End-to-end orchestration of alpha → strategy → portfolio → backtest
- Flexible configuration via JSON, YAML, or Python
- Comprehensive validation at each stage
- Result aggregation and reporting (HTML, CSV, JSON)
- Freqtrade integration for dashboard and UI
- Batch execution for comparing multiple strategies
- Preset configurations for common scenarios
from pipeline.config import PresetConfigs
from pipeline.orchestrator import run_pipeline
# Simple EMA cross strategy
config = PresetConfigs.simple_ema_cross()
result = run_pipeline(config)
# Balanced multi-alpha strategy
config = PresetConfigs.balanced_multi_alpha()
result = run_pipeline(config)
# Risk parity portfolio
config = PresetConfigs.risk_parity_portfolio()
result = run_pipeline(config)from pipeline.config import PipelineConfig
from pipeline.orchestrator import run_pipeline
# Load from JSON
config = PipelineConfig.from_file("pipelines/simple_ema_cross.json")
result = run_pipeline(config)
# Load from YAML
config = PipelineConfig.from_yaml("pipelines/custom_strategy.yaml")
result = run_pipeline(config)from pipeline.config import (
PipelineConfig, AlphaConfig, StrategyConfig,
PortfolioConfig, BacktestConfig, AlphaType,
StrategyType, PortfolioAlgorithm
)
config = PipelineConfig(
name="Custom Strategy",
alpha=[
AlphaConfig(type=AlphaType.EMA),
AlphaConfig(type=AlphaType.RSI),
],
strategies=[
StrategyConfig(
type=StrategyType.EMA_CROSS,
alpha_factors=[AlphaConfig(type=AlphaType.EMA)]
),
],
portfolio=PortfolioConfig(
algorithm=PortfolioAlgorithm.ONS,
strategies=[StrategyConfig(type=StrategyType.EMA_CROSS)],
strategy_weights={"ema_cross": 1.0},
params={"eta": 0.0, "beta": 1.0, "delta": 0.125}
),
backtest=BacktestConfig(
timerange="20250101-20250601",
timeframe="4h",
pairs=["BTC/USDT", "ETH/USDT", "SOL/USDT"],
initial_capital=50000.0
),
)
result = run_pipeline(config)PipelineConfig- Top-level configuration containerAlphaConfig- Alpha factor configurationStrategyConfig- Trading strategy configurationPortfolioConfig- Portfolio optimization configurationBacktestConfig- Backtesting parametersDataConfig- Data source configurationPresetConfigs- Pre-built configurations for common scenarios
PortfolioPipeline- Main orchestrator classrun_pipeline()- Convenience function
Pipeline stages:
- Load OHLCV data
- Generate alpha signals
- Compute strategy signals
- Construct portfolio weights
- Run backtest
- Compute metrics and validation
PipelineVerification- Validation orchestratorValidationResult- Individual validation result
Validation checks:
- Data integrity: pairs loaded, completeness, alignment, OHLC relationships, volume
- Alpha signals: required columns, no NaN, signal ranges
- Strategy signals: binary signals, signal density
- Portfolio weights: sum to 1.0, bounds [0,1], concentration
- Backtest results: required columns, portfolio value validity, return statistics
PipelineResult- Complete execution resultStageOutput- Individual stage output
Result features:
- Stage-by-stage tracking (duration, status, errors, warnings)
- Data preservation (prices, weights, backtest results)
- Metrics computation and aggregation
- Multiple export formats (JSON, CSV, HTML)
- HTML report generation
FreqtradeIntegration- Convert to/from freqtrade formatBatchPipelineRunner- Run multiple pipelinesPresetPipelineRunner- Convenience runnersFreqtradeStrategyExporter- Export as IStrategyPipelineComparator- Compare results
{
"name": "My Strategy",
"version": "1.0",
"description": "Strategy description",
"alpha": [
{
"type": "ema",
"params": {"fast_period": 12, "slow_period": 26}
}
],
"strategies": [
{
"type": "ema_cross",
"alpha_factors": [{"type": "ema"}]
}
],
"portfolio": {
"algorithm": "ons",
"strategies": [{"type": "ema_cross"}],
"strategy_weights": {"ema_cross": 1.0},
"params": {"eta": 0.0, "beta": 1.0}
},
"backtest": {
"timerange": "20250101-20250601",
"timeframe": "4h",
"pairs": ["BTC/USDT", "ETH/USDT"],
"initial_capital": 10000.0
},
"data": {
"data_dir": "./user_data/data/usstock",
"exchange": "portfoliobench"
},
"enable_validation": true,
"output_dir": "./output"
}name: "My Strategy"
version: "1.0"
alpha:
- type: ema
params:
fast_period: 12
slow_period: 26
strategies:
- type: ema_cross
alpha_factors:
- type: ema
portfolio:
algorithm: ons
strategies:
- type: ema_cross
strategy_weights:
ema_cross: 1.0
params:
eta: 0.0
beta: 1.0
backtest:
timerange: "20250101-20250601"
timeframe: "4h"
pairs:
- BTC/USDT
- ETH/USDT
initial_capital: 10000.0
data:
data_dir: "./user_data/data/usstock"
exchange: "portfoliobench"
enable_validation: true
output_dir: "./output"| Type | Indicators | Configuration |
|---|---|---|
| ema | EMA fast/slow/exit, mean-volume | fast_period, slow_period, exit_period |
| rsi | RSI, signal line, overbought/oversold | period, overbought, oversold |
| macd | MACD, signal, histogram | fast_period, slow_period, signal_period |
| bollinger | Bands, bandwidth, %B | period, std_dev |
| polymarket | Probability, momentum, volume | Parameters vary |
| Type | Entry Signal | Exit Signal | Alpha Factors |
|---|---|---|---|
| ema_cross | EMA fast > EMA slow + volume filter | EMA exit < EMA fast | EMA |
| rsi_bollinger | RSI + Bollinger Bands | RSI + Bollinger Bands reversal | RSI |
| macd_adx | MACD > signal + ADX > 25 | MACD < signal + ADX > 25 | MACD |
| ichimoku | Ichimoku Cloud signals | Ichimoku Cloud reversal | - |
| stochastic_cci | Stochastic + CCI | Stochastic + CCI reversal | - |
| mlp_speculative | MLP model predictions | MLP model predictions | - |
| polymarket_momentum | Contract momentum | Momentum exit | Polymarket |
| polymarket_mean_reversion | Contract reversion | Reversion exit | Polymarket |
| Algorithm | Method | Rebalance | Parameters |
|---|---|---|---|
| ons | Online Newton Step convex optimization | Per-candle | eta, beta, delta |
| inverse_volatility | Weight ∝ 1/volatility | Monthly | lookback_window |
| min_variance | Minimize portfolio variance | Monthly | target_vol |
| best_single_asset | Momentum rotation (winner-takes-all) | Monthly | lookback_window |
| exponential_gradient | Multiplicative weight update | Per-candle | learning_rate |
| max_sharpe | Maximize Sharpe ratio | Monthly | target_vol |
| risk_parity | Equal risk contribution | Monthly | target_vol, min/max_weight |
| polymarket | Prediction market weighted | Monthly | Parameters vary |
| equal_weight | 1/N allocation | Static | - |
from pipeline.orchestrator import run_pipeline
result = run_pipeline("pipelines/simple_ema_cross.json")
# Access results
print(f"Total return: {result.metrics['total_return_pct']}%")
print(f"Sharpe ratio: {result.metrics['annualised_sharpe']:.4f}")
# Save outputs
result.save_all("./output/my_run")from pipeline.integrations import (
BatchPipelineRunner,
PipelineComparator,
)
runner = BatchPipelineRunner()
configs = [
"pipelines/simple_ema_cross.json",
"pipelines/balanced_multi_alpha.json",
"pipelines/risk_parity.json",
]
results = runner.run_multiple(configs)
# Compare results
PipelineComparator.print_comparison(results)from pipeline.integrations import FreqtradeIntegration
# Get result from pipeline
result = run_pipeline(config)
# Convert config to freqtrade format
ft_config = FreqtradeIntegration.config_from_pipeline(result.config)
# Export weights and backtest results
FreqtradeIntegration.export_backtest_results_for_freqtrade(
result.backtest_result,
result.portfolio_weights,
"./output/freqtrade_export"
)from pipeline.config import (
PipelineConfig, AlphaConfig, StrategyConfig,
PortfolioConfig, BacktestConfig
)
from pipeline.orchestrator import PortfolioPipeline
config = PipelineConfig(
name="Custom Strategy",
alpha=[AlphaConfig(type="ema"), AlphaConfig(type="rsi")],
strategies=[
StrategyConfig(type="ema_cross", alpha_factors=[AlphaConfig(type="ema")])
],
portfolio=PortfolioConfig(
algorithm="ons",
strategy_weights={"ema_cross": 1.0}
),
backtest=BacktestConfig(
timerange="20250101-20250601",
pairs=["BTC/USDT", "ETH/USDT", "AAPL/USD"]
)
)
pipeline = PortfolioPipeline(config, verbose=True)
result = pipeline.run()
result.print_summary()result.
├── pipeline_name: str
├── start_time: str (ISO format)
├── end_time: str (ISO format)
├── duration_s: float
├── stages: Dict[str, StageOutput]
├── pair_data: pd.DataFrame (prices)
├── enriched_data: Dict[str, pd.DataFrame] (with alpha signals)
├── strategy_signals: Dict[str, pd.Series]
├── portfolio_weights: pd.DataFrame
├── backtest_result: pd.DataFrame (dates, values, returns)
├── metrics: Dict[str, float] (return, sharpe, drawdown, etc.)
├── validation: Dict[str, Any] (validation summary)
└── config: Dict[str, Any] (configuration used)
JSON Report:
{
"pipeline_name": "Simple EMA Cross",
"start_time": "2026-01-15T10:30:00",
"end_time": "2026-01-15T10:35:45",
"duration_s": 345.2,
"stages": {...},
"metrics": {
"total_return_pct": 23.45,
"annualised_return_pct": 18.92,
"annualised_sharpe": 1.2345,
"max_drawdown_pct": -8.73
},
"validation": {
"total": 15,
"passed": 14,
"failed": 1,
"pass_rate": 93.3
}
}HTML Report: Auto-generated with metrics dashboard
CSV Files:
portfolio_weights.csv- Weight matrixbacktest_results.csv- Daily portfolio values and returns
The pipeline includes comprehensive validation at each stage:
# Enable all validation checks
config.enable_validation = True
config.validate_data_integrity = True
config.validate_alpha_signals = True
config.validate_strategy_signals = True
config.validate_portfolio_weights = True
result = run_pipeline(config)
# View validation results
print(result.validation["pass_rate"]) # 0-100%
for check in result.validation["results"]:
print(f"{check['name']}: {check['passed']}")Available checks:
- Data completeness and alignment
- OHLC relationship validity
- Alpha signal ranges
- Strategy signal density
- Portfolio weight constraints
- Backtest result validity
Run pipelines from command line:
# Run from configuration
portbench pipeline pipelines/simple_ema_cross.json
# Run with output directory
portbench pipeline pipelines/balanced_multi_alpha.json --output output/results
# Run with verbose logging
portbench pipeline config.yaml --verbose
# Compare multiple pipelines
portbench pipeline-batch pipelines/ --compare- Data loading: Cached after first run
- Alpha computation: Uses TA-Lib (vendored)
- Portfolio optimization: Uses NumPy/SciPy
- Backtesting: Vectorized with Pandas
For large date ranges or many assets, consider:
- Filtering to relevant timeframes
- Reducing rebalance frequency
- Using simpler alpha factors
- Running in parallel with
BatchPipelineRunner
# Check data directory
config.data.data_dir = "./user_data/data/usstock"pip install ta-lib
# or use vendored version in requirements-ml.txt# Enable verbose logging
config.verbose = True
# Check validation report
result = run_pipeline(config)
print(result.validation)# Reduce computation scope
config.backtest.timeframe = "1d" # Fewer candles
config.backtest.timerange = "20250101-20250201" # Shorter rangeSee alpha/interface.py for IAlpha base class.
See strategy/ for strategy implementations.
See user_data/strategies/ for portfolio strategy templates.
Export results and visualize in freqtrade dashboard:
from pipeline.integrations import FreqtradeStrategyExporter
FreqtradeStrategyExporter.export_to_strategy_file(
result,
"./strategy/PortfolioBench_Strategy.py"
)See inline docstrings and examples in:
pipeline/config.py- Configuration objectspipeline/orchestrator.py- Pipeline executionpipeline/verification.py- Validation frameworkpipeline/results.py- Result handlingpipeline/integrations.py- Integrations
Part of PortfolioBench. See LICENSE file for details.