Skip to main content

ASRQuant

From scientific literature and economic hypotheses to auditable quantitative decisions, guarded broker execution, and production-readiness controls in Python.

ASRQuant is an open-source research package developed by Alpha Stochastic Research. It provides a compact, explicit and reproducible interface for scientific-paper ingestion, source-linked hypothesis discovery, market-data planning, feature and signal construction, quantitative visualization, stochastic simulation, derivative pricing, econometrics, machine learning, portfolio analytics, parameter exploration, backtesting, robustness, governed decisions and paper trading, production readiness, tamper-evident audit trails and explicitly authorized broker execution.

The central objective is simple: workflows that normally require many notebooks, formulas and plotting scripts should be expressible in a few readable lines without hiding the assumptions that materially affect the result.

Release status: 1.1.0 (stable public API). The 1.0.0 paper-contract remains preserved; 1.1.0 adds the Research Discovery / Weekly Research operating layer and the comprehensive interest-rate research stack. The paper workflows are enforced by a dedicated paper-contract test group. Guarded live-broker primitives remain fail-closed and require deployment-specific authorization; installing the stable software does not authorize capital deployment. ASRQuant is research software, not financial advice or an HFT exchange gateway.

Contents


Installation

ASRQuant requires Python 3.10 or later.

Install the provided wheel

pip install asrquant-1.1.0-py3-none-any.whl

Install from the source directory

pip install .

Editable development installation

pip install -e ".[dev]"
python scripts/test_all.py

Optional dependency groups

The base installation automatically installs the validated numerical and visualization engines required by ASRQuant. They remain internal implementation details: normal user workflows do not require importing NumPy, pandas, SciPy, Matplotlib, Plotly, statsmodels or scikit-learn directly.

# Yahoo Finance, Excel, Parquet and Feather support
pip install ".[data]"

# GARCH models
pip install ".[volatility]"

# CVXPY-backed optimization methods when required
pip install ".[optimization]"

# SHAP and hidden Markov model extensions
pip install ".[ml]"

# All optional research dependencies
pip install ".[all]"

# Documentation toolchain
pip install ".[docs]"

When the project is officially registered on PyPI, the intended commands are:

pip install asrquant
pip install "asrquant[all]"

Until registration is completed, install the supplied wheel or source archive.

Verify the installation

import asrquant

print(asrquant.__version__)
# 1.1.0
asrquant --version

Five-line workflow

import asrquant as asr

lab = asr.open_lab(prices)
result = lab.backtest("sma", fast=20, slow=100, costs_bps=5)
asr.save(result, "dashboard.png", kind="dashboard")
asr.report(result, "report.html")

The returned BacktestResult contains:

  • validated prices and asset returns;
  • target and effective weights;
  • gross and net strategy returns;
  • equity curve;
  • turnover;
  • aggregate and decomposed costs;
  • strategy metrics;
  • a transaction ledger;
  • the immutable backtest specification;
  • data, specification and experiment fingerprints;
  • plotting and HTML-report helpers.

One-import user contract

The recommended public interface is a single import:

import asrquant as asr

After that import, the standard workflow is available through ASRQuant itself:

lab = asr.open_lab("prices.csv", date_column="Date")
model = asr.models.random_forest(task="regression", trees=500, depth=6, seed=7)
ml = lab.ml(model, train_size=504, test_size=63, gap=5)
backtest = lab.backtest("momentum", lookback=126, costs_bps=5)
asr.save(backtest, "equity.png", kind="equity")

The following namespaces deliberately hide the backend libraries:

  • asr.models: linear models, trees, forests, boosting, SVM, KNN, clustering, PCA and anomaly detection;
  • asr.math: arrays, grids, elementary functions, normal distribution functions and random generators;
  • asr.visualize, asr.show, asr.save: backend-neutral visualization;
  • asr.stats: regression, econometrics, bootstrap and statistical tests;
  • asr.portfolio: allocation, covariance and optimization;
  • asr.options: derivative pricing and Greeks;
  • asr.stochastic: stochastic processes and Monte Carlo;
  • asr.rates: fixed-income analytics;
  • asr.vol: volatility estimators and forecasts;
  • asr.visuals: the complete visualization catalog.

Matplotlib, Plotly, scikit-learn and statsmodels are used internally because they are mature scientific engines. ASRQuant owns the user-facing API, parameter validation, output objects, visualization calls and reproducibility contract. Advanced users may access a raw backend object through PlotHandle.raw, but ordinary workflows never need to do so.


Production readiness and live capital

ASRQuant deliberately separates three different claims:

  1. research-valid: the strategy and backtest pass scientific review;
  2. broker-paper validated: the deployed execution path has survived a defined observation period in the broker simulator;
  3. live-authorized: a signed deployment certificate matches the exact release, broker account, risk policy, environment and maximum capital.

A wheel cannot certify the second and third claims by itself. The deployment gate therefore fails closed by default.

1. Create an evidence file

import asrquant as asr

evidence = asr.DeploymentEvidence(
    release_version=asr.__version__,
    ci_passed=True,
    test_count=120,
    coverage_percent=92.0,
    static_analysis_passed=True,
    dependency_scan_passed=True,
    secrets_scan_passed=True,
    sbom_present=True,
    artifacts_signed=True,
    reproducible_build_verified=True,
    disaster_recovery_tested=True,
    rollback_tested=True,
    monitoring_enabled=True,
    alerting_enabled=True,
    durable_audit_log_enabled=True,
    time_synchronization_verified=True,
    broker_paper_days=45,
    broker_paper_orders=1000,
    reconciliation_mismatches=0,
    unresolved_critical_incidents=0,
    operator_approved=True,
    legal_compliance_reviewed=True,
    data_licenses_reviewed=True,
    strategy_owner_approved=True,
    model_validation_approved=True,
    change_ticket="CHG-2026-001",
)

report = asr.ProductionReadinessGate().evaluate(evidence)
print(report.ready)
report.save("readiness-report.json")

The same check is available from the CLI:

asrquant readiness deployment-evidence.json --output readiness-report.json

The command exits with status 2 when any required gate fails.

2. Define the exact live risk policy

policy = asr.LiveRiskPolicy(
    max_gross_leverage=1.0,
    max_position_weight=0.10,
    max_order_notional=5_000,
    max_daily_turnover=0.25,
    max_drawdown=0.10,
    max_daily_loss=0.02,
    max_open_orders=5,
    max_orders_per_minute=10,
    max_price_deviation_bps=100,
    max_market_data_age_seconds=2,
    max_capital=50_000,
    max_position_notional=10_000,
    allow_short=False,
    require_market_open=True,
    symbol_allowlist=("SPY", "QQQ"),
)

3. Issue a signed, expiring deployment certificate

The signing key must come from a secret manager or protected deployment environment. Never place it in source code, notebooks, reports or CI logs.

certificate = asr.DeploymentCertificate.issue(
    report=report,
    evidence=evidence,
    secret_key=certificate_signing_key,
    release_version=asr.__version__,
    broker="alpaca",
    account_id=broker_account_id,
    account_salt=account_specific_salt,
    risk_policy=policy,
    max_live_capital=50_000,
    approved_by=("risk-owner", "operations-owner"),
    validity_hours=24,
)

certificate.save("deployment-certificate.json")

The certificate becomes invalid if any of the following changes:

  • package version;
  • broker;
  • broker account;
  • risk policy;
  • maximum authorized capital;
  • environment fingerprint when enforced;
  • validity period;
  • signed payload.

4. Test the real broker adapter in paper mode

credentials = asr.BrokerCredentials.from_environment()
broker = asr.AlpacaBroker.paper(credentials=credentials)
print(broker.health())

Paper and live Alpaca environments use different base URLs and should use different credentials. Paper execution remains a simulator and may differ from real fills.

5. Arm live mode explicitly

Live creation requires both a valid deployment certificate and an environment-level arm:

export ASRQUANT_LIVE_TRADING=ENABLED
broker = asr.AlpacaBroker.live(
    certificate=certificate,
    certificate_secret=certificate_signing_key,
    account_id=broker_account_id,
    account_salt=account_specific_salt,
    risk_policy=policy,
    requested_capital=25_000,
    credentials=asr.BrokerCredentials.from_environment(),
)

Direct construction of a live adapter is rejected. The certificate is not a substitute for broker permissions, venue rules, regulatory review or an accountable human go-live decision.

Durable audit, reconciliation and emergency controls

store = asr.SQLiteAuditStore("state/execution-audit.db")
kill_switch = asr.PersistentKillSwitch("state/KILL_SWITCH.json")

engine = asr.LiveTradingEngine(
    broker=broker,
    policy=policy,
    audit_store=store,
    kill_switch=kill_switch,
)

receipt = engine.submit(
    asr.Order("SPY", 10, asr.OrderSide.BUY),
    asr.MarketDataSnapshot(
        symbol="SPY",
        price=last_price,
        timestamp=market_timestamp,
        bid=bid,
        ask=ask,
        source="primary-feed",
    ),
)

Before submission, the engine checks at least:

  • live kill-switch state;
  • duplicate client-order IDs;
  • broker and account health;
  • market-open state;
  • market-data freshness;
  • symbol allowlist and denylist;
  • order size and notional;
  • price collars;
  • buying power;
  • daily loss;
  • open-order count;
  • order-entry rate;
  • projected position weight;
  • projected gross leverage;
  • short-selling policy.

Every risk decision, order intent, broker receipt, failure, reconciliation and emergency stop is appended to a SQLite write-ahead log with a SHA-256 hash chain.

valid, broken_sequence = store.verify_chain()
backup = store.backup("backups/execution-audit.db")
asrquant verify-audit state/execution-audit.db

Reconciliation is explicit:

report = engine.reconcile(
    expected_positions={"SPY": 10.0},
    expected_cash=24_000.0,
)

A mismatch outside tolerance activates the persistent kill switch and attempts to cancel open orders. Manual emergency stop is also available:

engine.emergency_stop("unexpected market-data divergence", operator="risk-owner")

Clearing the kill switch requires an explicit operator action and authorization string:

kill_switch.clear(
    operator="operations-owner",
    authorization="CLEAR_KILL_SWITCH",
)

