Skip to content

Latest commit

 

History

History
385 lines (274 loc) · 13.2 KB

File metadata and controls

385 lines (274 loc) · 13.2 KB

API Reference

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 overview

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

Top level metrics

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))

Portfolio optimization

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}")

Kelly criterion

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 rate

Risk analysis

RiskAnalyzer, 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%}")

Credit risk

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")

Volatility modeling

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()

Monte Carlo simulation

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")

Portfolio insurance

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)

Derivatives pricing

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}")

Fixed income

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}")

Performance and benchmark analytics

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}")

Execution algorithms

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,
)

Technical indicators

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.


Statistical arbitrage

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 extra

Backtesting

The 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()

Machine learning

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,
)

Module availability

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.


Optional dependencies

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