This document lists the public API of MeridianAlgo. Every example here matches the installed code. The core library runs on numpy, pandas, and scipy alone. Some features need an optional extra, which is noted where it applies.
| Module | Key classes and functions |
|---|---|
portfolio |
MeanVariance, HierarchicalRiskParity, RiskParity, BlackLitterman, KellyCriterion |
portfolio.insurance |
CPPI, TimeInvariantCPPI |
risk |
RiskAnalyzer, VaRCalculator, CVaRCalculator, StressTesting, RiskBudgeting |
risk.scenario |
ScenarioAnalyzer, CorrelationScenario |
credit |
MertonModel, CreditDefaultSwap, CreditRiskAnalyzer, ZSpreadCalculator |
volatility |
GARCHModel, RealizedVolatility, VolatilityForecaster, VolatilityTermStructure, VolatilityRegimeDetector |
monte_carlo |
GeometricBrownianMotion, HestonModel, JumpDiffusionModel, CIRModel, MonteCarloEngine |
derivatives |
BlackScholes, GreeksCalculator, ImpliedVolatility, MonteCarloPricer, OptionChain |
fixed_income |
BondPricer, YieldCurve, CreditSpreadAnalyzer |
backtesting |
BacktestEngine, Strategy, Backtest |
ml |
LSTMPredictor, WalkForwardValidator, FeatureEngineer, ModelSelector |
execution |
VWAP, TWAP, POV, ImplementationShortfall |
analytics |
PerformanceAnalyzer, BenchmarkAnalytics, ActiveShare, BrinsonAttribution |
quant |
StatisticalArbitrage, RegimeDetector, MarketMicrostructure |
signals |
RSI, MACD, BollingerBands, and more than forty indicators |
liquidity |
OrderBook, SpreadAnalyzer, MarketImpact |
factors |
FamaFrenchModel, FactorExposure |
These convenience functions run on a return series and need no extras.
import meridianalgo as ma
sharpe = ma.calculate_sharpe_ratio(returns)
sortino = ma.calculate_sortino_ratio(returns)
calmar = ma.calculate_calmar_ratio(returns)
max_dd = ma.calculate_max_drawdown(returns)
cvar_95 = ma.calculate_expected_shortfall(returns)
# One call summary of around 28 metrics plus a formatted text report
stats = ma.summary_stats(returns)
print(ma.tearsheet(returns))Each optimizer takes expected returns as a pandas Series and a covariance matrix as a pandas DataFrame. Annualize both before you pass them in. Each call returns an OptimizationResult with weights, expected_return, volatility, sharpe_ratio, and a success flag.
from meridianalgo import MeanVariance, HierarchicalRiskParity, RiskParity, BlackLitterman
expected_returns = returns.mean() * 252
covariance = returns.cov() * 252
max_sharpe = MeanVariance().optimize(expected_returns, covariance, objective="max_sharpe")
min_vol = MeanVariance().optimize(expected_returns, covariance, objective="min_volatility")
hrp = HierarchicalRiskParity().optimize(expected_returns, covariance, returns_data=returns)
rp = RiskParity().optimize(expected_returns, covariance)
bl = BlackLitterman().optimize(expected_returns, covariance)
print(max_sharpe.weights.sort_values(ascending=False))
print(f"Max sharpe ratio {max_sharpe.sharpe_ratio:.4f}")
print(f"Min variance vol {min_vol.volatility:.4f}")from meridianalgo import KellyCriterion
kc = KellyCriterion(fraction=0.5) # half kelly
f = kc.single_asset(win_prob=0.55, win_loss_ratio=1.0) # discrete binary bet
weights = kc.optimize(returns) # continuous multi asset
f_moments = kc.from_moments(expected_return=0.12, volatility=0.18)
g = kc.growth_rate(expected_return=0.12, volatility=0.18) # long run growth rateRiskAnalyzer, VaRCalculator, and CVaRCalculator share the same interface. Value at risk supports "historical", "parametric", "cornish_fisher", and "monte_carlo".
from meridianalgo import RiskAnalyzer
risk = RiskAnalyzer(portfolio_returns)
var_95 = risk.value_at_risk(confidence=0.95, method="historical")
var_99 = risk.value_at_risk(confidence=0.99, method="cornish_fisher")
cvar_95 = risk.conditional_var(confidence=0.95)Correlated, scenario based stress testing lives in ScenarioAnalyzer and CorrelationScenario. All inputs are pandas.
from meridianalgo import CorrelationScenario
gen = CorrelationScenario(mean_returns, correlation_matrix, volatilities, weights)
scenarios = gen.generate(n_scenarios=100_000, stress_correlation=True, stress_factor=0.5)
print(f"Stressed 99 percent VaR {scenarios['var_99']:.2%}")from meridianalgo import MertonModel, CreditDefaultSwap, CreditRiskAnalyzer, ZSpreadCalculator
import pandas as pd
# Merton structural model, equity as a call option on firm assets
model = MertonModel(
equity_value=500e6, equity_volatility=0.35,
debt_face_value=800e6, time_to_maturity=1.0, risk_free_rate=0.05,
)
result = model.calibrate()
print(f"Distance to default {result['distance_to_default']:.4f}")
# CDS fair spread and a bootstrapped hazard curve
cds = CreditDefaultSwap(hazard_rate=0.02, recovery_rate=0.40, maturity=5.0)
print(f"CDS fair spread {cds.price().fair_spread * 10000:.1f} bps")
curve = CreditDefaultSwap.bootstrap_hazard_curve(
maturities=[1, 3, 5, 7, 10], spreads=[0.0080, 0.0120, 0.0150, 0.0170, 0.0200],
)
# Z spread from a price
calc = ZSpreadCalculator(
cash_flows=[6, 6, 6, 6, 106], times=[1, 2, 3, 4, 5],
risk_free_rates=[0.035, 0.038, 0.040, 0.042, 0.044],
)
print(f"Z spread {calc.z_spread(market_price=97.5) * 10000:.1f} bps")RealizedVolatility accepts OHLCV columns in any capitalization, so open or Open or OPEN all work. The maximum likelihood GARCH family needs the volatility extra.
from meridianalgo import (
GARCHModel, RealizedVolatility, VolatilityTermStructure, VolatilityRegimeDetector,
)
rv = RealizedVolatility(ohlcv)
est = rv.all_estimators(window=21)
garch = GARCHModel(daily_returns, model_type="garch", p=1, q=1)
fit = garch.fit()
print(f"Persistence {fit.persistence:.4f}, half life {fit.half_life:.1f} days, aic {fit.aic:.1f}")
vts = VolatilityTermStructure(daily_returns)
vts.build(horizons=[5, 10, 21, 63, 126, 252])
regimes = VolatilityRegimeDetector(daily_returns).classify()from meridianalgo import (
GeometricBrownianMotion, HestonModel, JumpDiffusionModel, CIRModel, MonteCarloEngine,
)
gbm = GeometricBrownianMotion(mu=0.08, sigma=0.20)
res = gbm.simulate(S0=100, T=1.0, n_paths=100_000, n_steps=252, antithetic=True)
print(f"Mean {res.mean:.2f}, 5th pct {res.percentile_5:.2f}, 95th pct {res.percentile_95:.2f}")
call = gbm.call_price(S0=100, K=105, T=0.25, r=0.05, n_paths=200_000)
heston = HestonModel(mu=0.05, v0=0.04, kappa=2.0, theta=0.04, xi=0.30, rho=-0.70)
jdm = JumpDiffusionModel(mu=0.05, sigma=0.15, lam=0.10, mu_jump=-0.03, sigma_jump=0.06)
cir = CIRModel(r0=0.03, kappa=0.80, theta=0.04, sigma=0.06)
engine = MonteCarloEngine(model="heston")
engine.configure(mu=0.05, v0=0.04, kappa=2.0, theta=0.04, xi=0.30, rho=-0.7)
engine.simulate(S0=100, T=1.0, n_paths=100_000)
put = engine.price_option(K=95, r=0.05, T=1.0, option_type="put")from meridianalgo import CPPI, TimeInvariantCPPI
cppi = CPPI(multiplier=3.0, floor_pct=0.80, safe_rate=0.04, rebalance_frequency=1)
result = cppi.run(equity_returns, initial_value=1_000_000)
print(f"Total return {result.total_return:.2%}, max drawdown {result.max_drawdown:.2%}")
# TIPP, where the floor ratchets up with new portfolio peaks
tipp = TimeInvariantCPPI(multiplier=3.0, floor_pct=0.80)
tipp_result = tipp.run(equity_returns, initial_value=1_000_000)BlackScholes returns a dict with price, delta, gamma, theta, vega, and rho. ImpliedVolatility returns a float.
from meridianalgo import BlackScholes, ImpliedVolatility
call = BlackScholes(S=100, K=105, T=0.25, r=0.05, sigma=0.20, option_type="call")
put = BlackScholes(S=100, K=105, T=0.25, r=0.05, sigma=0.20, option_type="put")
print(f"Call {call['price']:.4f}, delta {call['delta']:.4f}, vega {call['vega']:.4f}")
iv = ImpliedVolatility(market_price=3.50, S=100, K=105, T=0.25, r=0.05, option_type="call")
print(f"Implied volatility {iv:.4f}")BondPricer().price_bond returns price, duration, and modified duration in one call.
from meridianalgo import BondPricer, YieldCurve
pricer = BondPricer()
bond = pricer.price_bond(
face_value=1000, coupon_rate=0.05, yield_to_maturity=0.06,
years_to_maturity=10, frequency=2,
)
print(f"Price {bond['price']:.4f}")
print(f"Duration {bond['duration']:.4f}")
print(f"Modified duration {bond['modified_duration']:.4f}")
curve = YieldCurve()
for maturity, rate in [(0.25, 0.04), (1, 0.045), (2, 0.048), (5, 0.052), (10, 0.055), (30, 0.058)]:
curve.add_point(maturity, rate)
curve.build_curve(method="nelson_siegel")
print(f"7 year rate interpolated {curve.get_yield(7):.4f}")
print(f"5y5y forward rate {curve.get_forward_rate(5, 10):.4f}")from meridianalgo import PerformanceAnalyzer, BenchmarkAnalytics, ActiveShare, BrinsonAttribution
analyzer = PerformanceAnalyzer(portfolio_returns, benchmark=spy_returns, risk_free_rate=0.05)
metrics = analyzer.calculate_all_metrics()
analytics = BenchmarkAnalytics(
portfolio_returns=portfolio_returns, benchmark_returns=spy_returns, risk_free_rate=0.05,
)
m = analytics.active_metrics()
print(f"Active return {m.active_return:.2%}, tracking error {m.tracking_error:.2%}")
active_share = ActiveShare.compute(portfolio_weights, benchmark_weights)
attribution = BrinsonAttribution(
portfolio_weights=sector_w_port, benchmark_weights=sector_w_bench,
portfolio_returns=sector_r_port, benchmark_returns=sector_r_bench,
).compute()
print(f"Total active return {attribution.total_active_return:.4f}")The schedulers are built with the order size and a time window, then driven slice by slice as the market moves.
from meridianalgo import VWAP, TWAP, POV
vwap = VWAP(total_quantity=10_000, start_time="09:30", end_time="16:00")
twap = TWAP(total_quantity=10_000, duration_minutes=60, slice_interval_minutes=5)
pov = POV(total_quantity=10_000, target_pov=0.10)
slice_order = vwap.execute_slice(
current_time=now, market_volume=500_000, market_price=100.0, max_participation=0.1,
)More than forty indicators ship as plain functions on numpy and pandas. They need no extras.
import meridianalgo as ma
rsi = ma.RSI(prices, period=14)
macd = ma.MACD(prices)
upper, mid, lower = ma.BollingerBands(prices, period=20)
atr = ma.ATR(high, low, close, period=14)See docs/api/technical_indicators.md for the full indicator list.
The z score helper runs on the core install. The cointegration test relies on statsmodels, so install the ml extra to use it.
import meridianalgo as ma
stat_arb = ma.StatisticalArbitrage(prices)
zscore = stat_arb.calculate_zscore(window=21) # rolling spread z score
result = stat_arb.calculate_cointegration(prices["KO"], prices["PEP"]) # needs the ml extraThe backtesting engine is event driven. It processes market, signal, order, and fill events through a portfolio and order manager rather than a single run call.
from meridianalgo import BacktestEngine
engine = BacktestEngine(initial_capital=100_000, commission=0.001, slippage=0.0005)
# feed market data, submit orders, and read metrics through the event API
metrics = engine.get_performance_metrics()The machine learning models need the ml extra. With it installed you get LSTM and GRU predictors, walk forward cross validation, and feature engineering.
from meridianalgo.ml import FeatureEngineer, WalkForwardValidator
from sklearn.ensemble import RandomForestClassifier
features = FeatureEngineer().create_features(
prices, features=["returns", "rsi", "macd", "volume_ratio", "volatility", "momentum"],
)
labels = (returns.shift(-1) > 0).astype(int)
results = WalkForwardValidator().validate(
features, labels,
model=RandomForestClassifier(n_estimators=200, random_state=42),
train_window=252, test_window=21,
)Every submodule loads behind a registry, so the package imports even when an optional dependency is missing. Check what is available at runtime.
import meridianalgo as ma
print(ma.ModuleRegistry.status())
print(ma.ModuleRegistry.is_available("ml"))Install the matching extra, for example pip install "meridianalgo[ml]", to enable a module reported as unavailable.
| Extra | Packages | Enables |
|---|---|---|
ml |
scikit-learn, torch, statsmodels, hmmlearn | LSTM models, walk forward CV, HMM regime, cointegration |
optimization |
cvxpy, cvxopt | convex portfolio optimization, CVaR minimization |
volatility |
arch | GARCH, EGARCH, GJR GARCH by maximum likelihood |
data |
lxml, beautifulsoup4, polygon-api-client | Polygon data, web scraping |
distributed |
ray, dask | parallel backtesting and optimization |
all |
everything above | the full feature set |