See docs/production_readiness.md, docs/live_trading.md, docs/operations_runbook.md, docs/regulatory_controls.md and THREAT_MODEL.md before enabling a broker connection.


Core concepts

1. Explicit data

ASRQuant accepts ASR-created frames and compatible time-indexed tabular objects. It does not silently infer an arbitrary date order or broadcast one asset across a multi-asset panel.

2. Explicit execution contract

A backtest is defined by a BacktestSpec, including execution delay, rebalance frequency, leverage limits, missing-data policy, annualization, cash rate and all modeled costs.

3. Explicit chronology

Signals, features and validation folds are designed to preserve temporal ordering. Same-bar execution is possible for diagnostics, but it is not the default.

4. Scalar experiment interface

Any Python experiment that returns, or can be reduced to, a scalar value can be evaluated over a finite parameter grid and represented as a surface, heatmap, contour or animation.

5. Auditable outputs

Important results expose summary tables, underlying data, parameters and deterministic fingerprints rather than only displaying a chart.


Research discovery and Weekly Research

ASRQuant can now start before a formal hypothesis exists. The discovery engine converts transparent market, curve, literature, model-disagreement and robustness evidence into ranked research candidates. Automated candidates never receive an automatic novelty claim.

import asrquant as asr

board = asr.discovery.weekly(data=curve_history, domain="fixed_income", n=10)
print(board.to_frame())

project = board.start(0)
cycle = asr.weekly_cycle(board, 0, launch_friday="2026-08-14")
cycle.publication_pack("WR-001")

The resulting cycle follows ASR's Friday-to-Friday contract: launch, prior-art review, data design, baseline, main experiment, robustness, independent review, then publication on the next Friday. See docs/research_discovery.md and docs/team_research_operating_model.md.


Scientific literature to decision

ASRQuant 1.0.0 connects paper provenance, economic hypotheses, data, features, signals, econometric testing, portfolio construction, backtesting, robustness, decisions and paper trading in one stateful ResearchProject. The automatic stages remain reviewable and do not hide their assumptions.

1. Ingest PDF papers

import asrquant as asr

project = asr.research.from_pdfs(
    "papers/",
    topic="interest rates and equity style returns",
    name="Rates and equity styles",
)

print(project.corpus.paper_table())

Each paper retains:

  • a stable paper identifier;
  • path and file fingerprint;
  • page-level text;
  • title, authors, year and abstract when extractable;
  • extraction warnings;
  • source excerpts with exact page numbers.

Text-based PDFs are supported directly. Scanned PDFs must be OCRed before ingestion. ASRQuant never silently runs OCR because OCR errors can alter scientific claims.

2. Discover source-linked hypotheses

registry = project.discover_hypotheses()
print(registry.to_frame())

Corpus-relative labels are:

  • established: similar claims in at least three supplied papers;
  • replicated: similar claims in two supplied papers;
  • underexplored: one direct source in the supplied corpus;
  • contradictory: similar claims with opposing directional language;
  • corpus-novel: a gap or limitation with no direct match elsewhere in the supplied corpus.

corpus-novel never means that the hypothesis has never been tested anywhere. It means only that no direct match was found in the documents supplied to this run.

Each candidate also receives an independent evidence status:

  • tested_in_corpus: the source passage explicitly reports a test, estimate, result or finding;
  • not_directly_tested_in_corpus: the candidate comes from an explicit gap or future-research passage;
  • proposed_in_corpus: the passage formulates or predicts a relationship without a directly detected empirical test.

These labels are evidence-triage aids. Researchers must verify the quoted page passages before treating a candidate as tested or untested.

A custom extractor can connect a local or remote language model while preserving the same citation contract:

def extractor(paper):
    return [
        {
            "statement": "...",
            "page": 12,
            "source_text": "...",
            "confidence": 0.82,
        }
    ]

registry = project.discover_hypotheses(extractor=extractor)

3. Operationalize one hypothesis

hypothesis = project.select_hypothesis(
    "H001",
    predictor="US10Y",
    target="VALUE minus GROWTH",
    expected_sign="positive",
    horizon=20,
    universe="US equity style ETFs",
    mechanism=(
        "Higher discount rates reduce the present value of long-duration "
        "growth cash flows more strongly."
    ),
    invalidation_criteria=[
        "The predictive coefficient has the opposite sign out of sample.",
        "The result disappears under realistic costs and a one-bar delay.",
    ],
)

An EconomicHypothesis stores the statement, mechanism, predictor, target, expected sign, horizon, universe, null, evidence, novelty status and falsification criteria.

4. Generate and review a data plan

plan = project.plan_data()
print(plan.to_frame())

For recognized economic concepts, ASRQuant proposes provider mappings such as FRED and Yahoo Finance. Suggestions are not treated as proof that a proxy correctly measures the construct.

Download is explicit:

data = project.fetch_data(
    start="2005-01-01",
    end="2026-07-31",
    provider_kwargs={
        "fred": {"api_key": "..."},
    },
)

For revised macroeconomic series, the user must verify point-in-time vintages and publication timestamps. Standard endpoints may expose revised observations.

Local data can be attached instead:

project.attach_data(
    "research_panel.csv",
    date_column="Date",
    tradable_assets=["VALUE", "GROWTH"],
)

5. Build leakage-aware features

feature_plan = asr.FeaturePlan([
    asr.FeatureSpec(
        name="yield_change_20",
        source="US10Y",
        transform="diff",
        params={"periods": 20},
        availability_lag=1,
    ),
    asr.FeatureSpec(
        name="yield_change_z252",
        source="US10Y",
        transform="zscore",
        window=252,
        availability_lag=1,
    ),
])

features = project.build_features(feature_plan)

Available transformations include raw values, differences, percentage changes, log returns, momentum, rolling means, rolling standard deviations, z-scores, exponential averages, ranks, lags, volatility, drawdowns, ratios, spreads and interactions.

availability_lag represents when a datum becomes known. lag adds a modelling delay after availability.

A transparent starting plan is available:

feature_plan = project.recommend_features()
features = project.build_features(feature_plan)

6. Convert features into a signal

signal = project.build_signal(
    asr.SignalSpec(
        feature="yield_change_z252",
        method="threshold_pair",
        long_asset="VALUE",
        short_asset="GROWTH",
        upper=1.0,
        lower=-1.0,
        signal_lag=1,
    )
)

Supported signal mappings include threshold pairs, continuous pairs, threshold long-only signals and sign signals.

The automatic starting proposal is reviewable:

proposal = project.recommend_signal()
print(proposal)

7. Test the economic hypothesis

test = project.test_hypothesis(
    feature="yield_change_z252",
    horizon=20,
    covariance="HAC",
)

print(test.summary)

For a two-asset pair, ASRQuant constructs the future long-minus-short return and regresses it on the time-t feature. The output reports coefficient, robust p-value, R-squared and whether the estimated sign agrees with the hypothesis.

This is a predictive econometric test, not proof of structural causality.

8. Construct the portfolio

weights = project.construct_portfolio(
    asr.PortfolioSpec(
        gross_leverage=1.0,
        max_abs_weight=0.5,
        volatility_target=0.10,
        volatility_window=20,
        max_leverage=1.5,
    )
)

Portfolio construction applies long-only constraints when requested, position limits, gross leverage limits and optional ex-ante volatility targeting.

9. Run an auditable backtest

result = project.backtest(
    costs_bps=5,
    execution_delay=1,
    rebalance="W-FRI",
)

print(result.metrics)

The ordinary ASRQuant backtest contract remains available, including spread, slippage, borrow costs, nonlinear impact, leverage, rebalance frequency and initial capital.

10. Run robustness checks

robustness = project.robustness(
    execution_delays=(1, 2, 3),
    costs_bps=(0, 5, 10, 20),
    rebalances=("bar", "W-FRI", "ME"),
    n_subperiods=4,
    n_boot=2000,
)

print(robustness.summary)

The result contains:

  • implementation audit across costs, delays and rebalancing conventions;
  • chronological subperiod metrics;
  • moving-block bootstrap confidence interval for Sharpe;
  • look-ahead alignment diagnostics;
  • positive-subperiod and positive-contract ratios;
  • optional parameter-sweep results.

11. Obtain a governed decision

decision = project.decide()
print(decision.summary)

Possible statuses are:

  • REJECT;
  • RESEARCH-ONLY;
  • COLLECT MORE DATA;
  • REVISE HYPOTHESIS;
  • PAPER-TRADING CANDIDATE;
  • LIMITED-CAPITAL CANDIDATE.

A backtest alone never produces an automatic unrestricted live-deployment authorization. The decision includes reasons, risks, score, evidence and the required next step.

12. Paper trade the strategy

paper = project.paper_trade(
    initial_capital=100_000,
    commission_bps=1,
    slippage_bps=2,
    policy=asr.RiskPolicy(
        max_gross_leverage=1.0,
        max_position_weight=0.50,
        max_daily_turnover=1.0,
        max_drawdown=0.15,
        minimum_cash=0.0,
    ),
)

print(paper.summary)
print(paper.orders)
print(paper.fills)
print(paper.risk_events)

The paper broker supports market, limit, stop and stop-limit order primitives, order states, commissions, slippage, partial-fill modelling, position accounting, turnover controls, cash limits and a drawdown kill switch.

It does not connect to a live broker by default.

13. Preserve the entire research record

project.save_manifest("research_manifest.json")
project.report("research_dossier.html")

The manifest records paper, hypothesis, data-plan, feature, signal, portfolio, backtest, robustness, decision and workflow history fingerprints.

One-call quantitative stages

When the hypothesis and data are already available:

import asrquant as asr

project = asr.autoresearch(
    hypothesis="Rising yields predict value outperformance relative to growth.",
    data="research_panel.csv",
    tradable_assets=["VALUE", "GROWTH"],
    feature_plan=asr.FeaturePlan([
        asr.FeatureSpec(
            "yield_change",
            "US10Y",
            "diff",
            params={"periods": 20},
            availability_lag=1,
        )
    ]),
    signal_spec=asr.SignalSpec(
        "yield_change",
        long_asset="VALUE",
        short_asset="GROWTH",
        upper=0.15,
        lower=-0.15,
    ),
)

