Skip to main content

Using TradeFlow as a library

TradeFlow is primarily a command-line tool, but the layer the CLI and the MCP server both call is a clean, JSON-returning boundary — and it is usable from your own code.

uv add tradeflow-engine # or: pip install tradeflow-engine
from datetime import datetime

from tradeflow.services.analysis import run_verdict
from tradeflow.services.data import build_data_client

result = run_verdict(
build_data_client(),
"volume_spike",
["NVDA", "AAPL", "META"],
datetime(2024, 1, 1),
datetime(2024, 12, 31),
)

print(result["verdict"]["summary"])
for name, check in result["verdict"]["checks"].items():
print(name, check["value"], "vs", check["threshold"], "->", check["passed"])

What is supported, and what is not

Supported: tradeflow.services.*. Every function there takes a data-only client and returns a plain, JSON-serializable dict. This is the same code path the CLI renders and the MCP server exposes, so anything you can do from the terminal you can do from Python, and you get identical numbers by construction rather than by agreement.

ModuleWhat it gives you
services.analysisrun_verdict, run_backtest, run_optimization, run_walk_forward, compute_alphas, construct_portfolio, compute_information, and the rest of the research pipeline
services.databuild_data_client (data-only, never a broker), resolve_universe
services.registryAvailable strategies and scanners, and their tunable ranges
services.glossaryCanonical definitions and pitfalls for every metric reported
services.configsThe promoted-config store
services.setupCredential inspection and validation
store.trialsThe campaign's trial history (TrialStore)
analytics.htmlreportrender_html — a result dict to a self-contained report

Not supported: everything else. engine, execution, strategies, optimization, portfolio, risk, alphas, data, and the rest are internal. They are importable — nothing stops you — but they change without notice, and a refactor that moves a class between them is not treated as a breaking change.

tradeflow.cli is not an API. It is a transport. If you find yourself importing from it, the thing you want should probably move into services/ first; that is the same rule the MCP server follows.

Things worth knowing before you embed it

State is shared, and that is the point. Anything that runs a trial writes to the research journal, and the campaign's trial count is what the Deflated Sharpe deflates against. Embedding TradeFlow in a loop that runs thousands of backtests will — correctly — make every subsequent result harder to clear. Set TRADEFLOW_HOME to keep a project's campaign separate from your own, or pass journal=False / no_journal where a function offers it if a run genuinely should not count.

Results are memoized. An identical prior run is served from the trial store rather than recomputed, labeled memoized with the original timestamp. That is usually what you want; pass force=True when it is not.

Nothing here can trade. build_data_client constructs only a market-data client, never a broker — the same structural guarantee that makes the MCP server safe. Live trading lives behind tradeflow.engine.live and is deliberately not part of the supported surface.

Optional capabilities are extras. tradeflow-engine[store] for the bar cache, [viz] for charts, [optimize] for Bayesian search, [portfolio] for the constraint solver. A missing extra raises an actionable message at the point of use, not at import.

Stability

The project's status is Experimental: interfaces and gate thresholds may still change, and there are no production users. Practically that means services/ is where changes are made carefully and announced, and everything else may move at any time.

A formal commitment — curated exports, semantic versioning, internal refactors treated as breaking — is on the roadmap but deliberately not made yet. It constrains every future change, and that constraint should be paid for by real dependents rather than in anticipation of them.