Skip to content

Quickstart

This guide walks through five progressively complex examples — from a minimal optimization to a full pipeline with stock selection, regime blending, and rebalancing.

1. Basic Optimization

The simplest use case: maximize the Sharpe ratio with walk-forward validation.

import pandas as pd
from optimizer.optimization import MeanRiskConfig, build_mean_risk
from optimizer.pipeline import run_full_pipeline
from optimizer.validation import WalkForwardConfig

# Load price data (DatetimeIndex, one column per asset)
prices = pd.read_csv("prices.csv", index_col=0, parse_dates=True)

# Build optimizer from config
optimizer = build_mean_risk(MeanRiskConfig.for_max_sharpe())

# Run end-to-end pipeline
result = run_full_pipeline(
    prices=prices,
    optimizer=optimizer,
    cv_config=WalkForwardConfig.for_quarterly_rolling(),
)

print(result.weights)          # pd.Series of asset weights
print(result.summary)          # dict with Sharpe, max drawdown, etc.
print(result.backtest)         # out-of-sample MultiPeriodPortfolio

run_full_pipeline handles everything internally: price-to-return conversion, pre-selection, optimization, cross-validation, and backtesting. See the Pipeline Overview for the full data flow.

2. Custom Pre-selection and Moments

Control which assets survive filtering and how expected returns and covariance are estimated.

from optimizer.pre_selection import PreSelectionConfig
from optimizer.moments import MomentEstimationConfig
from optimizer.optimization import MeanRiskConfig, build_mean_risk
from optimizer.pipeline import run_full_pipeline

# Drop correlated assets (>85%) and keep top 30 by variance
preselection = PreSelectionConfig(
    correlation_threshold=0.85,
    top_k=30,
)

# Shrunk mu + denoised covariance
moments = MomentEstimationConfig.for_shrunk_denoised()

# Minimum CVaR optimization
optimizer = build_mean_risk(
    MeanRiskConfig.for_min_cvar(beta=0.95),
    moment_config=moments,
)

result = run_full_pipeline(
    prices=prices,
    optimizer=optimizer,
    preselection_config=preselection,
    sector_mapping={"AAPL": "Tech", "JPM": "Financials", ...},
)

The sector_mapping dict enables sector-aware imputation during preprocessing. See Preprocessing and Pre-selection.

3. Black-Litterman Views

Incorporate analyst views into the optimization through the Black-Litterman framework.

from optimizer.views import BlackLittermanConfig
from optimizer.moments import MomentEstimationConfig
from optimizer.optimization import MeanRiskConfig, build_mean_risk
from optimizer.pipeline import run_full_pipeline

# Define views: AAPL returns 12%, MSFT outperforms GOOG by 3%
bl_config = BlackLittermanConfig.for_equilibrium(
    views=("AAPL == 0.12", "MSFT - GOOG == 0.03"),
    tau=0.05,
)

# Build optimizer with BL prior
optimizer = build_mean_risk(
    MeanRiskConfig.for_max_utility(risk_aversion=1.0),
    bl_config=bl_config,
)

result = run_full_pipeline(prices=prices, optimizer=optimizer)
print(result.weights)

Views use a string syntax: "TICKER == value" for absolute views, "TICKER1 - TICKER2 == value" for relative views. See Views for Entropy Pooling and Opinion Pooling alternatives.

4. HMM Regime Blending

Use a Hidden Markov Model to blend moments across market regimes, producing estimates that adapt to the current regime.

from skfolio.preprocessing import prices_to_returns
from optimizer.moments import (
    HMMConfig,
    fit_hmm,
    HMMBlendedMu,
    HMMBlendedCovariance,
    MomentEstimationConfig,
)
from optimizer.optimization import MeanRiskConfig, build_mean_risk
from optimizer.pipeline import run_full_pipeline

returns = prices_to_returns(prices)

# Fit 2-state HMM
hmm_result = fit_hmm(returns.values, config=HMMConfig(n_states=2))
print(f"Current regime: {hmm_result.filtered_probs[-1]}")

# Build optimizer with regime-blended moments
optimizer = build_mean_risk(
    MeanRiskConfig.for_max_sharpe(),
    moment_config=MomentEstimationConfig.for_hmm_blended(),
)

result = run_full_pipeline(prices=prices, optimizer=optimizer)

HMMBlendedCovariance uses the full law of total variance (including between-regime mean dispersion), while blend_moments_by_regime() uses within-regime covariance only. Use the class for optimizer inputs. See Moments for details.

5. Full Pipeline with Rebalancing

Combine optimization with threshold-based rebalancing to determine whether to trade.

import numpy as np
import pandas as pd
from optimizer.optimization import MeanRiskConfig, build_mean_risk
from optimizer.rebalancing import HybridRebalancingConfig
from optimizer.pipeline import run_full_pipeline

optimizer = build_mean_risk(MeanRiskConfig.for_max_sharpe())

# Current portfolio weights (from previous period)
previous_weights = np.array([0.25, 0.25, 0.25, 0.25])

result = run_full_pipeline(
    prices=prices,
    optimizer=optimizer,
    previous_weights=previous_weights,
    rebalancing_config=HybridRebalancingConfig.for_monthly_with_5pct_threshold(),
    current_date=pd.Timestamp("2024-06-28"),
    last_review_date=pd.Timestamp("2024-05-31"),
)

if result.rebalance_needed:
    print(f"Rebalance! Turnover: {result.turnover:.2%}")
    print(f"New weights: {result.weights}")
else:
    print("No rebalance needed — drift within threshold")

Hybrid rebalancing checks drift only at calendar review dates, preventing over-trading between reviews. See Rebalancing for calendar, threshold, and hybrid strategies.

Next Steps

Want to... Read
Understand the full data flow Pipeline Overview
Clean and impute return data Preprocessing
Estimate expected returns and covariance Moments
Add analyst views Views
Choose an optimization model Optimization
Validate out-of-sample Validation
Tune hyperparameters Tuning
Run factor-based stock selection Factors
Generate synthetic scenarios Synthetic Data
Screen an investable universe Universe Screening

See the examples/ directory for complete, runnable scripts.