print(project.decision_result.summary)

The short API does not remove the intermediate objects. Every generated plan remains available for inspection and modification.

See docs/research_workflow.md for the standalone guide.


Algorithmic trading and paper execution

The built-in execution layer is safe by default: it simulates order creation, acceptance, fills, commissions, slippage, positions, cash and risk events without connecting to a live broker.

import asrquant as asr

lab = asr.open_lab("prices.csv", date_column="Date")
weights = lab.strategy("sma", fast=20, slow=100)

paper = lab.paper_trade(
    weights,
    initial_capital=100_000,
    commission_bps=1,
    slippage_bps=2,
    policy=asr.RiskPolicy(
        max_gross_leverage=1.0,
        max_position_weight=0.20,
        max_daily_turnover=0.50,
        max_drawdown=0.15,
        minimum_cash=0.0,
    ),
)

print(paper.summary)
print(paper.orders)
print(paper.fills)
print(paper.risk_events)

The public primitives are Order, Fill, OrderSide, OrderType, OrderStatus, RiskPolicy, PaperBroker, PaperTrader, PaperTradingResult, paper_trade and the BrokerAdapter protocol.

Order types are market, limit, stop and stop-limit. The paper broker supports immediate or partial fills, commissions and slippage. The trader enforces maximum leverage, position size, order notional, daily turnover, short-selling permission, minimum cash and a maximum-drawdown kill switch.

A live or external paper broker can implement the following explicit adapter contract:

class MyBroker:
    def submit_order(self, order, market_price): ...
    def cancel_order(self, order_id): ...
    def positions(self): ...
    def cash_balance(self): ...

ASRQuant does not include credentials, exchange authentication or unrestricted live-order submission. See docs/algorithmic_trading.md.


Data ingestion and preparation

Required price-panel format

The standard input is created with asr.series(...), asr.frame(...), a file loader, a provider, or any compatible time-indexed table with:

  • a unique DatetimeIndex or an index convertible to dates;
  • one column per asset or numerical series;
  • strictly positive values for price data;
  • no infinite values;
  • at least two observations.
import asrquant as asr

prices = asr.frame(
    {
        "SPY": [100.0, 101.2, 100.8, 102.5],
        "QQQ": [100.0, 102.1, 101.6, 103.9],
    },
    index=asr.date_range("2026-01-01", periods=4, freq="D"),
)

lab = asr.open_lab(prices)

Missing-data policies

lab = QuantLab(prices, missing_data="raise")  # reject missing observations
lab = QuantLab(prices, missing_data="drop")   # remove rows containing missing values
lab = QuantLab(prices, missing_data="ffill")  # forward-fill, then drop unresolved rows

Forward filling is an explicit user choice because it may be inappropriate for some instruments or research questions.

CSV

from asrquant import QuantLab

lab = QuantLab.from_csv(
    "prices.csv",
    date_column="Date",
    columns=["SPY", "QQQ", "TLT"],
    missing_data="drop",
)

Expected CSV structure:

Date,SPY,QQQ,TLT
2024-01-02,472.65,402.12,98.41
2024-01-03,470.26,399.77,99.02

If date_column is omitted, the first column is used.

Other local file formats

QuantLab.from_file(...) supports:

  • CSV and text files;
  • Parquet;
  • Excel;
  • JSON;
  • Feather.
lab = QuantLab.from_file("prices.parquet", date_column="timestamp")
lab = QuantLab.from_file("prices.xlsx", date_column="Date", sheet_name="Close")
lab = QuantLab.from_file("prices.json", date_column="date")
lab = QuantLab.from_file("prices.feather", date_column="timestamp")

Excel, Parquet and Feather may require the data optional dependencies.

Lower-level file loader

from asrquant import load_prices

prices = load_prices(
    "prices.csv",
    date_column="Date",
    columns=["SPY", "QQQ"],
)

SQL

import sqlite3
from asrquant import load_sql, QuantLab

connection = sqlite3.connect("market_data.db")
prices = load_sql(
    "SELECT timestamp, SPY, QQQ FROM daily_prices ORDER BY timestamp",
    connection,
    date_column="timestamp",
    columns=["SPY", "QQQ"],
)

lab = QuantLab(prices)

Returns

from asrquant import simple_returns, log_returns

simple = simple_returns(prices)
logs = log_returns(prices)

No implicit forward fill is used during return calculation.

Data-quality report

print(lab.quality)

or:

from asrquant import data_quality_report

quality = data_quality_report(prices)

The report includes row count, column count, date range, missingness, duplicate timestamps, index monotonicity, median spacing, maximum gap and constant columns.

Data fingerprint

from asrquant import data_fingerprint

fingerprint = data_fingerprint(prices)
print(fingerprint)

The SHA-256 fingerprint incorporates values, timestamps and columns.

OHLCV validation and resampling

Canonical OHLCV columns are Open, High, Low, Close and optionally Volume.

from asrquant.data import validate_ohlcv, resample_ohlcv

clean_ohlcv = validate_ohlcv(ohlcv)
weekly = resample_ohlcv(clean_ohlcv, "W-FRI")

Resampling uses finance-consistent rules:

  • Open: first;
  • High: maximum;
  • Low: minimum;
  • Close: last;
  • Volume: sum.

Remote providers

Available providers:

Provider Name Credentials Typical use
Yahoo Finance yahoo or yfinance No Equities, ETFs, indices and other supported instruments
Binance Spot binance No Public cryptocurrency OHLCV
Alpha Vantage alpha_vantage API key Equities and supported Alpha Vantage series
FRED fred API key Macroeconomic and interest-rate series

Yahoo Finance

lab = QuantLab.from_provider(
    "yahoo",
    ["SPY", "QQQ", "TLT"],
    start="2020-01-01",
    end="2026-01-01",
    interval="1d",
    field="Close",
)

Requires:

pip install ".[data]"

Binance

lab = QuantLab.from_provider(
    "binance",
    "BTCUSDT",
    interval="1h",
    limit=1000,
    field="Close",
)

Alpha Vantage

export ALPHAVANTAGE_API_KEY="your-key"
lab = QuantLab.from_provider(
    "alpha_vantage",
    "IBM",
    interval="daily",
    adjusted=True,
    outputsize="full",
    field="Adjusted Close",
)

The key can also be passed explicitly:

lab = QuantLab.from_provider(
    "alpha_vantage",
    "IBM",
    provider_kwargs={"api_key": "your-key"},
    interval="daily",
)

FRED

export FRED_API_KEY="your-key"
lab = QuantLab.from_provider(
    "fred",
    ["DGS10", "DGS2"],
    observation_start="2015-01-01",
    observation_end="2026-01-01",
    field="Value",
)

Provider-neutral download function

from asrquant import download

prices = download(
    "yahoo",
    ["SPY", "QQQ"],
    start="2020-01-01",
    field="Close",
)

Near-real-time polling

PollingFeed repeatedly calls a provider's quote method. It is appropriate for research dashboards and data collection, not order execution.

from asrquant import BinanceProvider, PollingFeed

provider = BinanceProvider()
feed = PollingFeed(provider, "BTCUSDT", interval_seconds=60)

for quote in feed.stream(max_updates=5, interval="1m", limit=1):
    print(quote.name, quote["Close"], quote.attrs["received_at"])

A user application is responsible for persistence, retry policies, rate-limit management and interruption handling.


The QuantLab interface

QuantLab is the unified high-level entry point.

from asrquant import QuantLab

lab = QuantLab(prices)

Important attributes:

lab.prices          # validated price DataFrame
lab.returns         # simple returns
lab.assets          # asset names
lab.quality         # data-quality report
lab.source_metadata # origin and loading metadata
lab.last_weights    # most recently generated weights
lab.last_result     # most recent BacktestResult

Main methods:

QuantLab.from_file(...)
QuantLab.from_csv(...)
QuantLab.from_provider(...)
QuantLab.strategy(...)
QuantLab.backtest(...)
QuantLab.audit(...)
QuantLab.sweep(...)
QuantLab.surface(...)
QuantLab.animate_surface(...)
QuantLab.parameter_surface(...)
QuantLab.explore(...)
QuantLab.surface_from_frame(...)
QuantLab.backtest_parameter_surface(...)
QuantLab.monte_carlo(...)
QuantLab.option(...)
QuantLab.martingale_test(...)
QuantLab.regress(...)
QuantLab.ml_features(...)
QuantLab.ml_walk_forward(...)
QuantLab.plot(...)

Quick data plots

lab.plot("prices")
lab.plot("prices", normalize=True)
lab.plot("prices", log_scale=True)
lab.plot("returns")
lab.plot("cumulative_returns")

After a backtest, lab.plot(...) forwards unknown plot kinds to the latest BacktestResult.


Strategies

Built-in strategy names

Strategy Accepted names Main parameters
Equal-weight buy and hold buy_hold, buy-and-hold gross=1.0
Moving-average crossover sma, sma_crossover fast=20, slow=100, long_short=False, gross=1.0
Cross-sectional momentum momentum lookback=126, top_fraction=0.2, long_short=True, gross=1.0
Rolling z-score mean reversion mean_reversion lookback=20, z_entry=1.0, long_short=True, gross=1.0
Volatility targeting vol_target, volatility_target target_vol=0.1, lookback=20, annualization=252, max_leverage=2.0
Donchian breakout breakout lookback=55, exit_lookback=20, gross=1.0
Bollinger mean reversion bollinger, bollinger_mean_reversion window=20, entry_z=2.0, exit_z=0.5, gross=1.0
RSI strategy rsi window=14, oversold=30, overbought=70, gross=1.0
Two-asset pairs strategy pairs, pairs_zscore asset_a, asset_b, lookback=60, entry_z=2.0, gross=1.0

Generate weights without backtesting

weights = lab.strategy("sma", fast=20, slow=100)
weights = lab.strategy(
    "momentum",
    lookback=126,
    top_fraction=0.25,
    long_short=True,
    gross=1.0,
)

Custom strategy

A custom strategy receives the validated price panel and must return target weights with matching timestamps and asset columns.

