Skip to content

Python API

Generated by mkdocstrings from the docstrings of the fastmm package on every build. Start at Python; the prose reference is Python strategy API.

Everything below is reachable as fastmm.<name>; the compiled core is documented from the type stub python/fastmm/_core.pyi, so it is the same API the type checker sees.

Strategies

Strategy

Strategy()

Base class of Python strategies.

Define any subset of the hooks (on_start(self, ctx), on_book(self, ctx, inst, book), on_fill(self, ctx, fill), ... see HOOKS); undefined hooks are never called. Declare parameters with Param; override validate() for cross-field checks. With BacktestConfig.state_file set, state(self) -> bytes | str is written there at the end and restore(self, data: bytes) -> bool reads it at the start, after on_start.

strategy_name classmethod

strategy_name() -> str

Name in results and journals: py:<QualName>.

params classmethod

params() -> Dict[str, Param]

Every Param of the class and its bases, in declaration order (bases first).

schema classmethod

schema() -> list

Parameter schema like fastmm.strategies(): [{'name', 'type', 'default', 'min', 'max', 'doc'}, ...]; an unbounded side is None.

hooks classmethod

hooks() -> Tuple[str, ...]

The hooks this class defines, in table order. Raises TypeError for a hook with the wrong signature and warns about likely misspellings.

validate

validate() -> Optional[str]

Optional cross-field check after parameters are applied; return an error message.

configure

configure(params: Mapping[str, Any]) -> None

Apply parameters (like C++ StrategyBase::configure): keys in sorted order, the first error raises ValueError and leaves the previous values.

param_values

param_values() -> Dict[str, str]

Effective parameters as strings (BacktestResult.params).

publish

publish(inst: Any = None, **values: Any) -> bool

New parameter values for a running strategy with slow methods, from any thread: the same checks and effect as ctx.publish(). False when no session of this instance runs or the session did not take the update.

Param

Param(
    default: T,
    *,
    min: Optional[T] = None,
    max: Optional[T] = None,
    doc: str = "",
)

Bases: Generic[T]

A strategy parameter: typed from its default (bool, int or float), with an optional range.

Values from cfg.params (strings) and run_backtest(params=...) (Python scalars) are parsed like FASTMM_PARAM values in C++, with the same error messages. Read it as a plain attribute: self.levels.

parse

parse(value: Any) -> Tuple[Optional[str], Any]

(error, typed value): error is the C++ message without the parameter name.

format

format(value: Any) -> str

The value in a form parse() accepts (the C++ formatter).

Hot hooks

A @hot method is compiled with Numba and runs on the trading thread; see Write hot hooks in Python.

hot

hot(
    fn: Optional[Callable[..., Any]] = None,
    *,
    every: Optional[str] = None,
) -> Any

Marks a strategy method as a hot hook: @fastmm.hot for on_book, on_fill, on_quoting and on_connection, @fastmm.hot(every="100ms") for a timer hook. Every hot hook takes (self, ctx, book).

State

State(default: Any, *, doc: str = '')

A per-instrument value that hot hooks read and write and that persists between calls. The type comes from the default: bool, int (int64) or float.

HotCompileError

Bases: TypeError

A hot hook does not compile in Numba's nopython mode or fails the IR check.

Slow methods

An @every method runs on a second thread and publishes parameters to the hot hooks; see Run slow methods beside hot hooks.

every

every(
    period: str, *, timeout: str = DEFAULT_TIMEOUT
) -> Callable[[F], F]

Runs the decorated method, name(self, ctx), on the slow thread once per period of session time, first when the session starts. In a live session a call that runs longer than timeout stops the session. Units: ns, us, ms, s, m, h.

Running a strategy

run_backtest

run_backtest(
    config: "BacktestConfig",
    data: Any = None,
    strategy: StrategyArg = None,
    params: Optional[Mapping[str, Any]] = None,
    *,
    hot_cache: bool = True,
    slow_delay_ms: float = 0,
    max_param_age_ms: Optional[int] = None,
    fills_capacity: Optional[int] = None,
    recent_rows: int = 4096,
) -> "BacktestResult"

Run one backtest.

data: None (config.source / config.path), 'synthetic', a .fmj or .csv path, or a dict of numpy columns (see docs/python.md).

strategy: None (config.strategy), a registered C++ strategy name, a fastmm.Strategy subclass (a fresh instance per run) or an unused Strategy instance.

