What this walkthrough covers
You will learn how to:- Define a strategy as streams of events instead of manual polling loops.
- Combine streams so derived values (“signals”) refresh whenever any input changes.
- Run on live venue data real-time feeds, with reconnect/retry so the pipeline survives network hiccups.
- Backtest on stored history: fetch, persist, replay; same codebase as live, no forked project.
- Hooks and composition—bundle connections, storage, and logging once; reuse them and keep strategy files small.
Create the project
Prerequisites
- Node.js and npm (the scaffold uses
npmto install dependencies). - Network access when you run live or when replay syncs historical files from remote archives.
Scaffold a new folder
Run the create tool with your project directory name (replace The CLI expects that folder name as its argument.
my-strategy if you like):Build the strategy
Start from the entryapp.ts. The first step is to define a strategy hook, your main entry, that pulls in other hooks and turns their streams into signals you can act on.
In this walkthrough the strategy listens to three Binance trade streams (three symbols), folds them into one combined stream so you always see the latest prices together, and from that joint view computes a spread to surface cross-market inefficiency.
app.ts registers modules, exposes the entry handle, and binds your strategy to the runtime:
app()creates the base runtime and its default dependency graph (logging, execution context, in-memory storage factory, and other services hooks expect)..use(sqlite())registers the SQLite-backed module so persistence-backed parts of the strategy resolve storage through the same graph..start(triangularArbitrageInefficiency)binds the strategy function to the runner: the runtime invokes it and drives the main stream it returns—your strategy entry tied to the running process.
Strategy execution: live & replay
Live — Command (project root):from / to in package.json if you need another window:
Technical walkthrough
The sections below step through the strategy implementation: live feed, replay storage, switching live versus replay, venue hooks, and how they connect back toapp.ts.
1. Real-time trades: watchTradeLive
watch-trade.live.ts uses useSocket to subscribe to Binance spot composite stream trades. Incoming messages are schema validated, retried on failure, and mapped to a normalized { timestamp, payload: { price, size } } shape.
d(...) builds decimal values suitable for precise math in the strategy layer.
2. Historical trades: watchTradeReplay
watch-trade.replay.ts uses useReplayStorage to manage historical data. The sync callback is invoked for the replay time range: it walks each day, downloads the Binance Vision spot daily trades ZIP for that symbol, parses CSV rows, and **storage.save**s normalized events.
Then watch() emits events in timestamp order through the same map as live for a consistent payload shape.
3. Execution mode: useReplay
watch-trade.ts delegates to useReplay(watchTradeReplay, watchTradeLive). That helper reads useExecutionMode(): in replay runs it uses the first function; otherwise it uses the second (live/paper paths both use the “real-time” implementation).
qf paper, qf live, and qf replay commands each inject the appropriate execution mode and replay options when they call run on your default export.
4. Venue composition: useBinance
use-binance.ts exposes a single object so strategy code does not import every low-level file:
useBinance() lets you reuse the same Binance implementation in other strategies without copying imports or stream setup.
Next steps
Overview
Return to the product overview and execution model summary.
Composition
Go deeper on hooks, tokens, and dependency wiring.