def custom_trend(prices, window: int = 50):
    trend = prices / prices.rolling(window).mean() - 1.0
    raw = trend.rank(axis=1, pct=True) - 0.5
    gross = raw.abs().sum(axis=1).replace(0.0, float("nan"))
    return raw.div(gross, axis=0).fillna(0.0)


result = lab.backtest(custom_trend, window=50, costs_bps=5)

Direct weight input

weights = asr.frame(
    {"SPY": 0.6, "QQQ": 0.4},
    index=lab.prices.index,
)

result = lab.backtest(weights)

A single-column weight series is not silently broadcast across multiple assets.


Backtesting

Minimal backtest

result = lab.backtest(
    "sma",
    fast=20,
    slow=100,
    costs_bps=5,
    execution_delay=1,
)

costs_bps is a convenience argument that sets commission basis points while preserving the other components of the active CostModel. For a complete cost model, use BacktestSpec.

Complete backtest contract

from asrquant import BacktestSpec, CostModel, MissingDataPolicy

spec = BacktestSpec(
    name="Cross-sectional momentum research",
    initial_capital=250_000,
    annualization=252,
    execution_delay=1,
    rebalance="ME",
    long_only=False,
    max_gross_leverage=1.5,
    max_abs_weight=0.25,
    risk_free_rate=0.02,
    missing_data=MissingDataPolicy.DROP,
    costs=CostModel(
        commission_bps=2.0,
        spread_bps=3.0,
        slippage_bps=2.0,
        borrow_bps_annual=75.0,
        impact_coefficient=0.0001,
        impact_exponent=1.5,
    ),
    metadata={"research_cycle": "C01", "author": "ASR"},
)

result = lab.backtest(
    "momentum",
    lookback=126,
    top_fraction=0.2,
    spec=spec,
)

Execution delay

execution_delay=1 means a target weight formed at timestamp t is first applied to the return ending at t+1.

spec = BacktestSpec(execution_delay=1)

execution_delay=0 is allowed for diagnostics but can create same-bar leakage depending on how the signal was constructed.

Rebalance frequency

Common values:

bar      every observation
D        daily
W-FRI    weekly on Friday
ME       month end
QE       quarter end

Any valid pandas resampling rule compatible with the engine may be supplied.

Cost model

Linear trading cost per period is based on turnover and:

commission_bps + spread_bps + slippage_bps

Nonlinear impact is:

impact_coefficient * turnover ** impact_exponent

Short borrow cost is applied to short exposure using borrow_bps_annual and the specified annualization factor.

Backtest result fields

result.prices
result.asset_returns
result.target_weights
result.effective_weights
result.gross_returns
result.net_returns
result.equity
result.turnover
result.costs
result.cost_breakdown
result.spec
result.metadata

Metrics

print(result.metrics)

Transaction ledger

trades = result.trades
print(trades.head())

The ledger contains timestamp, asset, weight change, direction, execution-reference price and resulting target weight.

Export time series

frame = result.to_frame()
frame.to_csv("backtest_timeseries.csv")

Compare with a benchmark

benchmark = lab.returns["SPY"]
comparison = result.compare(benchmark)
print(comparison)

Compare several backtests

from asrquant import compare_backtests

results = {
    "SMA": lab.backtest("sma", fast=20, slow=100),
    "Momentum": lab.backtest("momentum", lookback=126),
    "Vol target": lab.backtest("vol_target", target_vol=0.10),
}

comparison = compare_backtests(results)
print(comparison)

Plot backtest results

result.plot()                         # dashboard
result.plot("equity")
result.plot("drawdown")
result.plot("equity_drawdown")
result.plot("rolling_metrics", window=63)
result.plot("monthly_heatmap")
result.plot("annual_returns")
result.plot("turnover")
result.plot("costs")
result.plot("weights")
result.plot("exposures")
result.plot("return_contributions")
result.plot("trade_pnl")
result.plot("benchmark", benchmark_returns=benchmark)

HTML report

path = result.report(
    "reports/sma_report.html",
    title="SMA strategy research report",
)
print(path)

Performance and risk metrics

from asrquant import summary_metrics

metrics = summary_metrics(
    result.net_returns,
    annualization=252,
    risk_free_rate=0.02,
    benchmark=benchmark,
    turnover=result.turnover,
)

Lower-level functions are available in asrquant.metrics:

from asrquant.metrics import (
    cumulative_returns,
    annualized_return,
    annualized_volatility,
    sharpe_ratio,
    sortino_ratio,
    calmar_ratio,
    omega_ratio,
    max_drawdown,
    drawdown_series,
    drawdown_duration,
    value_at_risk,
    parametric_var,
    expected_shortfall,
    conditional_drawdown_at_risk,
    hit_rate,
    profit_factor,
    alpha_beta,
    information_ratio,
    capture_ratio,
    m_squared,
    kelly_fraction,
    probabilistic_sharpe_ratio,
    deflated_sharpe_ratio,
)

Examples:

from asrquant.metrics import value_at_risk, expected_shortfall

var_95 = value_at_risk(result.net_returns, level=0.95)
es_95 = expected_shortfall(result.net_returns, level=0.95)
from asrquant.metrics import parametric_var

gaussian_var = parametric_var(result.net_returns, method="gaussian")
cornish_fisher_var = parametric_var(result.net_returns, method="cornish_fisher")

Probabilistic and deflated Sharpe outputs are diagnostics, not guarantees that a strategy will remain profitable.


Implementation audits

The same logical strategy can produce materially different results under different defensible execution conventions. QuantLab.audit(...) reruns fixed weights under alternative delays, costs and rebalance rules.

audit = lab.audit(
    "sma",
    fast=20,
    slow=100,
    execution_delays=(0, 1, 2),
    linear_costs_bps=(0, 5, 10, 25),
    rebalances=("bar", "W-FRI", "ME"),
)

print(audit.summary)
print(audit.diagnostics)
audit.plot()

The result contains:

audit.summary      # metrics for every implementation contract
audit.diagnostics  # cross-contract dispersion statistics
audit.results      # dictionary of individual BacktestResult objects

Lower-level usage:

from asrquant import implementation_audit

audit = implementation_audit(
    prices,
    weights,
    base_spec=spec,
    execution_delays=(0, 1, 2),
    linear_costs_bps=(0, 5, 10),
    rebalances=("bar", "ME"),
)

Parameter sweeps, surfaces and animations

Simple parameter sweep

results = lab.sweep(
    "sma",
    {
        "fast": [5, 10, 20, 30],
        "slow": [50, 100, 150, 200],
    },
    metric="Sharpe",
    costs_bps=5,
)

print(results.head())

Generic two-dimensional surface

import asrquant as asr

surface = lab.surface(
    lambda risk_aversion, cost: -(risk_aversion - 2.0) ** 2 - 0.04 * cost,
    x_values=asr.math.linspace(0.1, 5.0, 40),
    y_values=asr.math.linspace(0.0, 25.0, 40),
    x_name="risk_aversion",
    y_name="cost",
    z_name="utility",
)

surface.plot("surface")
surface.plot("heatmap")
surface.plot("contour")

Multidimensional parameter exploration

Any finite experiment can be explored if each parameter combination returns a scalar or an object from which a scalar can be extracted.

parameter_grid = {
    "risk_aversion": asr.math.linspace(0.1, 5.0, 30),
    "transaction_cost": asr.math.linspace(0.0, 25.0, 30),
    "hedge_frequency": [1, 5, 20],
    "volatility": [0.15, 0.20, 0.30, 0.40],
    "model": ["linear", "neural"],
}

surface = lab.parameter_surface(
    train_and_evaluate,
    parameter_grid,
    x="risk_aversion",
    y="transaction_cost",
    animate_by=["hedge_frequency", "volatility", "model"],
    metric="metrics.entropic_utility",
    z_name="entropic utility",
    n_jobs=4,
    max_evaluations=100_000,
)

lab.explore(...) is an alias:

surface = lab.explore(
    experiment,
    parameter_grid,
    x="parameter_a",
    y="parameter_b",
    animate_by=["regime", "model"],
    metric=lambda output: output.test_metrics["cvar"],
)

Metric extraction

The experiment may return:

  • a Python number;
  • a dictionary;
  • a pandas Series;
  • a dataclass;
  • a BacktestResult;
  • a machine-learning result;
  • a pricing result;
  • any custom object.

Select the plotted scalar with:

metric="Sharpe"
metric="metrics.Sharpe"
metric="out_of_sample.cvar"
metric=lambda result: result.losses[-1]

Backtest parameter surface

surface = lab.backtest_parameter_surface(
    "sma",
    {
        "fast": [5, 10, 20, 30],
        "slow": [40, 80, 120, 160],
        "costs_bps": [0, 5, 10, 20],
        "execution_delay": [0, 1, 2],
    },
    x="fast",
    y="slow",
    animate_by=["costs_bps", "execution_delay"],
    metric="Sharpe",
    error_policy="nan",
)

costs_bps and execution_delay are applied to the backtest contract. Other grid parameters are passed to the strategy.

Surface from an existing DataFrame or CSV

import asrquant as asr

results = asr.read_table("experiment_results.csv")

surface = lab.surface_from_frame(
    results,
    x="gamma",
    y="cost_bps",
    z="utility",
    frame_cols=["hedge_every", "volatility", "model"],
    agg="mean",
)

Vectorized formulas

Set vectorized=True when the function accepts arrays for the two surface axes.

surface = lab.surface(
    vectorized_formula,
    strikes,
    maturities,
    x_name="strike",
    y_name="maturity",
    fixed_params={"spot": 100, "rate": 0.03, "volatility": 0.20},
    vectorized=True,
)

Positional functions

The default call_style="keyword" calls the experiment with named parameters. Use positional style only when necessary:

surface = lab.surface(
    positional_function,
    x_values,
    y_values,
    call_style="positional",
)

Error policy

surface = lab.parameter_surface(
    experiment,
    parameter_grid,
    x="x",
    y="y",
    error_policy="nan",
)

error_policy="nan" preserves the grid and stores failed evaluations as missing values. error_policy="raise" stops at the first error.

Evaluation limit

surface = lab.parameter_surface(
    experiment,
    parameter_grid,
    x="x",
    y="y",
    animate_by=["a", "b"],
    max_evaluations=50_000,
)