params: overrides applied on top of config.params (str, int, float or bool values).

hot_cache: for a strategy with @fastmm.hot methods, whether compiled hooks are kept in FastMM's Numba cache directory; False compiles every hook afresh.

C++ strategies run with the GIL released. A Python strategy holds the GIL for the whole run; a hook that raises stops the run and raises StrategyError (original exception as cause, partial result as .result). KeyboardInterrupt and SystemExit from a hook propagate unchanged, with the partial result as .result. Hot hooks run with the GIL released; a failing hot hook trips the kill switch and raises StrategyError with .status, .fail_code and .kill_reason.

Slow methods (on_start, on_stop and @fastmm.every methods of a class with hot hooks) run at simulated times. slow_delay_ms: simulated delay before their publishes take effect. max_param_age_ms: overrides [strategy] max_param_age_ms (0 disables); None keeps the configured value, or when that is 0 uses 3 x the shortest @fastmm.every period and at least 1000 ms. fills_capacity: fills the ring holds (None: from the order rate limit). recent_rows: rows per instrument that ctx.recent() keeps. A slow method that raises, or a full fills ring, stops the run and raises StrategyError with .slow_failure.

run_live

run_live(
    strategy: Any,
    config: Union[str, "os.PathLike[str]"],
    params: Optional[Mapping[str, Any]] = None,
    *,
    duration: Union[None, str, int, float] = None,
    dry_run: bool = False,
    journal: Optional[str] = None,
    no_journal: bool = False,
    status: Optional[str] = None,
    no_status: bool = False,
    log: Optional[str] = None,
    record_raw: Optional[str] = None,
    allow_inline_secrets: bool = False,
    standby: bool = False,
    takeover: bool = False,
    fills_capacity: Optional[int] = None,
    recent_rows: int = 4096,
    slow_tier_timeout_ms: int = SLOW_TIER_TIMEOUT_MS,
) -> int

Run a strategy with @fastmm.hot methods against the venues in config until SIGINT, SIGTERM, duration, a kill switch or a slow-tier failure; returns the exit code.

strategy: a fastmm.Strategy subclass or an unused instance. Parameters come from the configuration's [strategy.params] when [strategy] name names the class (py:Class, module:Class or Class), then from params.

The keyword arguments up to takeover are the fastmm-live options: duration in seconds or as "60s", journal, status and log paths, record_raw directory, standby and takeover (wait for, or take over, the instance lock of a running session of the same engine name). fills_capacity and recent_rows size the slow methods' fills ring and recent rows as in run_backtest. slow_tier_timeout_ms: how long to wait for the slow thread after the session stops; run_live warns and returns if it has not returned by then.

Configuration, parameter and compilation errors and an exception in on_start print a message and return 3. Raises ImportError without fastmm_live, TypeError for a strategy that is not a fastmm.Strategy and RuntimeError when a session already runs in this process.

replay

replay(
    journal: Any,
    strategy: type,
    *,
    verify: bool = True,
    config: "Optional[BacktestConfig]" = None,
    params: Optional[Mapping[str, Any]] = None,
    param_updates: bool = True,
    hot_cache: bool = True,
) -> ReplayResult

Replay a journal recorded with a Python hot strategy.

The engine consumes the journal's inputs, ParamUpdate messages included, with strategy's hot hooks; slow methods do not run. verify compares every outbound message with the recording. The parameters start at the values the journal records (a backtest journal), else at the configuration's [strategy.params]; params overrides them. max_param_age_ms is the recorded one. config defaults to the configuration the journal embeds. A different class, hot-hook source, fastmm or numba version, params or param_updates=False makes it a what-if replay: ReplayResult.what_if is True and the reasons are listed.

sweep

sweep(
    config: BacktestConfig,
    grid: dict,
    data: Any = None,
    strategy: str | None = None,
    threads: SupportsInt | SupportsIndex = 0,
) -> list

Cartesian parameter sweep on a thread pool (GIL released). grid: {param: [values]}. Returns [(params, BacktestResult)] in grid order, first parameter varying slowest. Every worker opens its own cursor over data (same forms as run_backtest). threads <= 0 uses every hardware thread.

walk_forward

walk_forward(
    config: BacktestConfig,
    grid: dict,
    folds: SupportsInt | SupportsIndex,
    data: Any = None,
    metric: str = "net_pnl",
    strategy: str | None = None,
    threads: SupportsInt | SupportsIndex = 0,
) -> dict

