Version: 0.0.1
Python: 3.11+
Complete API reference for the alphaweave backtesting framework.
- Overview
- Core Types
- Frame Abstraction
- Data Loaders
- Corporate Actions
- Strategy API
- Factor Library & Pipeline API
- Backtesting Engine
- Results
- Utilities
alphaweave provides a minimal but production-ready backtesting framework with:
- Unified data interface: Works with both Pandas and Polars DataFrames
- Simple strategy API: Minimal base class for implementing trading strategies
- Vector backtester: Bar-by-bar execution engine with naive order execution
- Type-safe core: Dataclasses for all core trading concepts
OHLCV bar data container.
@dataclass
class Bar:
datetime: datetime
open: float
high: float
low: float
close: float
volume: Optional[float] = None
symbol: Optional[str] = NoneAttributes:
datetime(datetime): Bar timestampopen(float): Opening pricehigh(float): High pricelow(float): Low priceclose(float): Closing pricevolume(Optional[float]): Trading volumesymbol(Optional[str]): Symbol identifier
Example:
from alphaweave.core.types import Bar
from datetime import datetime
bar = Bar(
datetime=datetime(2020, 1, 1),
open=100.0,
high=105.0,
low=99.0,
close=103.0,
volume=1000000.0,
symbol="AAPL"
)Order request container.
@dataclass
class Order:
id: int
symbol: str
size: float # positive = buy, negative = sell
price: Optional[float] = None
order_type: str = "market"Attributes:
id(int): Unique order identifiersymbol(str): Symbol to tradesize(float): Order size (positive = buy, negative = sell)price(Optional[float]): Limit price (if applicable)order_type(str): Order type, default "market"
Executed order fill record.
@dataclass
class Fill:
order_id: int
symbol: str
size: float
price: float
datetime: datetimeAttributes:
order_id(int): Associated order IDsymbol(str): Traded symbolsize(float): Fill sizeprice(float): Execution pricedatetime(datetime): Fill timestamp
Portfolio position container.
@dataclass
class Position:
symbol: str
size: float
avg_price: floatAttributes:
symbol(str): Position symbolsize(float): Position size (positive = long, negative = short)avg_price(float): Average entry price
Canonical Frame abstraction wrapping a pandas or polars DataFrame. Provides a unified interface for working with time-series OHLCV data regardless of the underlying DataFrame library.
Frame(backend_obj: Any)Initialize Frame with a pandas or polars DataFrame.
Parameters:
backend_obj: pandas DataFrame or polars DataFrame
Raises:
TypeError: If backend_obj is not a pandas or polars DataFrame
Create Frame from pandas DataFrame and validate/normalize columns.
Parameters:
df(pd.DataFrame): pandas DataFrame with OHLCV data
Returns:
Frame: Frame instance with normalized columns and datetime index
Raises:
ValueError: If required columns are missing or datetime cannot be determined
Example:
import pandas as pd
from alphaweave.core.frame import Frame
df = pd.DataFrame({
"datetime": pd.date_range("2020-01-01", periods=10, freq="D"),
"open": [100 + i for i in range(10)],
"high": [101 + i for i in range(10)],
"low": [99 + i for i in range(10)],
"close": [100 + i for i in range(10)],
"volume": [1000000] * 10
})
frame = Frame.from_pandas(df)Create Frame from polars DataFrame and validate/normalize columns.
Parameters:
pl_df(pl.DataFrame): polars DataFrame with OHLCV data
Returns:
Frame: Frame instance with normalized columns and datetime index
Raises:
ValueError: If required columns are missing or datetime cannot be determined
Example:
import polars as pl
from alphaweave.core.frame import Frame
pl_df = pl.DataFrame({
"datetime": pl.date_range("2020-01-01", "2020-01-10", "1d"),
"open": [100 + i for i in range(10)],
"close": [100 + i for i in range(10)],
# ... other columns
})
frame = Frame.from_polars(pl_df)Convert Frame to pandas DataFrame.
Returns:
pd.DataFrame: pandas DataFrame with datetime index
Note: The returned DataFrame will have a datetime index. If the Frame was created from polars, the datetime column will be converted to the index.
Convert Frame to polars DataFrame.
Returns:
pl.DataFrame: polars DataFrame with datetime as a column
Note: The returned DataFrame will have datetime as a column (polars doesn't support named indexes). The datetime column is named "datetime".
Raise ValueError if datetime column/index missing or no required columns.
Raises:
ValueError: If validation fails (missing datetime index or required OHLC columns)
Required columns:
open,high,low,close(minimum)volume(optional but recommended)
Column normalization: The Frame automatically normalizes column names:
- Datetime variations:
timestamp,dt,date,time→datetime - OHLCV variations:
o→open,h→high,l→low,c→close,v/vol→volume - All column names are lowercased
load_csv(path: str, symbol: Optional[str] = None) -> FrameLoad CSV file into a Frame. Automatically normalizes column names to canonical form.
Parameters:
path(str): Path to CSV filesymbol(Optional[str]): Optional symbol name to add as a column to the frame
Returns:
Frame: Frame instance with normalized columns and datetime index
Example:
from alphaweave.data.loaders import load_csv
# Load CSV with automatic column normalization
frame = load_csv("data/AAPL.csv", symbol="AAPL")
# Access as pandas DataFrame
df = frame.to_pandas()Supported column name variations:
- Datetime:
Timestamp,timestamp,dt,date,time - OHLCV:
Open/open,High/high,Low/low,Close/close,Volume/volume
load_parquet(path: str, symbol: Optional[str] = None) -> FrameLoad Parquet file into a Frame. Automatically normalizes column names.
Parameters:
path(str): Path to Parquet filesymbol(Optional[str]): Optional symbol name to add as a column to the frame
Returns:
Frame: Frame instance with normalized columns and datetime index
Example:
from alphaweave.data.loaders import load_parquet
frame = load_parquet("data/AAPL.parquet", symbol="AAPL")Represents a stock split action.
@dataclass
class SplitAction:
symbol: str
date: datetime
ratio: float # e.g., 2.0 for a 2-for-1 splitAttributes:
symbol(str): Stock symboldate(datetime): Split dateratio(float): Split ratio (e.g., 2.0 for 2-for-1, 4.0 for 4-for-1)
Example:
from alphaweave.data.corporate_actions import SplitAction
from datetime import datetime
split = SplitAction(
symbol="AAPL",
date=datetime(2020, 8, 31),
ratio=4.0 # 4-for-1 split
)Represents a cash dividend action.
@dataclass
class DividendAction:
symbol: str
date: datetime
amount: float # dividend per shareAttributes:
symbol(str): Stock symboldate(datetime): Dividend payment dateamount(float): Dividend amount per share
Example:
from alphaweave.data.corporate_actions import DividendAction
from datetime import datetime
dividend = DividendAction(
symbol="AAPL",
date=datetime(2023, 11, 16),
amount=0.24 # $0.24 per share
)Stores and provides access to corporate actions by symbol and date.
class CorporateActionsStore:
def add_split(self, split: SplitAction) -> None
def add_dividend(self, dividend: DividendAction) -> None
def get_splits_on_date(self, symbol: str, date: datetime) -> List[SplitAction]
def get_dividends_on_date(self, symbol: str, date: datetime) -> List[DividendAction]
def has_actions_for_symbol(self, symbol: str) -> boolMethods:
add_split(split: SplitAction) -> None: Add a split action to the storeadd_dividend(dividend: DividendAction) -> None: Add a dividend action to the storeget_splits_on_date(symbol: str, date: datetime) -> List[SplitAction]: Get all splits for a symbol on a specific dateget_dividends_on_date(symbol: str, date: datetime) -> List[DividendAction]: Get all dividends for a symbol on a specific datehas_actions_for_symbol(symbol: str) -> bool: Check if there are any corporate actions for a symbol
Example:
from alphaweave.data.corporate_actions import (
CorporateActionsStore,
SplitAction,
DividendAction,
)
from datetime import datetime
store = CorporateActionsStore()
# Add a split
split = SplitAction(symbol="AAPL", date=datetime(2020, 8, 31), ratio=4.0)
store.add_split(split)
# Add a dividend
dividend = DividendAction(symbol="AAPL", date=datetime(2023, 11, 16), amount=0.24)
store.add_dividend(dividend)
# Retrieve actions
splits = store.get_splits_on_date("AAPL", datetime(2020, 8, 31))
dividends = store.get_dividends_on_date("AAPL", datetime(2023, 11, 16))load_splits_csv(path: str) -> List[SplitAction]Load split actions from a CSV file.
Expected CSV format:
symbol,date,ratio
AAPL,2020-08-31,4.0
MSFT,2003-02-18,2.0Parameters:
path(str): Path to CSV file
Returns:
List[SplitAction]: List of split actions
Example:
from alphaweave.data.corporate_actions import load_splits_csv
splits = load_splits_csv("data/splits.csv")load_dividends_csv(path: str) -> List[DividendAction]Load dividend actions from a CSV file.
Expected CSV format:
symbol,date,amount
AAPL,2023-11-16,0.24
MSFT,2023-11-15,0.75Parameters:
path(str): Path to CSV file
Returns:
List[DividendAction]: List of dividend actions
Example:
from alphaweave.data.corporate_actions import load_dividends_csv
dividends = load_dividends_csv("data/dividends.csv")build_corporate_actions_store(
splits: Optional[List[SplitAction]] = None,
dividends: Optional[List[DividendAction]] = None,
) -> CorporateActionsStoreBuild a CorporateActionsStore from lists of splits and dividends.
Parameters:
splits(Optional[List[SplitAction]]): Optional list of split actionsdividends(Optional[List[DividendAction]]): Optional list of dividend actions
Returns:
CorporateActionsStore: Store containing all provided actions
Example:
from alphaweave.data.corporate_actions import (
build_corporate_actions_store,
SplitAction,
DividendAction,
)
from datetime import datetime
splits = [
SplitAction(symbol="AAPL", date=datetime(2020, 8, 31), ratio=4.0),
]
dividends = [
DividendAction(symbol="AAPL", date=datetime(2023, 11, 16), amount=0.24),
]
store = build_corporate_actions_store(splits=splits, dividends=dividends)apply_split(symbol: str, ratio: float) -> NoneApply a stock split to a position. Adjusts position size and cost basis per share so that total position cost remains unchanged.
Parameters:
symbol(str): Symbol of the position to splitratio(float): Split ratio (e.g., 2.0 for a 2-for-1 split)
Behavior:
- Multiplies position size by ratio
- Divides cost basis per share by ratio
- Keeps total position cost unchanged
Example:
from alphaweave.engine.portfolio import Portfolio
portfolio = Portfolio(starting_cash=10000.0)
# ... after buying 100 shares at $50 ...
# Apply 2-for-1 split
portfolio.apply_split("AAPL", ratio=2.0)
# Now: 200 shares at $25 avg_price (total cost unchanged)Base class for implementing trading strategies. Subclasses must implement the next() method.
Strategy(data: Union[Frame, Dict[str, Frame]])Initialize strategy with data.
Parameters:
data: Either a single Frame or a dictionary mapping symbol names to Frame objects
Example:
from alphaweave.strategy.base import Strategy
from alphaweave.core.frame import Frame
# Single asset
strategy = MyStrategy(frame)
# Multiple assets
strategy = MyStrategy({"AAPL": frame_aapl, "MSFT": frame_msft})Called once before the backtest loop starts. Override this method to perform initialization tasks.
Example:
class MyStrategy(Strategy):
def init(self):
self.sma_period = 20
self.position = 0.0Called once per bar during the backtest loop. Must be implemented by subclasses.
Parameters:
index: Current bar index (integer or pandas.Timestamp)
Example:
class MyStrategy(Strategy):
def next(self, i):
# Access data
df = self.data["AAPL"].to_pandas()
current_price = df.iloc[i]["close"]
# Place orders
if current_price > 100:
self.order_target_percent("AAPL", 1.0) # Go 100% long
else:
self.order_target_percent("AAPL", 0.0) # Exit positionRequest to set target percentage of portfolio in a symbol.
Parameters:
symbol(str): Symbol to targettarget(float): Target percentage (0.0 to 1.0, where 1.0 = 100% of portfolio)
Example:
# Allocate 50% of portfolio to AAPL
self.order_target_percent("AAPL", 0.5)
# Exit position (0%)
self.order_target_percent("AAPL", 0.0)
# Go 100% long
self.order_target_percent("AAPL", 1.0)Note: Orders are collected during each bar and executed at the end of the bar at the close price. The backtester will automatically calculate the required position size based on current portfolio equity.
Log a custom scalar metric. If the engine was provided with a monitor, the metric is forwarded (with the current timestamp) via Monitor.on_metric. If no monitor is attached, the call is a no-op.
Useful for tracking strategy-specific diagnostics like signal strength or risk estimates that later appear in dashboards.
Override to expose strategy-specific serializable state used for live persistence/restarts. Default returns {}.
Inverse of get_state. Called by live runners when restoring from persisted state. Default implementation is a no-op.
Protocol defining the interface for backtesters.
class BaseBacktester(Protocol):
def run(
self,
strategy_cls: Type[Strategy],
data: dict[str, Frame],
capital: float = 100_000.0,
) -> BacktestResult:
...Parameters:
strategy_cls: Strategy class to instantiatedata: Dictionary mapping symbol names to Frame objectscapital: Starting capital (default: 100,000)
Returns:
BacktestResult: Backtest results with equity series and trades
Vector backtester with naive bar-by-bar execution. Implements the BaseBacktester protocol.
The VectorBacktester implements a simple execution model:
- Strategy instantiation: Creates an instance of the strategy class with the provided data
- Initialization: Calls
strategy.init()once before the loop - Bar-by-bar loop:
- Clears strategy orders for the current bar
- Calls
strategy.next(index)to allow strategy to place orders - Processes orders:
- For
order_target_percent: Calculates target position size based on current portfolio equity and symbol price - Executes immediately at bar close price (no slippage, no fees)
- For
- Updates portfolio (cash, positions)
- Records equity value for the bar
- Returns:
BacktestResultwith equity series and trades
VectorBacktester.run() now accepts:
monitor(Optional[Monitor]): Hook that receiveson_run_start, per-barBarSnapshot, per-tradeTradeRecord, arbitrary metrics, andon_run_end.monitor_meta(Optional[Mapping[str, Any]]): Metadata merged into the dictionary passed tomonitor.on_run_start. By default the backtester reportsmode="backtest",performance_mode,trade_at_next_open, andcapital.
When monitor is supplied, Strategy.log_metric() will forward custom metrics to the same monitor.
Simple facade that reuses VectorBacktester for live/replay workflows while tagging monitor metadata with mode="live".
from alphaweave.live.engine import LiveEngine
engine = LiveEngine(monitor=my_monitor)
result = engine.run(MyStrategy, data={"AAPL": frame}, capital=10_000.0)Arguments mirror VectorBacktester.run, and you can still override monitor/monitor_meta per call.
- No slippage: Orders execute exactly at the close price
- No fees: No commission or transaction costs
- No partial fills: Orders execute fully or not at all
- No margin/leverage: Cash-only trading
- Single asset support: Multi-asset support is basic (all frames must have the same index)
Run the backtest and return results.
Parameters:
strategy_cls(Type[Strategy]): Strategy class to instantiatedata(Dict[str, Frame]): Dictionary mapping symbol names to Frame objectscapital(float): Starting capital (default: 100,000.0)fees(Optional[FeesModel]): Fees model (default: NoFees)slippage(Optional[SlippageModel]): Slippage model (default: NoSlippage)strategy_kwargs(Optional[Dict[str, Any]]): Keyword arguments to pass to strategy constructorvolume_limit(Optional[VolumeLimitModel]): Volume limit model (default: VolumeLimitModel)risk_limits(Optional[RiskLimits]): Risk limits (default: RiskLimits)corporate_actions(Optional[CorporateActionsStore]): Corporate actions store for splits and dividends (default: None)
Returns:
BacktestResult: Results containing equity series and trades
Corporate Actions:
When corporate_actions is provided, the backtester will:
- Apply stock splits before processing orders on each bar (adjusts position size and cost basis)
- Apply cash dividends before processing orders on each bar (credits cash to portfolio)
- Ensure portfolio value remains consistent across splits
Example:
from alphaweave.engine.vector import VectorBacktester
from alphaweave.core.frame import Frame
from alphaweave.data.corporate_actions import (
build_corporate_actions_store,
SplitAction,
DividendAction,
)
from datetime import datetime
# Prepare data
data = {
"AAPL": frame_aapl,
"MSFT": frame_msft
}
# Create corporate actions store
splits = [SplitAction(symbol="AAPL", date=datetime(2020, 8, 31), ratio=4.0)]
dividends = [DividendAction(symbol="AAPL", date=datetime(2023, 11, 16), amount=0.24)]
corporate_actions = build_corporate_actions_store(splits=splits, dividends=dividends)
# Run backtest
backtester = VectorBacktester()
result = backtester.run(
MyStrategy,
data,
capital=100000.0,
corporate_actions=corporate_actions
)
# Access results
print(f"Final equity: ${result.equity_series.iloc[-1]:,.2f}")
print(f"Number of trades: {len(result.trades)}")Portfolio Management:
- The backtester maintains cash and positions
- Positions are tracked with average entry price
- Equity is calculated as:
cash + sum(position_size * current_price) - Orders are limited by available cash (for buys) or position size (for sells)
Container for backtest results with comprehensive metrics and analytics.
BacktestResult(
equity_series: List[float],
trades: List[Any],
timestamps: Optional[List[Any]] = None
)Parameters:
equity_series(List[float]): List of equity values, one per bartrades(List[Any]): List of trade/fill records (Fill objects)timestamps(Optional[List[Any]]): Optional timestamps for equity series
equity_series(pd.Series): Equity curve values (indexed by timestamp if provided)trades(pd.DataFrame): Standardized trades DataFrame with columns:symbol,entry_time,exit_time,entry_price,exit_pricesize,pnl,pnl_pct,duration,fees,slippage,direction
returns(pd.Series): Returns series (pct_change of equity)
Final equity value.
Total return as a decimal (e.g., 0.10 for 10%).
Maximum drawdown as a decimal.
Annualized Sharpe ratio.
Rolling cumulative return over a lookback window.
Parameters:
window(str): Window size (e.g., "63D" for calendar, "63" for bars)freq(Literal): "calendar" for time-based or "bars" for bar-count
Returns:
pd.Series: Series of rolling returns
rolling_vol(window: str = "63D", freq: Literal["calendar", "bars"] = "calendar", annualize: bool = True, trading_days: int = 252) -> pd.Series
Rolling volatility of returns.
Parameters:
window(str): Window sizefreq(Literal): "calendar" or "bars"annualize(bool): If True, annualize the volatilitytrading_days(int): Trading days per year for annualization
Returns:
pd.Series: Series of rolling volatilities
rolling_sharpe(window: str = "63D", freq: Literal["calendar", "bars"] = "calendar", risk_free_rate: float = 0.0, trading_days: int = 252) -> pd.Series
Rolling Sharpe ratio over the given window.
Parameters:
window(str): Window sizefreq(Literal): "calendar" or "bars"risk_free_rate(float): Annual risk-free ratetrading_days(int): Trading days per year
Returns:
pd.Series: Series of rolling Sharpe ratios
Rolling max drawdown.
Parameters:
window(str): Window sizefreq(Literal): "calendar" or "bars"
Returns:
pd.Series: Series of rolling max drawdowns
Basic summary stats for trades.
Returns:
- Dictionary with
n_trades,win_rate,avg_win,avg_loss,expectancy,max_consecutive_wins,max_consecutive_losses,median_duration
Return distributions or precomputed quantiles for trade metrics.
Returns:
- Dictionary with quantiles for
pnl,pnl_pct,duration
Get TradeAnalytics helper for detailed trade analysis.
Returns:
TradeAnalyticsinstance with methods:by_symbol(),by_month(),pnl_curve()
Portfolio turnover per period.
Parameters:
freq(str): Resampling frequency (e.g., "1M", "1D")
Returns:
pd.Series: Series indexed by period end timestamp
Average slippage per share across all trades.
Time series of cumulative slippage cost.
Time series of cumulative fees/commissions.
Run factor regression on strategy returns.
Parameters:
factor_returns(pd.DataFrame): DataFrame with factor returns (columns = factors, index = datetime)**kwargs: Additional arguments for factor_regression
Returns:
FactorRegressionResultwith alpha, betas, R², t-stats, etc.
Example:
factor_returns = pd.DataFrame({
"SPY": spy_returns,
"MKT": market_returns,
}, index=dates)
factor_result = result.factor_regression(factor_returns)
print(f"Alpha: {factor_result.alpha:.4f}")
print(f"Beta (SPY): {factor_result.betas['SPY']:.4f}")
print(f"R²: {factor_result.r2:.4f}")Plot the equity curve using matplotlib.
Example:
result = backtester.run(MyStrategy, data)
result.plot_equity() # Opens matplotlib windowFactor regression and decomposition.
factor_regression(strategy_returns: pd.Series, factor_returns: pd.DataFrame, *, add_constant: bool = True) -> FactorRegressionResult
Run a simple OLS regression of strategy returns on factor returns.
Parameters:
strategy_returns(pd.Series): Strategy returns series (indexed by datetime)factor_returns(pd.DataFrame): Factor returns DataFrame (columns = factor names, index = datetime)add_constant(bool): If True, add intercept term (alpha)
Returns:
FactorRegressionResultwith alpha, betas, R², t-stats, residual_std, n_obs
@dataclass
class FactorRegressionResult:
alpha: float
betas: pd.Series # index = factor names
residual_std: float
r2: float
tstats: pd.Series # index = ["alpha"] + factor names
n_obs: intGenerate HTML and Markdown reports for backtest results.
generate_markdown_report(result: BacktestResult, *, title: str = "Backtest Report", benchmark: Optional[pd.Series] = None, factor_returns: Optional[pd.DataFrame] = None) -> str
Generate a Markdown report summarizing the backtest.
Parameters:
result(BacktestResult): BacktestResult to report ontitle(str): Report titlebenchmark(Optional[pd.Series]): Optional benchmark returns for comparisonfactor_returns(Optional[pd.DataFrame]): Optional factor returns for regression
Returns:
- Markdown string
generate_html_report(result: BacktestResult, *, title: str = "Backtest Report", benchmark: Optional[pd.Series] = None, factor_returns: Optional[pd.DataFrame] = None, include_plots: bool = True) -> str
Generate an HTML report with optional embedded plots.
Parameters:
result(BacktestResult): BacktestResult to report ontitle(str): Report titlebenchmark(Optional[pd.Series]): Optional benchmark returns for comparisonfactor_returns(Optional[pd.DataFrame]): Optional factor returns for regressioninclude_plots(bool): If True, embed matplotlib plots as base64 images
Returns:
- HTML string
Example:
from alphaweave.results.report import generate_html_report
html = generate_html_report(
result,
title="My Strategy Backtest",
factor_returns=factor_returns,
include_plots=True,
)
with open("report.html", "w") as f:
f.write(html)ensure_datetime_index(df: pd.DataFrame) -> pd.DataFrameConvert datetime column to pandas datetime and set as index if not already.
Parameters:
df(pd.DataFrame): Input DataFrame
Returns:
pd.DataFrame: DataFrame with datetime index
Raises:
ValueError: If no datetime column or index can be found
Behavior:
- If index is already a DatetimeIndex, returns DataFrame unchanged
- Looks for datetime columns:
datetime,dt,timestamp - Converts found column to datetime and sets as index
- If no column found, attempts to convert existing index to datetime
Example:
from alphaweave.utils.time import ensure_datetime_index
import pandas as pd
df = pd.DataFrame({
"datetime": ["2020-01-01", "2020-01-02"],
"close": [100, 101]
})
df = ensure_datetime_index(df)
# df now has DatetimeIndeximport alphaweave as aw
import pandas as pd
# Create sample data
df = pd.DataFrame({
"datetime": pd.date_range("2020-01-01", periods=10, freq="D"),
"open": [10 + i for i in range(10)],
"high": [11 + i for i in range(10)],
"low": [9 + i for i in range(10)],
"close": [10 + i for i in range(10)],
"volume": [100] * 10,
})
# Create Frame
frame = aw.core.frame.Frame.from_pandas(df)
# Define strategy
class BuyAndHold(aw.strategy.base.Strategy):
def init(self):
pass
def next(self, i):
# Buy and hold on first bar
self.order_target_percent("TEST", 1.0)
# Run backtest
backtester = aw.engine.vector.VectorBacktester()
result = backtester.run(
BuyAndHold,
data={"TEST": frame},
capital=1000.0
)
# Print results
print(f"Equity series: {result.equity_series}")
print(f"Number of trades: {len(result.trades)}")
print(f"Final equity: ${result.equity_series[-1]:,.2f}")from alphaweave.data.loaders import load_csv
# Load CSV with automatic normalization
frame = load_csv("data/AAPL.csv", symbol="AAPL")
# Convert to pandas for analysis
df = frame.to_pandas()
print(df.head())from alphaweave.strategy.base import Strategy
from alphaweave.core.frame import Frame
class DualAssetStrategy(Strategy):
def init(self):
self.aapl_weight = 0.6
self.msft_weight = 0.4
def next(self, i):
# Allocate 60% to AAPL, 40% to MSFT
self.order_target_percent("AAPL", self.aapl_weight)
self.order_target_percent("MSFT", self.msft_weight)
# Prepare data
data = {
"AAPL": frame_aapl,
"MSFT": frame_msft
}
# Run backtest
result = VectorBacktester().run(DualAssetStrategy, data)All public APIs include type hints for better IDE support and type checking:
from typing import Optional, Dict, List, Type, Union
from datetime import datetime
import pandas as pd
import polars as plValueError: Raised when:
- Frame validation fails (missing required columns or datetime)
- Data format is invalid
TypeError: Raised when:
- Wrong type passed to Frame constructor
- Invalid argument types
NotImplementedError: Raised when:
- Strategy subclass doesn't implement
next()
- Datetime handling: All Frames must have a datetime index. The Frame abstraction automatically handles conversion between pandas (index) and polars (column) representations.
- Column normalization: Column names are automatically normalized to lowercase canonical forms (e.g., "Open" → "open", "Timestamp" → "datetime").
- Order execution: Orders execute at the close price of the current bar. Fees and slippage can be configured via optional parameters.
- Portfolio tracking: Positions are tracked with average entry price using weighted averages when adding to existing positions.
- Corporate actions: Stock splits preserve portfolio value by adjusting position size and cost basis. Cash dividends are credited to portfolio cash.
Portfolio optimization algorithms for constructing optimal asset allocations.
alphaweave.portfolio.costs.TransactionCostModel: base interface withestimate_cost(current_weights, target_weights, prices=None, volumes=None) -> float.- Concrete models:
ProportionalCostModel(cost_per_dollar)— linear turnover cost (scalar or per-asset series).SpreadBasedCostModel(spread_bps, participation_rate=1.0)— models half-spread * turnover.ImpactCostModel(adv, impact_coeff=0.1)— square-root market impact using average daily volume.CompositeCostModel(components=list[TransactionCostModel])— sums multiple models.
alphaweave.portfolio.turnover:compute_turnover(prev, target)— sum of absolute weight changes.TurnoverConstraint(max_turnover=None, max_change_per_asset=None)— hard caps.RebalancePenalty(lambda_rebalance=1.0)— soft penalty that shrinks weights back towardprev_weights.
All optimizers accept these objects via optional keyword arguments so that realism is opt-in and backward compatible.
equal_weight(assets: Sequence[str], *, constraints: Optional[PortfolioConstraints] = None) -> OptimizationResult
Equal weight portfolio optimizer.
Parameters:
assets(Sequence[str]): List of asset identifiersconstraints(Optional[PortfolioConstraints]): Optional portfolio constraints
Returns:
OptimizationResultwith equal weights
Example:
from alphaweave.portfolio.optimizers import equal_weight
result = equal_weight(["AAPL", "MSFT", "GOOG"])
weights = result.weights # Series with equal weights (1/3 each)mean_variance(expected_returns: Weights, cov_matrix: pd.DataFrame, *, risk_free_rate: float = 0.0, max_weight: Optional[float] = None, constraints: Optional[PortfolioConstraints] = None, prev_weights: Optional[pd.Series] = None, transaction_cost_model: Optional[TransactionCostModel] = None, turnover_constraint: Optional[TurnoverConstraint] = None, rebalance_penalty: Optional[RebalancePenalty] = None, prices: Optional[pd.Series] = None, volumes: Optional[pd.Series] = None) -> OptimizationResult
Mean-variance optimizer (maximize Sharpe ratio).
Parameters:
expected_returns(Weights): Expected returns per asset.cov_matrix(pd.DataFrame): Covariance matrix (aligned to expected_returns).risk_free_rate(float): Risk-free rate for Sharpe calculation.max_weight(Optional[float]): Maximum weight per asset.constraints(Optional[PortfolioConstraints]): Optional portfolio constraints.prev_weights(Optional[pd.Series]): Previous rebalance weights (default zeros). Required when using costs/turnover controls.transaction_cost_model(Optional[TransactionCostModel]): Estimated cost penalty incorporated directly into the optimization objective.turnover_constraint(Optional[TurnoverConstraint]): Hard caps on total turnover and/or per-asset change.rebalance_penalty(Optional[RebalancePenalty]): Soft shrinkage back towardprev_weights.prices/volumes(Optional[pd.Series]): Optional market data passed through to the cost model.
Returns:
OptimizationResultwith optimized weights
Example:
from alphaweave.portfolio.optimizers import mean_variance
expected_returns = pd.Series({"AAPL": 0.10, "MSFT": 0.08, "GOOG": 0.06})
cov_matrix = pd.DataFrame({...}) # Covariance matrix
result = mean_variance(expected_returns, cov_matrix)
weights = result.weights # Optimized weights maximizing Sharpemin_variance(cov_matrix: Optional[pd.DataFrame] = None, *, risk_model: Optional[RiskModel] = None, max_weight: Optional[float] = None, constraints: Optional[PortfolioConstraints] = None, prev_weights: Optional[pd.Series] = None, transaction_cost_model: Optional[TransactionCostModel] = None, turnover_constraint: Optional[TurnoverConstraint] = None, rebalance_penalty: Optional[RebalancePenalty] = None, prices: Optional[pd.Series] = None, volumes: Optional[pd.Series] = None) -> OptimizationResult
Minimum variance optimizer.
Parameters:
cov_matrix/risk_model: Provide either a covariance matrix or aRiskModelsupplying one.max_weight: Maximum weight per asset.constraints: Optional portfolio constraints.prev_weights,transaction_cost_model,turnover_constraint,rebalance_penalty,prices,volumes: same semantics asmean_variance.
Returns:
OptimizationResultwith minimum variance weights
risk_parity(cov_matrix: Optional[pd.DataFrame] = None, *, risk_model: Optional[RiskModel] = None, constraints: Optional[PortfolioConstraints] = None, prev_weights: Optional[pd.Series] = None, transaction_cost_model: Optional[TransactionCostModel] = None, turnover_constraint: Optional[TurnoverConstraint] = None, rebalance_penalty: Optional[RebalancePenalty] = None, prices: Optional[pd.Series] = None, volumes: Optional[pd.Series] = None) -> OptimizationResult
Risk parity optimizer (equal risk contribution).
Parameters:
cov_matrix/risk_model: Covariance input for risk contributions.constraints: Optional portfolio constraints.prev_weights,transaction_cost_model,turnover_constraint,rebalance_penalty,prices,volumes: optional realism knobs identical tomean_variance.
Returns:
OptimizationResultwith risk parity weights
target_volatility(base_weights: Weights, cov_matrix: pd.DataFrame, *, target_vol: float, max_leverage: Optional[float] = None) -> OptimizationResult
Target volatility allocator.
Parameters:
base_weights(Weights): Base portfolio weightscov_matrix(pd.DataFrame): Covariance matrixtarget_vol(float): Target annualized volatility (e.g., 0.10 for 10%)max_leverage(Optional[float]): Maximum leverage
Returns:
OptimizationResultwith scaled weights
Risk estimation helpers for portfolio optimization.
estimate_covariance(returns: pd.DataFrame, *, method: str = "sample", span: int = 60) -> pd.DataFrame
Estimate asset return covariance matrix.
Parameters:
returns(pd.DataFrame): DataFrame with columns = assets, index = datesmethod(str): Estimation method ("sample" or "ewma")span(int): Span parameter for EWMA
Returns:
- Covariance matrix DataFrame
estimate_volatility(returns: pd.DataFrame, *, method: str = "sample", span: int = 60, trading_days: int = 252) -> pd.Series
Per-asset volatility estimate.
Parameters:
returns(pd.DataFrame): DataFrame with columns = assets, index = datesmethod(str): Estimation method ("sample" or "ewma")span(int): Span parameter for EWMAtrading_days(int): Trading days per year for annualization
Returns:
- Series of annualized volatilities
Universe selection and ranking utilities.
Select top-N assets by score.
Parameters:
scores(pd.Series): Series indexed by assetn(int): Number of top assets to selectascending(bool): If True, select bottom-N
Returns:
- List of asset identifiers
Map arbitrary scores to portfolio weights.
Parameters:
scores(pd.Series): Series indexed by asset with arbitrary scoreslong_only(bool): If True, set negative scores to 0 before normalizing
Returns:
- Series of normalized weights (sums to 1.0)
Portfolio constraint definitions.
@dataclass
class PortfolioConstraints:
weight_bounds: Optional[Mapping[str, WeightBounds]] = None
default_bounds: Optional[WeightBounds] = None
target_gross: Optional[float] = 1.0
long_only: bool = TrueAttributes:
weight_bounds: Per-asset weight boundsdefault_bounds: Fallback bounds for assets not in weight_boundstarget_gross: Target gross exposure (sum of absolute weights)long_only: If True, enforce long-only constraint
@dataclass
class WeightBounds:
lower: float # e.g. 0.0 for long-only
upper: float # e.g. 0.1 for 10% capBarSnapshot: dataclass capturing bar-level state (timestamp,prices,equity,cash,positions,pnl,drawdown, etc.).TradeRecord: dataclass for executed fills (symbol,side,price,size,fees,slippage, ...).Monitor: protocol definingon_run_start(),on_bar(),on_trade(),on_metric(), andon_run_end().InMemoryMonitor: default implementation that stores bars/trades/metrics in memory and exposesbars_df(),trades_df(),metrics_df().
Wrap an InMemoryMonitor and gain derived analytics:
equity_curve()/drawdown_curve()exposure_over_time()turnover_over_time()cost_over_time()
Matplotlib helpers returning Figure objects:
plot_equity_and_drawdown(run)plot_exposure_heatmap(run)plot_turnover(run)plot_trade_pnl_histogram(run)
Produce a self-contained HTML dashboard embedding the plots above, overview stats, a trades table, and (optionally) live-vs-backtest drift comparisons.
Align backtest equity with a RunMonitor's live equity and return (live - backtest) / backtest for drift monitoring.
BrokerFill/BrokerAccountStatedataclasses describe fills and account snapshots.Brokerprotocol defines the minimal interface (submit_order,cancel_order,get_open_orders,get_account_state,poll_fills).
BrokerConfig: normalized config payload for adapters.- Concrete adapters:
MockBrokerAdapter– instant fills using the backtester portfolio model.PaperBrokerAdapter– thin wrapper over the mock adapter with friendlier config defaults.IBKRAdapter,AlpacaAdapter,BinanceAdapterskeletons (raiseNotImplementedErroruntil wired to real APIs).
LiveConfig: dataclass containing broker/strategy/datafeed/risk/monitor/persistence sections.load_live_config(path)reads YAML or JSON (PyYAML optional). Useparse_live_config(mapping)if you already have a dict.
High-level orchestrator:
from alphaweave.live.config import load_live_config
from alphaweave.live.runner import LiveRunner
cfg = load_live_config("examples/live_config_template.yaml")
runner = LiveRunner.from_config(cfg)
run_monitor = runner.run()- Builds the broker adapter, replay datafeed, strategy (via dotted path),
LiveEngine, andInMemoryMonitor. - Writes dashboards (
monitor.dashboard_html) and persists broker snapshots (persistence.state_path) after each run. - Provides a
RunMonitorfor downstream analysis.
LiveStatedataclass plussave_live_state(path, state)/load_live_state(path)helpers.- Strategies can override
get_state/set_stateto persist custom fields.
ReplayDataFeed: convenience wrapper over static Frames; used by LiveRunner for deterministic dry runs/replays.
This documentation corresponds to alphaweave version 0.0.1 (Sprints 0-18).