The safeguard prevents accidental Cartesian explosions.

Progress callback

def progress(done: int, total: int) -> None:
    print(f"{done}/{total}")

surface = lab.parameter_surface(
    experiment,
    parameter_grid,
    x="x",
    y="y",
    progress=progress,
)

Surface result API

surface.summary
surface.frame_count
surface.frame_labels
surface.is_animated
surface.parameters_at(0)
surface.best("max")
surface.best("min")
surface.to_frame(frame=0)
surface.to_long_frame()

Static plotting

surface.plot("surface", frame=0)
surface.plot("heatmap", frame=0)
surface.plot("contour", frame=0)
surface.plot("surface", frame=0, interactive=True)

Animation in memory

animation = surface.animate(
    kind="surface",
    interval=250,
    repeat=True,
    stable_scale=True,
    elevation=30,
    azimuth=-60,
    rotate_camera=2,
)

Available animation kinds:

surface
heatmap
contour

Animation export

surface.save_animation("surface.html")
surface.save_animation("surface.gif", kind="heatmap", fps=8)
surface.save_animation("surface.mp4", kind="surface", fps=12)
  • HTML uses an interactive Plotly animation with play/pause controls and a slider.
  • GIF requires a compatible Matplotlib/Pillow writer.
  • MP4 requires FFmpeg.

Export each frame

paths = surface.export_frames(
    "frames",
    kind="contour",
    prefix="deep_hedging",
)

Scientific interpretation

A geometric surface can display only two explicit axes. Higher-dimensional parameters are represented as:

  • fixed values;
  • successive slices;
  • animation frames;
  • categorical frame labels.

The plotting engine does not remove the need to justify parameter ranges, metrics, random seeds or out-of-sample protocols.

See docs/parameter_surfaces.md for the dedicated surface guide.


Stochastic simulation and Monte Carlo

Unified simulation interface

simulation = lab.monte_carlo(
    "gbm",
    drift=0.05,
    volatility=0.20,
    maturity=1.0,
    steps=252,
    paths=10_000,
    random_state=7,
)

For a single-asset QuantLab, the latest observed price is used as initial unless explicitly supplied.

Available stochastic models

Model Dispatcher names Main parameters
Arithmetic Brownian motion abm, brownian initial, drift, volatility, maturity, steps, paths
Geometric Brownian motion gbm initial, drift, volatility, antithetic
Ornstein-Uhlenbeck ou, ornstein_uhlenbeck initial, speed, mean, volatility
Cox-Ingersoll-Ross cir initial, speed, mean, volatility
Vasicek vasicek initial, speed, mean, volatility
Heston heston initial_variance, mean_reversion, long_variance, vol_of_vol, correlation
Merton jump diffusion merton, jump_diffusion jump_intensity, jump_mean, jump_volatility

Direct model functions

from asrquant import (
    arithmetic_brownian_motion,
    geometric_brownian_motion,
    ornstein_uhlenbeck,
    cir_process,
    vasicek_process,
    heston_process,
    merton_jump_diffusion,
)

GBM

from asrquant import geometric_brownian_motion

simulation = geometric_brownian_motion(
    initial=100,
    drift=0.05,
    volatility=0.20,
    maturity=1,
    steps=252,
    paths=20_000,
    antithetic=True,
    random_state=7,
)

Heston

simulation = lab.monte_carlo(
    "heston",
    initial=100,
    drift=0.03,
    initial_variance=0.04,
    mean_reversion=2.0,
    long_variance=0.04,
    vol_of_vol=0.5,
    correlation=-0.7,
    maturity=1,
    steps=252,
    paths=20_000,
    random_state=7,
)

For Heston simulations, variance paths are available as:

simulation.variance_paths

Correlated multi-asset GBM

import asrquant as asr

simulation = asr.correlated_gbm(
    initial=asr.math.array([100.0, 80.0, 120.0]),
    drift=asr.math.array([0.05, 0.04, 0.06]),
    volatility=asr.math.array([0.20, 0.15, 0.25]),
    correlation=asr.math.array(
        [
            [1.0, 0.40, 0.30],
            [0.40, 1.0, 0.20],
            [0.30, 0.20, 1.0],
        ]
    ),
    maturity=1.0,
    steps=252,
    paths=5_000,
    random_state=7,
)

Simulation outputs

simulation.paths
simulation.terminal
simulation.summary
simulation.parameters
simulation.model

Simulation plots

simulation.plot("paths", max_paths=50)
simulation.plot("terminal")
simulation.plot("fan")

Additional functions:

from asrquant.viz.simulation import (
    quantile_bands,
    first_passage_distribution,
    increment_diagnostics,
    terminal_distribution,
    paths,
)

Generic Monte Carlo pricing

import asrquant as asr

simulation = asr.geometric_brownian_motion(
    initial=100,
    drift=0.03,
    volatility=0.20,
    maturity=1,
    steps=252,
    paths=100_000,
    antithetic=True,
    random_state=7,
)

result = asr.monte_carlo_price(
    simulation,
    payoff=lambda terminal: asr.math.maximum(terminal - 100, 0.0),
    rate=0.03,
    maturity=1.0,
    confidence=0.95,
)

print(result.summary)

The output includes price, standard error, confidence interval, discounted pathwise payoffs and the underlying simulation.

European option Monte Carlo

from asrquant import european_option_mc

mc = european_option_mc(
    spot=100,
    strike=100,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
    paths=100_000,
    antithetic=True,
    random_state=7,
)

Asian option Monte Carlo

from asrquant import asian_option_mc

asian = asian_option_mc(
    spot=100,
    strike=100,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
    paths=50_000,
    steps=252,
    random_state=7,
)

Synthetic regime-switching market

from asrquant.simulation import regime_switching_prices

synthetic_prices = regime_switching_prices(
    periods=1500,
    assets=4,
    start=100,
    random_state=7,
)

Stationary bootstrap

from asrquant.simulation import stationary_bootstrap

samples = stationary_bootstrap(
    lab.returns,
    samples=1000,
    expected_block=20,
    random_state=7,
)

Derivative pricing

High-level option interface

option = lab.option(
    "black_scholes",
    strike=100,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
)

print(option.summary)

For a one-asset QuantLab, the latest observed value is used as spot or forward unless supplied explicitly.

Supported pricing models

Model Common names Notes
Black-Scholes-Merton black_scholes, bsm European call or put, optional dividend yield
Bachelier normal model bachelier, normal European option on a forward with normal volatility
Black-76 black76, black_76 European option on a forward or futures price
Cox-Ross-Rubinstein crr, binomial European or American call/put
Monte Carlo monte_carlo, mc European option under risk-neutral GBM

Black-Scholes-Merton

from asrquant import black_scholes_price, black_scholes_greeks

price = black_scholes_price(
    spot=100,
    strike=100,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
    dividend=0.01,
)

greeks = black_scholes_greeks(
    spot=100,
    strike=100,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
    dividend=0.01,
)

Greeks include delta, gamma, vega, theta and rho.

Bachelier

from asrquant import bachelier_price, bachelier_greeks

price = bachelier_price(
    forward=100,
    strike=105,
    maturity=1,
    normal_volatility=12,
    option="call",
    discount=0.97,
)

Black-76

from asrquant import black76_price

price = black76_price(
    forward=100,
    strike=105,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
)

CRR binomial tree

from asrquant import crr_binomial_price

american_put = crr_binomial_price(
    spot=100,
    strike=105,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="put",
    steps=1000,
    dividend=0.0,
    american=True,
)

Implied volatility

from asrquant import implied_volatility

iv = implied_volatility(
    market_price=9.50,
    spot=100,
    strike=100,
    maturity=1,
    rate=0.03,
    option="call",
    model="black_scholes",
)

Supported inversion models are Black-Scholes-Merton, Black-76 and Bachelier.

Finite-difference Greeks

from asrquant.derivatives import finite_difference_greeks

fd = finite_difference_greeks(
    black_scholes_price,
    spot=100,
    volatility=0.20,
    rate=0.03,
    strike=100,
    maturity=1,
    option="call",
)

Payoff

import asrquant as asr

terminal = asr.math.linspace(50, 150, 500)
pnl = asr.options.option_payoff(
    terminal,
    strike=100,
    option="call",
    premium=9.41,
    position=1.0,
)

Derivative visualizations

from asrquant.viz.derivatives import (
    payoff_diagram,
    option_price_curve,
    greek_curves,
    greek_heatmap,
    greek_surface,
    volatility_surface,
    implied_volatility_smile,
    term_structure_slices,
    scenario_pnl_surface,
    model_comparison,
    monte_carlo_convergence,
    yield_curve,
)

Example volatility surface:

fig = volatility_surface(
    strikes,
    maturities,
    implied_vol_matrix,
    interactive=True,
)

Martingale diagnostics

diagnostic = lab.martingale_test(
    asset="SPY",
    rate=0.03,
    annualization=252,
    lags=10,
)

print(diagnostic.statistics)
print(diagnostic.conclusion)
diagnostic.plot()

Lower-level usage:

from asrquant import discount_process, martingale_diagnostics

discounted = discount_process(price_series, rate=0.03, annualization=252)
diagnostic = martingale_diagnostics(discounted, lags=10)

The diagnostics examine finite-sample implications such as:

  • mean increments;
  • predictability of increments;
  • serial correlation;
  • HAC-robust regression inference.

Failure to reject these diagnostics is not a mathematical proof that a process is a martingale.


Regression and econometrics

High-level regression interface

fit = lab.regress(
    y="Strategy",
    x=["Market", "Value", "Momentum"],
    method="ols",
    use_returns=True,
    covariance="HAC",
    maxlags=5,
)

print(fit.summary)
fit.plot("residuals")
fit.plot("coefficients")
fit.plot("fitted")

Available high-level methods:

ols
quantile
polynomial
logistic
ridge
lasso
elastic_net

OLS and robust covariance

from asrquant.statistics import ols

fit = ols(
    y,
    x,
    add_constant=True,
    covariance="HAC",
    maxlags=5,
)

Accepted covariance values:

HAC
HC0
HC1
HC2
HC3
nonrobust
classical