Walk-forward sweep: the grid runs on folds consecutive time slices of data, and the best point of fold i-1 by metric (net_pnl, realized_pnl, sharpe_bar, spread_captured_bps) is scored on fold i. Returns a dict: metric, folds (start_ts, end_ts, best, chosen, in_sample, out_of_sample, hindsight, scores, points), mean_in_sample, mean_out_of_sample, mean_hindsight, choice_changes and table (the printable report). folds=1 runs exactly fastmm.sweep. The synthetic market needs fill_model='l2_queue' for folds > 1.

What a hook is given

Context

What a Python strategy sees (mirrors the C++ StrategyContext). Usable only inside a hook.

instruments property

instruments: tuple

Every instrument of the table, as a tuple of Instrument (index == id).

now_ns property

now_ns: int

Engine time of the event, timer, start or stop being processed (ns).

balance

balance(
    venue: SupportsInt | SupportsIndex, asset: str
) -> Balance

One asset of a venue's account: the venue's last report (on_balance) moved by this engine's orders and fills since. known is False until the venue reports it.

balance_room

balance_room(
    inst: Any, side: SupportsInt | SupportsIndex, price: Any
) -> float | None

Largest quantity of the instrument on side at price the balance covers, rounded down to the lot; None while the venue has not reported the balance that side draws on.

balances_live

balances_live() -> bool

A venue has reported balances (before that, balance() is unknown everywhere).

best_ex_self

best_ex_self(
    inst: Any, side: SupportsInt | SupportsIndex
) -> tuple

(price, qty) of the book's best level on one side after own_qty is taken out; a level that was only ours is skipped. (0.0, 0.0) when none is left.

book

book(inst: Any) -> Any

The instrument's book view (valid until the current hook returns).

cancel

cancel(order_id: SupportsInt | SupportsIndex) -> bool

Request a cancel; False when the order is unknown or cannot be cancelled.

every

every(
    period_ns: SupportsInt | SupportsIndex,
    tag: SupportsInt | SupportsIndex = 0,
) -> int

Repeating timer; on_timer(ctx, timer_id, tag) fires every period_ns. Returns the id.

fees

fees(inst: Any) -> Fees

Maker and taker rates of the instrument: the ones the simulated venue charges.

funding

funding(inst: Any) -> Funding

The venue's funding of a perpetual (on_perp_state), with its age.

index

index(inst: Any) -> RefPrice

The venue's index price of a derivative (on_perp_state), with its age.

margin

margin(venue: SupportsInt | SupportsIndex = 0) -> Margin

The venue's margin: its account-wide margin where it reports one, else the settlement asset of its first derivative.

mark

mark(inst: Any) -> RefPrice

The venue's mark price of a derivative (on_perp_state), with its age.

once

once(
    delay_ns: SupportsInt | SupportsIndex,
    tag: SupportsInt | SupportsIndex = 0,
) -> int

One-shot timer after delay_ns. Returns the id.

order

order(order_id: SupportsInt | SupportsIndex) -> Any

Snapshot of an open order, or None once it is terminal.

own_qty

own_qty(
    inst: Any,
    side: SupportsInt | SupportsIndex,
    price: SupportsFloat | SupportsIndex,
    at_ns: Any = None,
) -> float

Our resting quantity that the venue's feed shows at this price, as of the book's last update or of venue time at_ns. 0.0 where the feed does not show our orders (a backtest).

portfolio

portfolio() -> Portfolio

PnL totals over every instrument.

queue_ahead

queue_ahead(order_id: SupportsInt | SupportsIndex) -> Any

Estimated quantity resting ahead of an open order at its price (the l2_queue model on the market data the strategy sees), or None before the ack and once it is terminal. Queues are tracked from the first call on; call it in on_start to cover every order from its ack.

randint

randint(
    lo: SupportsInt | SupportsIndex,
    hi: SupportsInt | SupportsIndex,
) -> int

Integer in [lo, hi] inclusive from the engine's seeded RNG.

random

random() -> float

Float in [0, 1) from the engine's seeded RNG (deterministic).

replace

replace(
    order_id: SupportsInt | SupportsIndex,
    price: Any,
    qty: Any,
) -> None

Cancel-replace (venues that support it). Raises OrderRejected.

