nlalpha-backtest
Nautilus sets proportional fees per instrument: fill maker_fee and/or taker_fee in contract.instruments[<symbol>]. Both default to 0.0 and must satisfy 0 <= fee < 1; 0.001 means 0.1%. Native bar execution charges taker_fee, and the result reports total commission. This is not the same configuration contract as Vector's execution.fee_rate. Unknown-key validation messages list the allowed fields for the current configuration scope and point out unsupported keys first.
nlalpha-backtest is an independent MCP server for strategy validation and backtesting; the package version is 0.8.0. The core has no host sandbox, profile, object-storage, or platform-address knowledge.
Runtime architecture
MCP tools -> BacktestJobService -> BacktestRunner -> ExecutionBackend
└-> LocalExecutor (default)
ExecutionBackend owns four semantic operations: execute, observe, cancel, and shutdown. BacktestRunner never branches on a concrete backend type.
In addition to the event-driven run path, the Python API exposes run_batch for providers that declare support for a versioned batch input schema. The batch dispatcher resolves an engine against both runtime_mode and input_schema; an explicit engine that does not support the request fails before provider execution. The vector MCP batch path generates strategy signals and simulates them inside one asynchronous backend worker job using the raw Parquet data_ref.
The default LocalExecutor runs every accepted operation in a bounded, terminable child process. Once spawned, the process, pipes, and executor handle remain under cleanup ownership through progress callbacks and execution; callback failures, execution errors, timeout, and cancellation all clean up the child process before releasing its concurrency slot. Timeout and cancellation terminate the process instead of merely abandoning an asyncio wait. SQLite is persistent by default at ./nlalpha-backtest-jobs.db; after restart, non-terminal local jobs become explicit failed/interrupted records because their worker processes no longer exist.
Supported execution combinations
The job store and execution backend form one support decision. Use SQLite with LocalExecutor for a single process and local persistence. Use Redis with a durable remote sandbox adapter for multi-replica execution, where the backend can authoritatively observe and cancel remote work. SQLite with Sandbox and Redis with LocalExecutor are unsupported combinations: local process ownership cannot establish that another replica's worker has stopped, so shared job records alone do not provide safe high availability.
Each record separates user_id from owner_id: the former scopes user reads and cancellation, while the latter fences writes to the execution lease. Admission creates a queued record; backend acceptance is recorded separately, and running begins when execution is observed. cancelling records cancellation intent; completed, failed, timeout, and cancelled are terminal outcomes confirmed through backend observation. Recovery observes and reconciles existing work; expired or lost remote work is not automatically replayed.
The data handoff uses core DataArtifactStore on both sides. nlalpha-data publishes immutable bytes and returns a data_ref; this service resolves the reference within the request's environment and owner scope, then validates its declared media type and schema before decoding it. A reference is not authorization by itself: the host identity mapping supplies the scope. Event and batch tools preserve the public data_ref/data_refs request shape.
The public nlalpha_backtest.execution.run_strategy_code() API is a synchronous, event-driven compile-and-run entry point that executes in the caller's current process and returns only a clean successful result. A caller that invokes it directly must provide its own process isolation, timeout, and cancellation handling. BacktestRunner continues to delegate the hosted workflow to an ExecutionBackend; its default LocalExecutor provides the bounded child-process isolation and real timeout/cancellation behavior described above.
Hosting outside this package is supplied by a separate adapter package. Installing this package alone does not install aiohttp, aiobotocore, or any host integration code.
Modules
| Module | Responsibility |
|---|---|
server |
FastMCP tools, provider job lifecycle, result projection, application factory |
execution.backend |
BacktestRequest, backend protocol, canonical status/cancel/error types |
execution.local_executor |
Bounded process execution with real cancel and timeout |
execution.runner |
Domain validation and backend delegation |
jobs |
SQLite/Redis status and result persistence |
artifacts |
Scoped immutable data reference resolution via core DataArtifactStore |
engine |
Entry-point based engine discovery |
Batch MCP schema contract
Both run_batch_backtest and validate_batch_strategy publish the shared VectorBatchStrategySpec as strategy_spec. Its JSON Schema requires family as a string with enum vector; additionalProperties remains allowed for the evolving strategy contract, and the server does not synthesize a missing family. The same Pydantic field definition validates the request at runtime before provider/job/artifact work. Callers must therefore submit the complete strategy spec, including "family":"vector"; prose in a tool description cannot replace a required schema property.
The structured schema and runtime validation are covered by the real FastMCP publication tests and batch tests. The package is released as 0.8.0.
MCP tools
| Tool | Result |
|---|---|
run_backtest |
Accepts an event strategy with one data_ref or a multi-symbol data_refs map, returns job_id |
get_backtest_status |
Returns provider job status; a hosted backend may recover truth after restart |
get_backtest_result |
Reads hot cache, persistent result, then provider status |
cancel_backtest |
Cancels queued work or propagates cancellation to the accepted backend operation |
validate_strategy |
Runs a mock-data validation |
run_batch_backtest |
Submits a vector batch backtest using raw Parquet data_ref for one symbol or data_refs for multiple symbols |
validate_batch_strategy |
Validates a vector strategy through the backend worker mock-data path |
list_engines |
Lists discovered engines |
confess |
Returns an engine's capabilities and limitations |
The existing run_backtest supports event-driven single- and multi-symbol references. Vector batch tools require family="vector", execution.engine="vector", and execution.runtime_mode="batch_signals"; the runner resolves engine compatibility against both runtime mode and input schema. A single-symbol request supplies one data_ref; a multi-symbol request supplies data_refs, a map whose keys exactly match the ordered unique universe.symbols and whose values are distinct caller-owned references. The server loads each reference through DataArtifactStore, combines Arrow rows without rewriting symbol identities, and lets the worker validate and normalize the canonical panel. Batch requests sent through event-only tools are rejected before job submission, artifact loading, or mock-data validation. Existing list_engines fields remain available, with runtime_modes and input_schemas describing provider compatibility. Batch tools are callable only after their server image and tool metadata are published and the live catalog exposes them.
Engine selection
For hosted strategy requests, strategy_spec.execution.engine takes precedence over the top-level strategy_spec.engine. On the Python run and run_batch APIs, an explicit engine= argument takes precedence over the execution_config.engine host hint. The dispatcher then resolves the provider against both runtime mode and batch input schema. An explicit incompatible engine fails before execution and is never silently replaced.
Validation operation identity
BacktestRunner.run_validation keeps operation identity independent from any host platform. The caller may provide an explicit operation_id; it must be a string containing non-whitespace characters, is used as supplied, and is never silently replaced. A non-string value or a blank/whitespace-only string fails immediately.
When operation_id is omitted, the runner content-addresses the complete validation request. It builds this semantic payload:
{
"strategy_spec": "<complete strategy specification>",
"data": "<complete validation data>",
"validation": true
}
Before hashing, the payload is strictly normalized by execution/runner.py: dataclasses become dictionaries, dates and times use ISO-8601 strings, tuples become arrays, object keys must be strings, JSON keys are sorted, separators are compact, Unicode is kept as UTF-8, and non-finite numbers are rejected. The UTF-8 SHA-256 digest becomes validation:<hex-digest>.
Therefore, identical complete validation requests receive the same default operation identity, while any semantic change to the strategy specification or validation data receives a different identity. This rule is a property of the independent backtest package; it does not import or encode platform concepts. It prevents a mutable strategy_spec.id from being reused as the sole identity of semantically different validation requests while preserving stable retry behavior for the same request.
BacktestRunner.run has a different contract for a formal backtest: the caller must provide a non-blank operation_id. Missing, non-string, or blank/whitespace-only values fail immediately; the runner no longer falls back to strategy_spec.id and does not content-hash the potentially large market-data request by default. The MCP BacktestJobService creates the provider job_id and the hosted execution path passes that value as operation_id, so the existing hosted flow is unchanged.
ExecutionOutcome carries the domain result separately from backend evidence references. Successful and failed operations can both preserve those fields, and a host may archive them without teaching this package about its evidence service.
server.create_app() is the hosting boundary. It accepts the execution backend and DNS-rebinding host allow-list before constructing the SSE application; importing the module does not construct an ASGI app. The standalone command uses the local backend and localhost allow-list, while hosting adapters must pass their finalized runtime settings explicitly.
Result integrity and strategy contract
The shared core BacktestResult accepts only status == "succeeded" or status == "failed"; unknown values, including "completed", are rejected at construction. Its is_successful property is true only when the status is succeeded and the errors list is empty. BacktestRunner requires that clean-success predicate for backtest and validation results and raises a domain failure otherwise.
The event validate_strategy and vector validate_batch_strategy MCP tools return failures as exactly {"status": "failed", "error": <canonical error object>}; there is no top-level details or duplicate error metadata outside the canonical object. The canonical error object orders its fields by value priority: type (one of invalid_request, strategy_error, data_error, execution_error, infrastructure_error, or internal_error), message (≤2 KiB), optional stage, retryable, optional suggestion, optional traceback_tail (last 15 user/strategy frames, ≤4 KiB), and optional details (≤8 KiB, with lists trimmed to 10 entries × 512 B plus *_count/errors_total markers). The independent LocalExecutor carries the child worker's canonical error dict on the ExecutionFailed.canonical_error attribute; the former duplicated worker error record is gone. Success responses are bounded to 256 KiB with head-priority field ordering and summarized sections, preserving diagnostic context without changing success criteria.
On validation failure, confess guides the Agent using the available structured diagnostics and the fields needed for its current assessment; complete logs are not required by default. Missing or truncated content is unknown. If more evidence is needed, obtain it only through diagnostic or artifact-reading capabilities explicitly provided by the current tool contract; a reference alone does not guarantee read access. If required evidence is unavailable, report the gap and limit the conclusion accordingly.
An engine result and a host job are different objects with different status vocabularies. The job store can mark a clean result's job completed; the progress callback can also emit the text completed. Neither is a BacktestResult.status. Hosted status mapping belongs to the adapter that owns the host.
The core backtest contracts preserve the run context, inputs, results, and public strategy contracts. Execution timing, fees, account validation, and assumptions are provided by the actual engine; the host preserves them as supplied.
Configuration
| Variable | Default | Meaning |
|---|---|---|
JOB_STORE_DSN |
sqlite:///./nlalpha-backtest-jobs.db |
Job state and results; SQLite supports local single-process execution, redis:// selects Redis |
JOB_STORE_PREFIX |
nlalpha:jobs:v1 |
Redis key prefix |
NLALPHA_MAX_CONCURRENT_BACKTESTS |
min(4, CPU count) |
Local execution concurrency limit |
NLALPHA_ARTIFACT_BACKEND |
redis |
Artifact backend: redis or s3 |
NLALPHA_ARTIFACT_REDIS_URL |
redis://127.0.0.1:6379/0 |
Redis artifact connection |
NLALPHA_ARTIFACT_TTL_SECONDS |
86400 |
Per-artifact logical lifetime |
NLALPHA_ARTIFACT_MAX_BYTES |
unset | Optional artifact size limit; there is no default 10 MiB cap |
NLALPHA_ARTIFACT_S3_BUCKET |
unset | Required bucket when S3 is selected |
NLALPHA_ARTIFACT_S3_ENDPOINT |
unset | Optional S3-compatible endpoint, such as MinIO |
NLALPHA_ARTIFACT_S3_ACCESS_KEY / NLALPHA_ARTIFACT_S3_SECRET_KEY |
unset | S3 credentials when required by the endpoint |
NLALPHA_ARTIFACT_S3_REGION |
unset | Optional S3 region |
NLALPHA_ARTIFACT_S3_RETENTION_DAYS |
1 |
Required object lifecycle retention in days |
NLALPHA_IDENTITY_MODE |
single-user |
single-user rejects identity headers; trusted-host reads the configured host identity header |
NLALPHA_IDENTITY_USER_HEADER |
X-NLAlpha-User-ID |
Trusted host identity header |
NLALPHA_ENV |
local |
Environment component of artifact scope |
NLALPHA_ALLOWED_HOSTS |
localhost/loopback | DNS-rebinding host allow-list |
BacktestService and create_app() default to max_active=100, a 30-second owner lease, and a 5-second heartbeat interval. These are application-factory parameters, not environment variables.
The app factory owns MCP, runner, job service, stores, and their connections through its lifespan. /health reports process liveness. /ready checks application readiness, artifact backend health (PING for Redis or HeadBucket for S3), and job-store availability; a failed dependency probe returns 503.
Artifact storage is independent of job storage: selecting S3 for artifacts does not change the job store, and Redis artifacts do not imply Redis job state. MinIO is an optional S3-compatible backend. See the nlalpha-data package for publisher metadata and artifact lifecycle details.
Resolved artifact payloads are read into the service process. Set NLALPHA_ARTIFACT_MAX_BYTES to fit the deployment's per-artifact memory and storage budget; when unset, there is no package-level payload cap and the selected backend's deployment budget applies. max_active bounds concurrent jobs at the application service layer.
Installation
pip install nlalpha-backtest
nlalpha-backtest
For Redis:
pip install "nlalpha-backtest[redis]"
JOB_STORE_DSN=redis://localhost:6379/1 nlalpha-backtest
The container image stores SQLite state under /data; mount that volume for standalone persistence.
The member's pyproject.toml is a standalone PEP 517 package definition. To build its wheel from an independent checkout or copied package source, run this from the package directory:
uv build --no-config --no-sources --default-index https://pypi.org/simple/
Multi-symbol event execution
The standard run_backtest request accepts either one data_ref for a single-symbol strategy or a data_refs mapping for a configured multi-symbol universe. Mapping keys must exactly match the ordered, unique configured symbols, values must be distinct references visible to the authenticated caller, and each source table's symbol values must match its key. The server preserves symbol identity and does not let request data overwrite caller identity. Strategy validation uses the configured symbols and interval through the same mock-provider path used by the hosted worker.
Event execution defaults to Nautilus. It requires a positive initial balance and explicit account currency, one venue and valuation currency, NETTING, and explicit per-symbol instrument definitions. Native OHLCV bar execution uses execution.mode="native_bar" and a positive configured execution.bar_interval matching the data interval. Per-symbol timestamps must be unique and strictly increasing. on_bar accepts sparse symbol timestamps; on_bars requires a complete shared timestamp grid. Results expose actual per-symbol positions, free cash/equity, fees, fills and native order denials. trade_count counts fills; total return and maximum drawdown are ratios.
Event-order results
Strategies submit market, limit, stop, and stop-limit orders with day, gtc, ioc, or fok time-in-force. A local queued state does not claim native acceptance or execution. Cancellation is an intent until the engine confirms it. Check pending orders before submitting duplicates, use remaining_volume after partial fills, and respond to native denial, rejection, and cancel feedback. A helper price is only a reference, not the execution price.
Full Nautilus results expose diagnostics.final_orders and diagnostics.order_events; the latter preserves native event order and nanosecond timestamps. Each projected diagnostic array is capped at 100 rows with count/truncated metadata; the full result remains stored. Callbacks receive native notifications only while the strategy is running; initial events in on_init / on_start may not be delivered as callbacks, so read snapshots to inspect engine state. Commands submitted or canceled in on_stop fail the job. These orders and fills belong to historical simulation and are not sent to a broker or exchange.
Recovery result evidence
When a managed operation is observed after recovery, a successful result retains grouped metrics, equity curve, trade details, assumptions, diagnostics, reproducibility, cost breakdown, and recorded request metadata such as runtime mode, engine ID, input metadata, execution defaults, and strategy assumptions/warnings. Backend observation evidence is preserved as well. Failed outcomes retain available request metadata and backend evidence; an invalid successful result becomes a failed job with result-integrity diagnostics.
If the recovered result's assumptions.runtime_mode conflicts with the runtime mode recorded for the accepted request, the job fails while retaining recorded request metadata and backend evidence in its result details. This check validates runtime mode; it does not assert that every provider's engine_id matches the recorded request. A failed recovery observation or missing operation is surfaced as failure, never wrapped as a successful result.
Annualized basic statistics
The result keeps metrics.performance.annual_return, metrics.risk.annual_volatility, and metrics.risk.sharpe_ratio; each may be null. Annual return and volatility are ratios; Sharpe is dimensionless. Configure execution.annualization as a positive integer number of equity observation periods per year. If it is absent, all three are unavailable with diagnostics.statistics.unavailable reason annualization_not_provided; the engine never guesses 252 from the bar interval. execution.risk_free_rate is a finite annual rate greater than -1; its default is 0 with source engine_default. When annualization is configured, the per-observation rate is expm1(log1p(rate) / annualization).
Returns use net account equity: the initial-equity baseline to the first completed bar-close group is the first observation period, followed by each completed bar-close group's simple return. Fees are already reflected in net equity and are not subtracted again. Uneven observation spacing is disclosed, not filled or resampled; annualization expresses an explicit observations-per-year assumption, not actual-time CAGR. diagnostics.statistics records the count and source of settings, variance_ddof=1, return and annualization bases, period risk-free rate, unavailable reasons, initial/first/last nanosecond timestamps, observation count, minimum/maximum spacing, irregularity, and fee treatment. No returns, one return, zero variance, non-positive equity, and numerical overflow have explicit unavailable behavior; null is not zero.
Engine reports and retrieval
BacktestResult.reports preserves engine-specific tables in a common envelope. Result retrieval exposes the report catalog; get_backtest_report(job_id, report_name, offset=0, limit=50, columns=None, user_id=None) reads selected pages with a limit from 1 to 200. Ownership is checked before cache access. Original full results remain in existing storage and Sandbox artifacts (32 MiB limit); the response is not silently clipped. not_recorded does not cause a full-mode rerun. The callable contract must be confirmed from the current runtime catalog and capability description; local documentation does not prove that an operation is published or available.
Drawdown episodes
The shared drawdown summary is metrics.risk.drawdowns with episode_count, recovered_episode_count, unrecovered_episode_count, and deepest, longest, current episode rows (each nullable when absent). metrics.risk.max_drawdown remains the scalar maximum and agrees with deepest.depth, or is zero when observations exist without an episode. reports.drawdowns uses the standard report envelope and columns: episode_id, status, peak_equity, peak_timestamp_ns, peak_observation_index, peak_is_initial, start_timestamp_ns, start_observation_index, trough_equity, trough_timestamp_ns, trough_observation_index, recovery_equity, recovery_timestamp_ns, recovery_observation_index, end_timestamp_ns, end_observation_index, depth, decline_duration_ns, recovery_duration_ns, duration_ns, observed_duration_ns, duration_is_lower_bound, decline_observations, recovery_observations, duration_observations. The Vector and Nautilus schema IDs and sources remain engine-specific.
Episodes use completed-bar-close net equity, with fees already reflected. The initial-capital reference is index 0 but has no economic timestamp; its actual reference timestamp, when known, is informational metadata only. The high-water mark resets on equal or greater equity (latest equal peak); a drawdown starts strictly below it, recovery is the first observation at or above the episode peak, and equal troughs keep the earliest trough. Depth is (peak_equity - trough_equity) / peak_equity, a proportion. deepest chooses greatest depth (ties: earlier episode); longest means longest observed drawdown, ordered by observed elapsed duration, then observation intervals, then earlier episode.
If the peak is initial capital, peak timestamp, decline duration and total duration are null because the economic start is unknown. Observed duration from the first underwater point is only a lower bound (duration_is_lower_bound=true). Recovery duration is measured from trough to recovery only for recovered episodes; unrecovered recovery fields remain null. Elapsed nanoseconds and observation interval counts are separate, exact quantities: the initial reference to the first actual observation counts as an interval, never as a fabricated 1 ns. An episode still open at sample end uses the last observed point as its observational end without assuming recovery. No observations yield unavailable/no_equity_observations; available with zero rows means observations existed and no episode occurred.
The report describes sampled closes, not intrabar peaks/troughs. It does not resample calendars, infer annualization, or synthesize timestamps. Nautilus always records its available observed path, while Vector summary keeps bounded O(1) tracker state and aggregate rows but marks episode history not_recorded/record_mode_summary with the known episode count; Vector full records all episodes. Continue using get_backtest_report for paged drawdown rows (maximum 200 rows per page); the existing 32 MiB artifact cap remains.
Monthly and yearly net returns
metrics.performance.period_returns summarizes observed monthly and yearly buckets; detailed rows are exposed by the existing reports.monthly_returns and reports.yearly_returns tables and retrieved with get_backtest_report. No tool ID or input schema is added. Each report has an engine-specific schema and the shared report envelope. Rows identify the Gregorian period in execution.analysis_timezone and its exact calendar bounds represented as UTC nanoseconds, reference/end equity and timestamps, endpoint net PnL/return, observation indexes/count, sample-boundary flags, cross-period attribution, skipped periods, non-positive-equity observation count, and per-field unavailable reasons.
Buckets use local Gregorian month/year boundaries from raw nanosecond timestamps in execution.analysis_timezone (default UTC); boundaries are represented as UTC nanoseconds. A bucket return is the net-equity change assigned to the period of its ending observation; the initial bucket uses starting capital as reference and has no economic reference timestamp. Later buckets use the preceding real observation as reference. Fees and slippage are already reflected in net equity. Non-positive reference equity makes return unavailable; valid negative/zero end equity is preserved. Missing calendar periods are omitted, never filled with zero or carried equity. Skipped-period and cross-period interval diagnostics disclose when a bucket endpoint return spans a gap. diagnostics.period_returns records the analysis timezone, pinned timezone database version and TZIF hash, and strict local-midnight boundary policy; ambiguous or nonexistent month boundaries fail explicitly.
Summary counts, positive fraction, best/worst/current include valid observed buckets, including partial first and last sample periods; denominators are valid observed buckets, not all elapsed or only complete calendar periods. yearly_returns are observed yearly endpoint changes, not annual_return or CAGR and are not compounded from monthly rows. A summary run retains these bounded aggregates but marks report rows not_recorded; full stores rows. Vector uses O(1) aggregation without per-bar date conversion within a bucket. Nautilus independently derives its period rows from its own raw-nanosecond net-equity path, excluding its artificial baseline from buckets while preserving native execution and reporting. Engine schemas and sources remain distinct.
Each frequency summary reports observed_period_count, valid_period_count, unavailable_period_count, positive/negative/flat counts and fraction, skipped-period and cross-period interval counts, skipped-gap bucket count, and best/worst/current rows. Row fields include period, local calendar start/end represented as UTC nanoseconds, reference and ending equity/timestamp/index, net_pnl, net_return, observation_count, first/last flags, closed_by_next_period, cross_period_interval_count, skipped_periods_before, non_positive_equity_observation_count, and unavailable reasons. yearly and monthly use the same conceptual fields under their own table schema.
Source time, analysis calendar, and display
strategy_spec.data separates source interpretation from analysis: canonical data_refs carry nlalpha.time_contract metadata declaring UTC open_time/close_time bounds, Bar precision, and per-asset source timezone/semantics/calendar/session labels. Use the returned time_contract from load_bars; do not infer timezone from timestamp appearance or session_date. Canonical data does not need a redundant source data.timezone; any supplied source declaration must agree with retained source metadata. For raw custom data, aware timestamp columns are normalized from their own offsets. Naive mixed-source inputs use data.time_contracts keyed by symbol; data.timezone and data.timestamp_semantics are global declarations only when valid for all affected symbols. A conflict fails. Normalize each source before concatenation. open_time/close_time are actual endpoints and may have session or half-day widths different from the nominal data.interval; one-column datetime needs explicit open or close semantics. trading_date is a session label and requires a calendar with exact session bounds.
execution.analysis_timezone is an independent supported IANA timezone (default UTC) for monthly/yearly statistics; it does not reinterpret input timestamps or alter execution. The tzdata version/hash and local-calendar boundary policy are included in diagnostics. UTC remains the canonical result/report storage representation. Optional display_timezone on result/report reads adds a display-only timestamp sidecar: selected metric/diagnostic timestamps or declared report timestamp columns are formatted with nine fractional digits. It does not change stored UTC values or convert elapsed durations; default responses remain canonical UTC.
Agent contract guidance
For Agent workflows, read the current backtest capability description and the selected engine’s full capability guidance before validation or execution. This README documents package behavior; the live descriptions own the current callable contract. A job ending or a mock check passing does not, by itself, establish a successful historical result.
Execution and result semantics
Choose a provider from the user's requested behavior and the currently discovered manifests, runtime modes, input schemas, and execution tools. Read its current confession and confirm that it declares the required timing, account feedback, and order lifecycle. If no available route preserves the request, report the gap; do not silently change semantics or run providers in parallel. There is no default dual run or cross-engine validation. not_recorded summary tables are not evidence of zero fills. A business backtest is successful only when the engine result is genuinely successful; Temporal job completed alone is insufficient. On failure, keep existing runtime/input/default metadata and refresh failure evidence instead of reading an earlier cached success.
read_work_result performs a single bounded read of one original ref (default 512 KiB, hard cap 1 MiB). When the serialized item is over budget, it returns {result_head, output_truncated: true, returned_bytes, total_bytes} with the original structured fields dropped; the read is deterministic and not pageable. get_backtest_report retains row pagination with offset and limit 1–200. These are separate APIs.
NLAlpha App documentation
The live runtime descriptions own the current callable contract.
Focused service verification
The event/batch catalog and per-asset source contract group passed 461 tests and 395 subtests, with 48 warnings, in 27.90 seconds. Nine S3IntegrationTest cases were explicitly deselected, so this run does not establish S3 integration coverage. Remote execution version binding and release startup order remain separate pending work.
Canonical backtest guidance
The service's generic execution, validation, job, report, and evidence guidance is packaged in src/nlalpha_backtest/knowledge/execution.md. Engine-owned configuration stays with each engine: Nautilus uses nlalpha-nautilus/src/nlalpha_nautilus/knowledge/backtest-config.md, and Vector uses nlalpha-engine-vector/src/nlalpha_engine_vector/knowledge/backtest-config.md. These Markdown resources are canonical content for a later consumer integration; they are not registered or loaded by this README change. A future backtest.confess(engine_id) consumer should combine this shared resource with the selected provider's engine-owned backtest-config.md. The shared execution guidance leaves engine choice to the user's request and the currently discovered manifests, modes, schemas, and tools.
Runtime descriptions and telemetry ownership
The service about description and execution-engine confess description are generated from the installed package version, current engine manifests, execution backends, and packaged guidance. They use nlalpha.description.v1; selected sections preserve the full description's description_revision. The backend capability query is separate from publication: Local and Sandbox capability records are implemented and consumed by runtime descriptions, while workflow release ordering that publishes matching server and profile metadata remains pending.
The process entry point owns telemetry SDK setup, flush, and shutdown. Importing or constructing the app factory does not create or shut down process-wide telemetry providers. runner.execute_task is task logic; the runner process wrapper owns telemetry lifecycle and emits one terminal record after shutdown. If the task and telemetry shutdown both fail, terminal data preserves both errors.
Artifact readers treat 64 KiB as the metadata protocol limit, not a payload cap. Redis performs one bounded range read. S3 validates fixed header and bounded metadata first, checks the configured payload budget, then reads at most 64 KiB per chunk through declared-size plus one byte and closes the body. Reads that exceed an explicit budget fail without deleting a still-live artifact. NLALPHA_ARTIFACT_MAX_BYTES unset means no package default 10 MiB cap; deployment owners still budget deserialization memory and concurrent requests.
License
nlalpha-backtest is one of the 10 community distributions under AGPL-3.0-or-later. Host-specific adapters and private enhancement packages are separate distributions; third-party dependencies retain their own licenses.
Container images
The community Redis image installs the non-editable service wheel closure on Python 3.12 slim. A separate S3 image explicitly selects the S3 artifact backend. Hosting server and runner images are built from the same source tree with the enhanced lock; the Vector native wheel is compiled in a builder stage and runtime images contain no Rust toolchain. Redis remains the default artifact backend and does not require MinIO.
Metadata
Release files for nlalpha-backtest 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| nlalpha_backtest-0.8.0.tar.gz | 154.3 kB | Without provenance | Details for nlalpha_backtest-0.8.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| nlalpha_backtest-0.8.0-py3-none-any.whl | Python 3 | none | any | Details for nlalpha_backtest-0.8.0-py3-none-any.whl |
Total release size: 253.8 kB
Release files / nlalpha_backtest-0.8.0.tar.gz
| Download URL | nlalpha_backtest-0.8.0.tar.gz |
|---|---|
| Size | 154.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
72ec2ee5d7cb85d652a9352326737f757d588e2e3bf455908a6709e1e41a2194
|
|
BLAKE2b-256 checksum How to use checksums |
ee56dab83800da2effce5d997f1d02397e41c2866387bb23cc9aad36f4d3a51d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Release files / nlalpha_backtest-0.8.0-py3-none-any.whl
| Download URL | nlalpha_backtest-0.8.0-py3-none-any.whl |
|---|---|
| Size | 99.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a82944ad2ec16c0a17b756d93a91fcb52280e509101f59f08a7220a13178096f
|
|
BLAKE2b-256 checksum How to use checksums |
64dc1c7e61e13f02a21c3a75123de3c32a23fbc8dee8c1bbbf4bce82e246da26
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.13
|
Security for nlalpha-backtest 0.8.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.