Regression result

fit.model                 # advanced internal fitted-model object
fit.coefficients
fit.confidence_intervals
fit.fitted
fit.residuals
fit.diagnostics
fit.summary

The OLS diagnostics include R-squared, adjusted R-squared, Durbin-Watson, Jarque-Bera p-value, Ljung-Box p-value, Breusch-Pagan p-value and White-test p-value when applicable.

Rolling regression

from asrquant.statistics import rolling_regression

rolling_beta = rolling_regression(
    y=asset_returns,
    x=factor_returns,
    window=63,
)

Factor regression

from asrquant.statistics import factor_regression

factor_fit = factor_regression(
    asset_returns=strategy_returns,
    factors=factors,
    risk_free=risk_free_series,
    covariance="HAC",
    maxlags=5,
)

Quantile regression

from asrquant.statistics import quantile_regression

left_tail = quantile_regression(y, x, quantile=0.05)
median = quantile_regression(y, x, quantile=0.50)

Polynomial regression

from asrquant.statistics import polynomial_regression

fit = polynomial_regression(y, x, degree=3, covariance="HAC")

Regularized regression

from asrquant.statistics import regularized_regression

ridge = regularized_regression(y, x, method="ridge", alpha=1.0)
lasso = regularized_regression(y, x, method="lasso", alpha=0.01)
elastic = regularized_regression(
    y,
    x,
    method="elastic_net",
    alpha=0.01,
    l1_ratio=0.5,
)

Logistic regression

from asrquant.statistics import logistic_regression

classification_fit = logistic_regression(
    direction_target,
    features,
    covariance="HC1",
)

Stationarity tests

from asrquant.statistics import stationarity_tests

stationarity = stationarity_tests(price_or_spread_series)
print(stationarity)

ADF and KPSS are reported together to reduce one-test overinterpretation.

Cointegration

from asrquant.statistics import cointegration_test

result = cointegration_test(series_a, series_b, trend="c")

Granger predictability

from asrquant.statistics import granger_causality

p_values = granger_causality(x, y, maxlag=5)

This is a predictive test and does not establish structural causality.

ARIMA and VAR

from asrquant.statistics import arima_fit, var_fit

arima = arima_fit(series, order=(1, 0, 1), trend="c")
var = var_fit(multivariate_returns, lags=2, trend="c")

Moving-block bootstrap

import asrquant as asr

bootstrap = asr.stats.block_bootstrap(
    result.net_returns,
    statistic=asr.math.mean,
    n_boot=5000,
    block_size=20,
    confidence=0.95,
    random_state=7,
)

Permutation test

from asrquant.statistics import permutation_test

permutation = permutation_test(
    strategy_a_returns,
    strategy_b_returns,
    n_permutations=5000,
    random_state=7,
)

Multiple testing

from asrquant.statistics import benjamini_hochberg

adjusted = benjamini_hochberg(p_values, alpha=0.05)

Regression visualizations

from asrquant.viz.regression import (
    regression_scatter,
    residual_diagnostics,
    coefficient_intervals,
    actual_vs_fitted,
    rolling_coefficients,
    residual_acf,
    prediction_interval,
    influence_plot,
    partial_residual_plot,
    factor_exposure_heatmap,
)

Machine learning

ASRQuant's built-in ML workflow emphasizes chronological evaluation. It does not randomly shuffle time-series observations.

Feature generation

features = lab.ml_features(
    asset="SPY",
    windows=(5, 20, 63),
)

Generated features include one-period returns, log returns, momentum, volatility, rolling z-scores, rolling drawdowns and RSI.

Explicit lag features

from asrquant import lag_features

lagged = lag_features(
    lab.returns[["SPY", "QQQ"]],
    lags=[1, 2, 5, 10, 20],
    include_current=False,
)

Forward target

from asrquant import forward_target

target_return = forward_target(lab.prices["SPY"], horizon=5)
target_direction = forward_target(
    lab.prices["SPY"],
    horizon=5,
    classification=True,
)

The target is aligned at decision time and future observations are shifted backward only into the target column.

Walk-forward regression

import asrquant as asr

features = lab.ml_features("SPY").dropna()
target = forward_target(lab.prices["SPY"], horizon=5)

model = asr.models.random_forest(
    task="regression",
    trees=300,
    depth=5,
    seed=7,
)

ml_result = lab.ml_walk_forward(
    model,
    features,
    target,
    train_size=500,
    test_size=63,
    step=63,
    gap=5,
    expanding=True,
    task="regression",
)

print(ml_result.aggregate_metrics)
print(ml_result.fold_metrics)
ml_result.plot("predictions")
ml_result.plot("residuals")

Walk-forward classification

classifier = asr.models.logistic_regression(iterations=2000)

classification = lab.ml_walk_forward(
    classifier,
    features,
    target_direction,
    train_size=500,
    test_size=63,
    gap=5,
    task="classification",
)

print(classification.aggregate_metrics)
classification.plot("predictions")
classification.plot("roc")

Walk-forward result fields

ml_result.estimator_name
ml_result.task
ml_result.predictions
ml_result.actual
ml_result.probabilities
ml_result.fold_metrics
ml_result.aggregate_metrics
ml_result.fitted_models

ML visualizations

from asrquant.viz.ml import (
    prediction_path,
    residuals,
    confusion,
    roc,
    precision_recall,
    calibration,
    lift_curve,
    feature_importance,
    permutation_importance_plot,
    learning_curve_plot,
    regime_probabilities,
)

SHAP and hidden Markov functionality requires the optional ml dependency group where used by the user's workflow.


Portfolio optimization

from asrquant.optimization import estimate_covariance

returns = lab.returns.dropna()
expected_returns = returns.mean() * 252
covariance = estimate_covariance(returns, method="ledoit_wolf")

Covariance estimators

sample_cov = estimate_covariance(returns, method="sample")
ewma_cov = estimate_covariance(returns, method="ewma", span=60)
lw_cov = estimate_covariance(returns, method="ledoit_wolf")
oas_cov = estimate_covariance(returns, method="oas")

Minimum variance

from asrquant.optimization import minimum_variance

weights = minimum_variance(covariance, long_only=True)

Maximum Sharpe

from asrquant.optimization import maximum_sharpe

weights = maximum_sharpe(
    expected_returns,
    covariance,
    risk_free_rate=0.02,
    long_only=True,
)

Equal risk contribution

from asrquant.optimization import equal_risk_contribution

weights = equal_risk_contribution(covariance)

Maximum diversification

from asrquant.optimization import maximum_diversification

weights = maximum_diversification(covariance, long_only=True)

Hierarchical risk parity

from asrquant.optimization import hierarchical_risk_parity

weights = hierarchical_risk_parity(returns)

Efficient frontier

from asrquant.optimization import efficient_frontier

frontier = efficient_frontier(
    expected_returns,
    covariance,
    points=50,
    long_only=True,
)

Random portfolio cloud

from asrquant.optimization import random_frontier

cloud = random_frontier(
    expected_returns,
    covariance,
    n_portfolios=10_000,
    risk_free_rate=0.02,
    random_state=7,
)

Black-Litterman

import asrquant as asr

posterior_mean, posterior_covariance = asr.portfolio.black_litterman(
    covariance=covariance,
    market_weights=asr.math.array([0.4, 0.3, 0.2, 0.1]),
    risk_aversion=2.5,
    views=asr.math.array([0.03]),
    pick_matrix=asr.math.array([[1.0, -1.0, 0.0, 0.0]]),
    tau=0.05,
)

Risk contributions

from asrquant.optimization import (
    marginal_risk_contribution,
    risk_contributions,
    portfolio_return,
    portfolio_volatility,
)

mrc = marginal_risk_contribution(weights, covariance.to_numpy())
trc = risk_contributions(weights, covariance.to_numpy())
portfolio_mu = portfolio_return(weights, expected_returns.to_numpy())
portfolio_sigma = portfolio_volatility(weights, covariance.to_numpy())

Portfolio plots

from asrquant.viz.portfolio import (
    allocation_pie,
    covariance_heatmap,
    efficient_frontier,
    frontier_surface,
    risk_contribution_plot,
    rolling_risk_contributions,
    weights_heatmap,
    correlation_network,
    correlation_dendrogram,
    concentration_curve,
)

Volatility models

Realized volatility

from asrquant import realized_volatility

rv = realized_volatility(
    lab.returns["SPY"],
    window=21,
    annualization=252,
)

Parkinson estimator

from asrquant import parkinson_volatility

parkinson = parkinson_volatility(
    ohlcv["High"],
    ohlcv["Low"],
    window=21,
)

Garman-Klass estimator

from asrquant import garman_klass_volatility

gk = garman_klass_volatility(
    ohlcv["Open"],
    ohlcv["High"],
    ohlcv["Low"],
    ohlcv["Close"],
    window=21,
)

EWMA volatility

from asrquant import ewma_volatility

ewma = ewma_volatility(lab.returns["SPY"], decay=0.94)

GARCH

pip install ".[volatility]"
from asrquant import garch_forecast

garch = garch_forecast(
    lab.returns["SPY"].dropna(),
    p=1,
    q=1,
    horizon=5,
    distribution="t",
    annualization=252,
)

print(garch.forecast)
garch.plot()

Fixed income

ASRQuant 1.1.0 exposes the complete research-oriented rates stack through asr.rates and the high-level RateQuantLab: conventions and compounding; discount/zero/forward/par curves; deposit/FRA/swap bootstrapping; OIS/multi-curve projection; Nelson-Siegel/Svensson; bonds; FRAs/futures/IRS/basis; DV01/key-rate risk/convexity; caps/floors; swaptions; Black and normal rate vol; SABR; Vasicek/CIR/Hull-White/Ho-Lee/Black-Karasinski; HJM/LMM; RFR/OIS compounding; bond forwards; FX-forward/CIP and cross-currency foundations; zero-coupon inflation; Bermudan LSM; curve scenarios/key-rate hedging; PCA; carry/roll; no-arbitrage and interpolation-risk diagnostics.

import asrquant as asr