request_stop

request_stop() -> None

End the backtest after the current event.

risk_headroom

risk_headroom(inst: Any) -> RiskHeadroom

What each [risk] limit still admits on the instrument now; an order of exactly a room passes that limit.

send

send(
    inst: Any,
    side: SupportsInt | SupportsIndex,
    price: Any,
    qty: Any,
    *,
    post_only: bool = False,
    reduce_only: bool = False,
    ioc: bool = False,
    tag: SupportsInt | SupportsIndex = 0,
) -> int

Send a limit order (risk-checked, journaled); returns its client order id. The price is rounded passively to the tick and the quantity down to the lot. Raises OrderRejected.

set_quotes

set_quotes(
    inst: Any, bids: Any = None, asks: Any = None
) -> bool

Desired ladder as sequences of (price, qty) floats, level 0 first (at most 8 per side). Prices are rounded to the tick passively (bids down, asks up), quantities down to the lot; a non-positive price or quantity drops the level. Returns False when quoting is disabled.

set_quotes_raw

set_quotes_raw(
    inst: Any, bids: Any = None, asks: Any = None
) -> bool

Like set_quotes with raw int prices and quantities (1e-8 scale), used as given.

venue_health

venue_health(
    venue: SupportsInt | SupportsIndex = 0,
) -> VenueHealth

Feed lag, order round trip and the feed-lag gate of a venue.

order_budget

order_budget(
    venue: SupportsInt | SupportsIndex = 0,
) -> OrderBudget

What the [risk] rate limit and the venue's own limits still admit now; a backtest knows only the local token bucket.

working_quote

working_quote(
    inst: Any,
    side: SupportsInt | SupportsIndex,
    level: SupportsInt | SupportsIndex = 0,
) -> Any

The open order in a quote slot, or None.

BookView

The engine's L2 book of one instrument. Valid only inside the hook that received it or called ctx.book().

imbalance

imbalance(levels: SupportsInt | SupportsIndex = 1) -> float

(bid qty - ask qty) / (bid qty + ask qty) over the top levels, in [-1, 1].

level

level(
    side: SupportsInt | SupportsIndex,
    i: SupportsInt | SupportsIndex,
) -> tuple

(price, qty) of level i (0 is best); (0.0, 0.0) past the depth.

OrderBook

OrderBook()

Price-aggregated L2 book (up to 256 levels per side) for research. Prices and quantities are floats, stored in 1e-8 fixed point; best-first level order.

truncated property

truncated: bool

A side overflowed 256 levels since the last snapshot.

apply_delta

apply_delta(bids: Any = None, asks: Any = None) -> None

Set/update levels; qty 0 deletes the level.

apply_snapshot

apply_snapshot(bids: Any, asks: Any) -> None

Replace the book. bids/asks: (n, 2) float arrays or sequences of (price, qty).

asks

asks(
    n: SupportsInt | SupportsIndex | None = None,
) -> ndarray

Top n ask levels (all if None) as an (k, 2) float64 array, best first.

best_ask

best_ask() -> Any

(price, qty) of the best ask, or None.

best_bid

best_bid() -> Any

(price, qty) of the best bid, or None.

bids

bids(
    n: SupportsInt | SupportsIndex | None = None,
) -> ndarray

Top n bid levels (all if None) as an (k, 2) float64 array, best first.

imbalance

imbalance(levels: SupportsInt | SupportsIndex = 1) -> float

(bid qty - ask qty) / (bid qty + ask qty) over the top levels, in [-1, 1].

microprice

microprice() -> Any

Top-of-book quantity-weighted mid, or None if a side is empty.

mid

mid() -> Any

(best bid + best ask) / 2, or None if a side is empty.

spread

spread() -> Any

best ask - best bid, or None if a side is empty.

weighted_mid

weighted_mid(
    levels: SupportsInt | SupportsIndex = 5,
) -> Any

Mid of the per-side quantity-weighted prices over the top levels.

Instrument

Reference data of one instrument (a copy; safe to keep).

round_price

round_price(
    price: SupportsFloat | SupportsIndex,
    side: SupportsInt | SupportsIndex,
) -> float

Round to the tick grid passively: bids down, asks up.

round_qty

round_qty(qty: SupportsFloat | SupportsIndex) -> float

Round down to the lot.

Portfolio

PnL totals over every instrument (a copy).

Order