lab = asr.RateQuantLab.from_zero_rates(
    [0.25, 0.5, 1, 2, 3, 5, 7, 10],
    [0.020, 0.021, 0.022, 0.023, 0.024, 0.026, 0.027, 0.028],
)

par_5y = lab.par_swap(0, 5, frequency=2)
pv = lab.swap(0, 5, fixed_rate=0.025, notional=10_000_000)
print(lab.diagnostics())

For official ECB curve research, ECBProvider.yield_curve_history() downloads and aligns selected maturities and RateQuantLab.from_ecb() constructs the latest common curve. The full scope, conventions and limitations are documented in docs/interest_rate_derivatives.md.

Legacy bond helpers

Zero-coupon bond

from asrquant import zero_coupon_price

price = zero_coupon_price(
    face=100,
    rate=0.04,
    maturity=5,
    compounding=2,
)

Set compounding=None for continuous compounding.

Fixed-coupon bond price

from asrquant import bond_price

price = bond_price(
    face=100,
    coupon_rate=0.05,
    maturity=10,
    yield_rate=0.04,
    frequency=2,
)

Yield to maturity

from asrquant import yield_to_maturity

ytm = yield_to_maturity(
    price=108.11,
    face=100,
    coupon_rate=0.05,
    maturity=10,
    frequency=2,
)

Duration and convexity

from asrquant import macaulay_duration, modified_duration, convexity

mac = macaulay_duration(100, 0.05, 10, 0.04, frequency=2)
mod = modified_duration(100, 0.05, 10, 0.04, frequency=2)
conv = convexity(100, 0.05, 10, 0.04, frequency=2)

Cash flows and zero-curve bootstrap

from asrquant.fixed_income import bond_cashflows, bootstrap_zero_curve

cashflows = bond_cashflows(
    face=100,
    coupon_rate=0.05,
    maturity=5,
    frequency=2,
)

zero_curve = bootstrap_zero_curve(instruments, frequency=1)

Validation and stress testing

Walk-forward splits

from asrquant.validation import walk_forward_splits

for split in walk_forward_splits(
    n_samples=1000,
    train_size=500,
    test_size=100,
    step=100,
    expanding=True,
    gap=5,
):
    train_indices = split.train
    test_indices = split.test

Purged K-fold

from asrquant.validation import purged_kfold_splits

for split in purged_kfold_splits(
    n_samples=1000,
    n_splits=5,
    purge=5,
    embargo=10,
):
    ...

Look-ahead diagnostics

from asrquant.validation import detect_lookahead

diagnostics = detect_lookahead(signal, source_data)
print(diagnostics)

This is a heuristic diagnostic, not a formal proof of the absence of leakage.

Stress testing

from asrquant.validation import stress_returns

stress = stress_returns(
    result.net_returns,
    shocks={
        "mild selloff": -0.03,
        "severe selloff": -0.10,
    },
    windows={
        "historical period": ("2020-02-20", "2020-04-01"),
    },
)

print(stress)

Visualization system

ASRQuant v1.0.0 exposes 108 public plotting entry points across high-level result methods and lower-level visualization modules.

Backtest visualizations

Use BacktestResult.plot(...):

dashboard
equity
drawdown
equity_drawdown
rolling_metrics
monthly_heatmap
annual_returns
turnover
costs
weights
exposures
return_contributions
trade_pnl
benchmark

Market-data visualizations

from asrquant.viz.market import (
    price_chart,
    returns_chart,
    distribution,
    ecdf,
    qq_plot,
    box_violin,
    autocorrelation,
    lag_scatter,
    rolling_statistics,
    rolling_correlation,
    rolling_beta,
    correlation_heatmap,
    scatter_matrix,
    monthly_heatmap,
    calendar_heatmap,
    seasonality_boxplot,
    period_return_ranking,
    volatility_cone,
    candlestick,
)

Risk visualizations

from asrquant.viz.risk import (
    var_es_plot,
    rolling_var_es,
    var_exceedances,
    tail_plot,
    rolling_drawdown,
    drawdown_duration_plot,
    drawdown_table_plot,
    rolling_skew_kurtosis,
    expected_shortfall_contributions,
    risk_return_scatter,
    monte_carlo_fan,
    stress_plot,
    sensitivity_heatmap,
    implementation_audit_plot,
)

Generic 2D, 3D and animated visualizations

from asrquant.viz.general import (
    response_heatmap,
    parameter_heatmap,
    contour,
    surface3d,
    animate_surface,
)

Simulation visualizations

from asrquant.viz.simulation import (
    paths,
    terminal_distribution,
    quantile_bands,
    first_passage_distribution,
    increment_diagnostics,
    convergence_diagnostics,
    martingale_diagnostics_plot,
)

Regression visualizations

See Regression and econometrics.

Portfolio visualizations

See Portfolio optimization.

Derivative visualizations

See Derivative pricing.

Machine-learning visualizations

See Machine learning.

Market-microstructure visualizations

from asrquant.viz.microstructure import (
    bid_ask_spread,
    spread_distribution,
    order_book_depth,
    order_flow_imbalance,
    price_impact_scatter,
    slippage_curve,
    volume_profile,
    trade_timeline,
    intraday_seasonality,
)

Static versus interactive rendering

Use asr.visualize(...), asr.show(...) and asr.save(...) for backend-neutral rendering. Standard figures use the validated static engine internally; interactive surfaces and animations use the validated HTML engine internally. Normal users do not import either backend.

surface.plot("surface", interactive=True)

The exhaustive visualization reference is available in docs/visualization_catalog.md.


Reports and provenance

Portable HTML report

result.report("report.html")

Lower-level function:

from asrquant.report import create_html_report

html_or_path = create_html_report(
    result,
    output="report.html",
    title="Research report",
)

Experiment fingerprint

print(result.fingerprint)
print(result.spec.fingerprint())

Machine-readable manifest

from asrquant import build_manifest

manifest = build_manifest(
    result,
    project="Deep hedging parameter study",
    author="Alpha Stochastic Research",
    dataset="synthetic_heston_v1",
)

manifest.to_json("experiment_manifest.json")

The manifest records environment versions and data, specification and experiment fingerprints.

Store parameters and random seeds

Use the metadata field of BacktestSpec and save model configuration beside every exported result.

spec = BacktestSpec(
    metadata={
        "seed": 7,
        "data_vintage": "2026-07-31",
        "commit": "abc1234",
    }
)

Command-line interface

Help and version

asrquant --help
asrquant --version

Synthetic demonstration

asrquant demo --output asrquant_demo.html

Backtest a local file

asrquant backtest prices.csv \
  --date-column Date \
  --strategy sma \
  --fast 20 \
  --slow 100 \
  --costs-bps 5 \
  --execution-delay 1 \
  --output sma_report.html

Supported CLI strategy names:

buy_hold
sma
momentum
mean_reversion
vol_target
breakout
bollinger
rsi
pairs

Simulate a stochastic process

asrquant simulate \
  --model heston \
  --initial 100 \
  --drift 0.03 \
  --initial-variance 0.04 \
  --long-variance 0.04 \
  --mean-reversion 2.0 \
  --vol-of-vol 0.5 \
  --correlation -0.7 \
  --maturity 1 \
  --steps 252 \
  --paths 10000 \
  --seed 7 \
  --output heston_paths.csv

Supported models:

abm
gbm
ou
cir
vasicek
heston
merton

Price an option

asrquant price \
  --model black_scholes \
  --spot 100 \
  --strike 100 \
  --maturity 1 \
  --rate 0.03 \
  --volatility 0.20 \
  --option call

American CRR example:

asrquant price \
  --model crr \
  --spot 100 \
  --strike 105 \
  --maturity 1 \
  --rate 0.03 \
  --volatility 0.20 \
  --option put \
  --steps 1000 \
  --american

Download data

asrquant download SPY QQQ TLT \
  --provider yahoo \
  --start 2020-01-01 \
  --end 2026-01-01 \
  --field Close \
  --output prices.csv
asrquant download BTCUSDT \
  --provider binance \
  --interval 1h \
  --limit 1000 \
  --field Close \
  --output btc.csv

Complete end-to-end examples

1. CSV to audited report

from asrquant import QuantLab, BacktestSpec, CostModel, build_manifest

lab = QuantLab.from_csv(
    "prices.csv",
    date_column="Date",
    columns=["SPY", "QQQ", "TLT"],
    missing_data="drop",
)

print(lab.quality)

spec = BacktestSpec(
    execution_delay=1,
    rebalance="ME",
    max_gross_leverage=1.0,
    max_abs_weight=0.50,
    costs=CostModel(
        commission_bps=2,
        spread_bps=2,
        slippage_bps=1,
    ),
)

result = lab.backtest(
    "momentum",
    lookback=126,
    top_fraction=0.34,
    long_short=True,
    spec=spec,
)

print(result.metrics)
result.plot("dashboard")
result.report("momentum_report.html")

manifest = build_manifest(result, research_id="W01-C01")
manifest.to_json("momentum_manifest.json")

audit = lab.audit(
    "momentum",
    lookback=126,
    top_fraction=0.34,
    execution_delays=(0, 1, 2),
    linear_costs_bps=(0, 5, 10, 20),
    rebalances=("bar", "W-FRI", "ME"),
)

audit.summary.to_csv("implementation_audit.csv")
audit.plot()

2. Black-Scholes validation with Monte Carlo

from asrquant import black_scholes_price, european_option_mc

analytic = black_scholes_price(
    spot=100,
    strike=100,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
)

mc = european_option_mc(
    spot=100,
    strike=100,
    maturity=1,
    rate=0.03,
    volatility=0.20,
    option="call",
    paths=200_000,
    antithetic=True,
    random_state=7,
)

print("Analytic:", analytic)
print(mc.summary)
print("Absolute error:", abs(float(analytic) - mc.price))

3. Deep-hedging-style parameter animation

import asrquant as asr


def evaluate_hedging(
    risk_aversion,
    transaction_cost,
    hedge_frequency,
    volatility,
):
    # Replace this compact example with a PyTorch/JAX training callback.
    hedging_error = (
        0.08 / asr.math.sqrt(hedge_frequency)
        + 0.6 * volatility
        + 0.002 * transaction_cost * hedge_frequency
    )
    utility = -hedging_error - risk_aversion * hedging_error**2
    return {
        "metrics": {
            "utility": utility,
            "hedging_error": hedging_error,
        }
    }