Snapshot of one of our orders (a copy; safe to keep).

local_ack_ns property

local_ack_ns: int

When the ack reached us (recv_ts, ns); 0 before the ack.

sent_ns property

sent_ns: int

Engine time the order, or its latest acknowledged replace, was sent (ns).

venue_ack_ns property

venue_ack_ns: int

The venue's accept time from the ack (exch_ts, ns; whole ms on Binance); 0 before the ack or when the venue gives none.

Fees

Maker and taker rates of one instrument (a copy).

RiskHeadroom

What each [risk] limit still admits on one instrument (a copy). None where the limit is off.

VenueHealth

Feed lag and order round trip of one venue (a copy); durations in ns, 0 before the first sample.

Configuration and results

BacktestConfig

Everything one backtest needs: engine, instruments, strategy and parameters, simulated venue (fill model, latency, fees) and the synthetic market. Build one with from_toml() or single_instrument().

cancel_rate_per_order_s property writable

cancel_rate_per_order_s: float

Synthetic per-order cancel hazard per second.

duration_s property writable

duration_s: float

Synthetic horizon in seconds (data files run to their end).

engine_seed property writable

engine_seed: int

Engine RNG seed ([engine] rng_seed).

equity_bar_s property writable

equity_bar_s: float

Equity bar length in seconds.

fill_model property writable

fill_model: str

'matching' (orders rest in the simulated book) or 'l2_queue' (queue position on L2 data).

initial_capital property writable

initial_capital: float

Quote currency; only used for max_drawdown_pct.

journal_out property writable

journal_out: str

Record the session to this .fmj ('' = no journal).

latency_fixed_us property writable

latency_fixed_us: int

Fixed order and ack latency, microseconds.

latency_jitter_us property writable

latency_jitter_us: int

Mean lognormal order and ack latency jitter, microseconds.

latency_md_jitter_us property writable

latency_md_jitter_us: int

Mean market-data latency jitter, microseconds.

latency_md_us property writable

latency_md_us: int

Fixed market-data latency, microseconds.

limit_rate_per_s property writable

limit_rate_per_s: float

Synthetic limit order arrivals per second.

maker_fee_bps property writable

maker_fee_bps: float

Maker fee in bps (negative = rebate), 0.01 bps resolution. Setting it makes every instrument pay the same schedule; use set_instrument_fees() afterwards for per-instrument rates.

market_rate_per_s property writable

market_rate_per_s: float

Synthetic market order arrivals per second.

markout_horizons_s property writable

markout_horizons_s: list[float]

Post-fill markout horizons in seconds; [] turns markouts off.

max_param_age_ms property writable

max_param_age_ms: int

[strategy] max_param_age_ms: quoting is disabled before the first parameter update and while none was applied for this long (0 disables).

state_file property writable

state_file: str

[strategy] state_file: the strategy's restore() reads it at the start and its state() is written to it at the end ('' = none).

state_interval_s property writable

state_interval_s: int

[strategy] state_interval_s: how often the state is taken, seconds of engine time (default 300).

measure_wall_clock property writable

measure_wall_clock: bool

Measure wall-clock tick-to-order per engine step.

mid_step_rate_per_s property writable

mid_step_rate_per_s: float

Synthetic latent mid +-1 tick steps per second.

output_dir property writable

output_dir: str

Where the apps write results; run_backtest() never writes (see BacktestResult.write_all).

p_drop property writable

p_drop: float

Probability an outbound order message is lost.

params property writable

params: dict

Strategy parameters as {name: str} (a copy; assign a dict or use set_param()).

path property writable

path: str

Data file for source journal/csv.

queue_conservatism property writable

queue_conservatism: float

L2 queue model: 0 = cancels ahead always help, 1 = never.

seed property writable

seed: int

Seed of the synthetic market and the latency model.

source property writable

source: str

'synthetic' | 'journal' | 'csv' | '' (used when run_backtest(data=None)).

start_mid property writable

start_mid: str

Synthetic market initial mid (exact decimal string; float accepted).

start_ns property writable

start_ns: int

Synthetic market start time (ns since the epoch).

strategy property writable

strategy: str

Registered strategy name.

supports_replace property writable

supports_replace: bool

Venue and engine use native replace instead of cancel-then-new.

taker_fee_bps property writable

taker_fee_bps: float

Taker fee in bps, 0.01 bps resolution. Setting it clears per-instrument schedules.