surface = asr.open_lab(prices).explore(
    evaluate_hedging,
    {
        "risk_aversion": asr.math.linspace(0.1, 5.0, 35),
        "transaction_cost": asr.math.linspace(0.0, 25.0, 35),
        "hedge_frequency": [1, 5, 20, 63],
        "volatility": [0.15, 0.20, 0.30, 0.40],
    },
    x="risk_aversion",
    y="transaction_cost",
    animate_by=["hedge_frequency", "volatility"],
    metric="metrics.utility",
    z_name="utility",
    n_jobs=4,
)

print(surface.summary)
print(surface.best("max"))
surface.save_animation("hedging_landscape.html")
surface.save_animation("hedging_landscape.gif", kind="heatmap", fps=8)

4. Walk-forward ML to tradable weights

import asrquant as asr

lab = asr.open_lab("spy.csv", date_column="Date", columns=["SPY"])
features = lab.ml_features("SPY").dropna()
target = asr.forward_target(lab.prices["SPY"], horizon=5, classification=True)

model = asr.models.random_forest(
    task="classification",
    trees=300,
    depth=5,
    seed=7,
)

ml = lab.ml_walk_forward(
    model,
    features,
    target,
    train_size=500,
    test_size=63,
    step=63,
    gap=5,
    task="classification",
)

signal = (ml.probabilities > 0.55).astype(float)
weights = signal.reindex(lab.prices.index).fillna(0.0).to_frame("SPY")
result = lab.backtest(weights, costs_bps=5, execution_delay=1)

print(ml.aggregate_metrics)
print(result.metrics)
result.report("ml_strategy_report.html")

API map

Top-level exports

from asrquant import (
    QuantLab,
    BacktestSpec,
    CostModel,
    MissingDataPolicy,
    PlotConfig,
    BacktestResult,
    AuditResult,
    SurfaceResult,
    SimulationResult,
    MonteCarloPriceResult,
    MartingaleResult,
    WalkForwardMLResult,
    OptionPrice,
)

Data

clean_prices
simple_returns
log_returns
load_prices
load_sql
resample_ohlcv
data_quality_report
data_fingerprint

Backtesting and research

run_backtest
compare_backtests
implementation_audit
parameter_sweep
strategy_comparison

Surfaces

evaluate_surface
evaluate_surface_animation
evaluate_parameter_surface
surface_from_dataframe

Simulation

simulate
simulate_gbm
arithmetic_brownian_motion
geometric_brownian_motion
ornstein_uhlenbeck
cir_process
vasicek_process
heston_process
merton_jump_diffusion
monte_carlo_price
european_option_mc
asian_option_mc

Derivatives

black_scholes_price
black_scholes_greeks
bachelier_price
bachelier_greeks
black76_price
crr_binomial_price
implied_volatility
price_option

Providers

MarketDataProvider
AlphaVantageProvider
BinanceProvider
FREDProvider
YahooProvider
PollingFeed
download
get_provider

Fixed income

ASRQuant 1.1.0 exposes the complete research-oriented rates stack through asr.rates and the high-level RateQuantLab: conventions and compounding; discount/zero/forward/par curves; deposit/FRA/swap bootstrapping; OIS/multi-curve projection; Nelson-Siegel/Svensson; bonds; FRAs/futures/IRS/basis; DV01/key-rate risk/convexity; caps/floors; swaptions; Black and normal rate vol; SABR; Vasicek/CIR/Hull-White/Ho-Lee/Black-Karasinski; HJM/LMM; RFR/OIS compounding; bond forwards; FX-forward/CIP and cross-currency foundations; zero-coupon inflation; Bermudan LSM; curve scenarios/key-rate hedging; PCA; carry/roll; no-arbitrage and interpolation-risk diagnostics.

import asrquant as asr

lab = asr.RateQuantLab.from_zero_rates(
    [0.25, 0.5, 1, 2, 3, 5, 7, 10],
    [0.020, 0.021, 0.022, 0.023, 0.024, 0.026, 0.027, 0.028],
)

par_5y = lab.par_swap(0, 5, frequency=2)
pv = lab.swap(0, 5, fixed_rate=0.025, notional=10_000_000)
print(lab.diagnostics())

For official ECB curve research, ECBProvider.yield_curve_history() downloads and aligns selected maturities and RateQuantLab.from_ecb() constructs the latest common curve. The full scope, conventions and limitations are documented in docs/interest_rate_derivatives.md.

Legacy bond helpers

zero_coupon_price
bond_price
yield_to_maturity
macaulay_duration
modified_duration
convexity

Volatility

realized_volatility
parkinson_volatility
garman_klass_volatility
ewma_volatility
garch_forecast

Module namespaces

asrquant.data
asrquant.providers
asrquant.strategies
asrquant.backtest
asrquant.audit
asrquant.metrics
asrquant.statistics
asrquant.validation
asrquant.optimization
asrquant.derivatives
asrquant.simulation
asrquant.martingales
asrquant.machine_learning
asrquant.fixed_income
asrquant.volatility
asrquant.surfaces
asrquant.research
asrquant.provenance
asrquant.report
asrquant.viz

Additional references:


Reproducibility rules

For serious research, always:

  1. pin the ASRQuant version and dependency environment;
  2. preserve raw data or a stable data-vintage identifier;
  3. save the data fingerprint;
  4. save the complete BacktestSpec;
  5. use explicit random seeds;
  6. distinguish in-sample selection from out-of-sample evaluation;
  7. use execution delays consistent with information availability;
  8. model realistic commissions, spread, slippage, impact and borrow costs;
  9. report parameter-search breadth and multiple-testing controls;
  10. preserve code, manifests, reports and benchmark outputs together.

Example:

print(lab.source_metadata)
print(result.spec.to_dict())
print(result.spec.fingerprint())
print(result.fingerprint)

Current limitations

ASRQuant v1.1.0 retains the stable public API and adds Research Discovery and the expanded rates stack. It deliberately does not claim to be a complete production trading platform. Current limitations include:

  • a general-purpose asynchronous event-driven execution engine;
  • exchange queue-position and full limit-order-book simulation;
  • venue-specific fee schedules and maker/taker logic;
  • a full limit-order-book simulator;
  • automatic corporate-action reconstruction for arbitrary raw datasets;
  • borrow recalls or security-specific short availability;
  • a universal neural deep-hedging training implementation;
  • distributed training or cluster scheduling;
  • guarantees that external providers will maintain their APIs;
  • guarantees that a backtest is free from economic misspecification, data snooping or overfitting.

The generic experiment engine can wrap PyTorch, JAX, TensorFlow or custom research functions, but the user remains responsible for the correctness and computational cost of those functions.


Development and testing

Run the release-validation suite

python scripts/test_all.py

The runner executes each test module in a fresh interpreter. This prevents plotting, BLAS, optional statistical backends and third-party pytest plugins from leaking global state across test families. A conventional pytest invocation remains useful during local development for targeted modules.

Coverage

python -m coverage run --branch --source=src/asrquant -m pytest tests/test_production_readiness_v1.py
python -m coverage report --show-missing

Linting

ruff check src tests examples

Type checking

mypy src/asrquant

Build distributions

python -m build
python -m twine check dist/*

Validation status for v1.1.0

The supplied release was validated with:

  • 154 automated tests across isolated test modules, including 18 paper-contract tests;
  • wheel installation in an isolated environment;
  • source-distribution installation;
  • CSV, provider, backtest, simulation, pricing, martingale, regression, ML and surface examples;
  • HTML, GIF and MP4 surface-animation exports;
  • paper compilation and artifact-integrity checks.

See VALIDATION_v1.1.0.md; the original 1.0.0 validation is retained for provenance.

Runtime measurements are environment-specific and are not performance guarantees.

Contributing

Read:


Paper

The accompanying paper is:

ASRQuant: From Scientific Literature to Auditable Quantitative Decisions and Algorithmic Trading
Manuscript v0.1.0 — software release ASRQuant 1.0.0.

Files:

  • paper/ASRQuant_paper.pdf
  • paper/main.tex

The paper formalizes the package architecture, backtest contract, stochastic and statistical components, implementation sensitivity, multidimensional parameter surfaces and reproducible validation protocol.


Citation and license

ASRQuant is released under the MIT License. See LICENSE.

Citation metadata is available in CITATION.cff.

Alpha Stochastic Research. ASRQuant: From Scientific Literature to Auditable
Quantitative Decisions and Algorithmic Trading. Version 1.0.0.

Project links:

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

asrquant-1.1.0.tar.gz (5.8 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

asrquant-1.1.0-py3-none-any.whl (214.0 kB view details)

Uploaded Python 3

File details

Details for the file asrquant-1.1.0.tar.gz.

File metadata

  • Download URL: asrquant-1.1.0.tar.gz
  • Upload date:
  • Size: 5.8 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for asrquant-1.1.0.tar.gz
Algorithm Hash digest
SHA256 5568da563c6e91a5de8749f3b4036638b18b102090d23e5ed37c9ae465653ad0
MD5 43b704f4192095a6e2ddaa3147b5ddd4
BLAKE2b-256 9676c388ee197984c3c7e3f5575dcc8c7e79b20a702e0acec392114d71a1bfb1

See more details on using hashes here.

Provenance

The following attestation bundles were made for asrquant-1.1.0.tar.gz:

Publisher: release.yml on Alpha-Stochastic-Research/asr-quant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file asrquant-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: asrquant-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 214.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for asrquant-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5806b67703700e41386d82d99298620532d977013818e9e5daad0fe90cf6bc85
MD5 1e8a78d35baa3ef2e55f0792b9800f42
BLAKE2b-256 5006eb97667c41082d3ec5bd510f1a09b8db0447d419fc5886aec1f44fd73cf7

See more details on using hashes here.

Provenance

The following attestation bundles were made for asrquant-1.1.0-py3-none-any.whl:

Publisher: release.yml on Alpha-Stochastic-Research/asr-quant

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

1.2.0

2 files

This release

1.1.0 This release

2 files

1.0.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page