warnings property

warnings: list[str]

Unknown keys and sections of the TOML file, each with its line.

from_toml staticmethod

from_toml(path: Any) -> BacktestConfig

Load a FastMM TOML config (sections [engine] [[instruments]] [strategy] [risk] [venues..fees] [sim] [backtest]). Raises ConfigError, also for an unknown key or section; other warnings are listed in warnings.

from_toml_string staticmethod

from_toml_string(text: str) -> BacktestConfig

Parse a TOML document held in a string. Raises ConfigError, also for an unknown key or section.

single_instrument staticmethod

single_instrument(
    symbol: str, tick: str, lot: str
) -> BacktestConfig

One instrument on venue 0 with exact decimal tick/lot (e.g. '0.01', '0.00001'); synthetic market, matching fill model, no strategy selected.

clear_params

clear_params() -> None

Remove every strategy parameter (use before switching strategy).

instrument_fees

instrument_fees(
    instrument: SupportsInt | SupportsIndex,
) -> tuple[float, float]

(maker_bps, taker_bps) charged to one instrument.

set_instrument_fees

set_instrument_fees(
    instrument: SupportsInt | SupportsIndex,
    maker_bps: SupportsFloat | SupportsIndex,
    taker_bps: SupportsFloat | SupportsIndex,
) -> None

Charge one instrument its own maker / taker schedule (negative = rebate).

set_param

set_param(key: str, value: Any) -> None

Set one strategy parameter (str / int / float / bool).

BacktestResult

Outcome of one backtest. fills / equity / orders are dicts of read-only numpy views over the C++ vectors (prices, quantities, fees and PnL are raw int64 with a 1e-8 scale); to_pandas() converts them.

end_ts property

end_ts: int

Last event time, ns.

equity property

equity: dict

Equity bar columns (zero-copy numpy views).

equity_by_instrument property

equity_by_instrument: list

Per instrument id, its equity bar columns: pnl (realized + unrealized - fees, its settlement currency), position, mid, quoted.

fills property

fills: dict

Fill columns (zero-copy numpy views).

md_events property

md_events: int

Market-data messages delivered to the engine.

md_late property

md_late: int

Recorded events that came after a later-stamped one (beyond reorder_window_ms).

md_reordered property

md_reordered: int

Recorded events the simulated venue took ahead of one recorded before them.

orders property

orders: dict

Order columns (zero-copy numpy views).

outbound_sha256 property

outbound_sha256: str

SHA-256 of every message the engine sent (determinism fingerprint).

params property

params: dict

Strategy parameters of the run as {name: str}.

slow_methods property

slow_methods: dict

Wall time of each slow method of a Python strategy: {name: {'calls', 'p50_ms', 'p99_ms', 'max_ms'}}; empty for other strategies.

start_ts property

start_ts: int

First event time, ns.

engine_stats

engine_stats() -> dict

The engine's own counters (PnL converted to float).

markouts

markouts() -> list

Post-fill markouts, one entry per horizon: {'horizon_ns', 'label', 'excluded_fills', 'total', 'buy', 'sell', 'maker', 'taker', 'instrument'}. Each bucket has markout / capture / adverse_selection in quote currency and in bps of notional, over the fills that had a mid at the horizon.

stats

stats() -> dict

Every summary metric: PnL / volume / inventory in quote or base units (float), counts and latency percentiles (int, ns); kill_reason and kill_at_s (seconds after the start) when the kill switch stopped the run, else None.

summary_json

summary_json() -> str

Metrics as a JSON document.

summary_table

summary_table() -> str

Human-readable metrics table.

to_pandas

to_pandas() -> Any

fills / equity / orders as pandas DataFrames with float prices and datetime64 timestamps (see fastmm.results.to_pandas).

transport_stats

transport_stats() -> dict

Simulated venue / transport counters.

write_all

write_all(dir: Any) -> None

Write equity.csv, fills.csv, orders.csv and summary.json into dir (created).

ReplayResult dataclass

ReplayResult(
    ok: bool,
    outbound_sha256: str,
    recorded_sha256: str,
    outbound_messages: int,
    recorded_messages: int,
    events: int,
    first_mismatch: int,
    expected_message: str,
    actual_message: str,
    what_if: bool,
    what_if_reasons: List[str] = list(),
)

Outcome of fastmm.replay(). ok: the replayed engine sent the recorded messages. what_if: the replay differs from the recording (what_if_reasons), so a mismatch is a result, not an error.

to_pandas

to_pandas(result: BacktestResult) -> Dict[str, DataFrame]

{"fills", "equity", "orders", "markouts"} DataFrames with float prices / quantities / PnL and datetime64[ns] timestamps.

sweep_frame

sweep_frame(
    points: Iterable[Tuple[Dict[str, Any], BacktestResult]],
    stats: Sequence[str] = SWEEP_STATS,
) -> DataFrame

One row per sweep point: the grid parameters, the chosen stats() columns and the outbound hash. Row order follows the sweep (grid order).

markout_frame

markout_frame(result: BacktestResult) -> DataFrame

One row per (horizon, bucket) of result.markouts(): markout and spread capture in quote currency and in bps of notional, plus the adverse selection between them.

load_csv

load_csv(
    path: Union[str, "os.PathLike[str]"],
) -> Dict[str, ndarray]

Read a ts_ns,type,inst,side,price,qty,seq CSV (the CsvSource format, e.g. from tools/gen_synthetic_data.py) into the column dict :func:fastmm.run_backtest accepts.

Prices and quantities are parsed exactly into raw int64 (1e-8), so the arrays replay bit-identically to running the CSV by path.

data_sources

data_sources() -> str

The registered market-data sources, their options and what each one carries.

convert_data

convert_data(
    data: str, out: str, config: BacktestConfig
) -> int

Decode a data source into an .fmj journal, the format a backtest replays fastest, and return the number of events written. The instruments come from config.

fetch

fetch(
    symbol: str,
    dates: Sequence[str],
    *,
    market: str = "um",
    kinds: Optional[Iterable[str]] = None,
    cache: Optional[Cache] = None,
    extract: bool = True,
    force: bool = False,
    quiet: bool = False,
) -> List[Path]

Download and unpack the daily dumps of symbol.

Returns the extracted CSV paths (the ZIPs when extract is false). Every file is checked against the archive's published SHA-256 before it is accepted and a partial download resumes. A day the archive does not have raises :class:DownloadError naming the URL.

fetch

fetch(
    exchange: str,
    symbol: str,
    dates: Sequence[str],
    *,
    kinds: Optional[Iterable[str]] = None,
    cache: Optional[Cache] = None,
    extract: bool = True,
    force: bool = False,
    quiet: bool = False,
    api_key: Optional[str] = None,
) -> List[Path]

Download and gunzip the datasets of symbol on exchange.

Returns the extracted CSV paths. api_key defaults to $TARDIS_API_KEY; without one only the first day of a month is served and anything else raises :class:DownloadError with the HTTP 401.

Cache

Cache(
    root: Optional[Union[str, "os.PathLike[str]"]] = None,
)

Files under a root directory, addressed by the archive's own key.

fetch_text

fetch_text(url: str) -> Optional[str]

GET a small file as text; None on 404.

download

download(
    url: str,
    key: str,
    *,
    sha256: Optional[str] = None,
    force: bool = False,
    headers: Optional[Dict[str, str]] = None,
    progress: Optional[
        Callable[[str, int, int], None]
    ] = None,
) -> Path

Download url to key, resuming a partial file and verifying sha256.

A file already in the cache is returned untouched unless force. The download goes to <key>.part and is renamed only after the checksum matches, so an interrupted run never leaves a truncated file that looks complete. A server that refuses the range request starts the file again.

extract

extract(key: str, *, force: bool = False) -> Path

Unpack a cached .zip (one member) or .gz next to it, and return the file.

inspect_journal

inspect_journal(path: Any) -> dict

Header and message counts of an .fmj journal.

write_report

write_report(
    source: Any,
    out: Optional[PathLike] = None,
    config: Optional[PathLike] = None,
) -> str

Write the report for a run directory, a journal or a result, and return the path.

source is a path to a backtest output directory or a .fmj journal, or a :class:fastmm.BacktestResult. out defaults to report.html beside the input.

Trading records

open_store

open_store(path: Union[str, 'os.PathLike[str]']) -> Store

Opens a FastMM store read-only.

Parameters:

Name Type Description Default
path Union[str, 'os.PathLike[str]']

the store file, [storage] path (default runs/<engine name>.db).

required

Returns:

Type Description
Store

A Store whose methods return pandas DataFrames. Use it as a context manager to close

Store

the connection.

Raises:

Type Description
ValueError

the file is not a FastMM store, or was written by a newer FastMM.

OperationalError

the file does not exist or cannot be read.

Store

Store(path: Union[str, 'os.PathLike[str]'])

A read-only connection to a FastMM SQLite store.

Every method returns a pandas DataFrame with raw fixed-point columns decoded to floats and nanosecond timestamps decoded to UTC datetimes, so fee_raw reads back as fee.

close

close() -> None

Closes the connection. The store is read-only, so nothing is flushed.

query

query(
    sql: str, params: Iterable[Any] = ()
) -> "pd.DataFrame"

Runs sql and returns the rows, decoding _raw and _ns columns as above.

sessions

sessions(
    engine: Optional[str] = None,
    since: Optional[str] = None,
    until: Optional[str] = None,
) -> "pd.DataFrame"

One row per session: when it ran, what it made and how it ended.

fills

fills(
    session: Optional[int] = None,
    instrument: Optional[str] = None,
    since: Optional[str] = None,
    until: Optional[str] = None,
) -> "pd.DataFrame"

One row per execution, in time order.

orders

orders(
    session: Optional[int] = None,
    instrument: Optional[str] = None,
    open_only: bool = False,
) -> "pd.DataFrame"

One row per order, in the last state the session recorded for it.

positions

positions(session: Optional[int] = None) -> 'pd.DataFrame'

The last position snapshot of each session and instrument.

pnl

pnl(
    since: Optional[str] = None,
    until: Optional[str] = None,
    instrument: Optional[str] = None,
    by: str = "instrument",
) -> "pd.DataFrame"

Realised (with the funding in it, schema 4), fees and net by UTC day.

by is "instrument" (a row per day and symbol) or "currency" (a row per day and settlement currency). Unrealised PnL is not summed across days: it is a mark, not a flow.

funding

funding(
    session: Optional[int] = None,
    instrument: Optional[str] = None,
    since: Optional[str] = None,
    until: Optional[str] = None,
) -> "pd.DataFrame"

Every perpetual funding payment booked: amount in asset, negative paid.

Funding is realized PnL; pnl() and positions() carry it inside realized and say how much of it in funding. A store from before schema 4 has none.

duplicates

duplicates(
    engine: Optional[str] = None,
    instrument: Optional[str] = None,
) -> "pd.DataFrame"

One row per venue execution or funding payment that more than one session stored.

A restart booked it a second time: the positions, fees and PnL of the sessions holding the copies count it twice. An execution is the venue's id of it on one venue, symbol and side. Empty in a sound store. The columns are those of fastmm-pnl duplicates: kind (fill or funding), engine, venue, symbol, id, copies, side, qty (the amount of a funding payment), ts (of the first copy) and sessions (oldest first, space separated).

kill_events

kill_events(
    session: Optional[int] = None,
) -> "pd.DataFrame"

Every kill switch trip, global or per venue, with the reason and the PnL at the time.

journals

journals(session: Optional[int] = None) -> 'pd.DataFrame'

The journal parts each session wrote, in order.

Errors

StrategyError

StrategyError(
    message: str,
    result: "Optional[BacktestResult]" = None,
    hook: str = "",
    now_ns: int = 0,
    events: int = 0,
    status: int = 0,
    fail_code: int = 0,
    kill_reason: str = "",
    slow_failure: str = "",
)

Bases: RuntimeError

A strategy hook raised. The original exception (with its traceback) is __cause__; result is the partial BacktestResult of the run, which stopped after the failing event.

OrderRejected

Bases: Exception

ctx.send() or ctx.replace() was rejected; .reason is the RejectReason name.

StaleViewError

Bases: RuntimeError

A view (book, fill, trade, ...) was read outside the hook that received it.

ConfigError

Bases: ValueError

Logging and build information

enable_logging

enable_logging(
    level: str = "warn", path: Any = None
) -> None

Start writing C++ log records at level or above to path (appending) or to stderr. Calling it again restarts the logger with the new settings.

disable_logging

disable_logging() -> None

Flush pending log records, stop the logger thread and close the log file.

strategies

strategies() -> dict

Registered strategies and their parameter schemas: {name: [{'name', 'type', 'default', 'min', 'max', 'doc'}, ...]}.

build_info

build_info() -> str

Compiler, flags and build type of the native module.