Building an Order-Flow Trading System for Crypto Markets
A crypto order-flow system reconstructs a live limit order book, measures how depth and aggressive trades change through time, and tests whether that pressure is large enough to beat spread, fees, slippage, latency, and risk buffers.
Article Status
This article does not provide entries, exits, position sizing, leverage settings, or production parameters. It describes a software workflow for research, replay, and paper evaluation before any exchange-facing use is considered.
Quick Read
- The theory: Short-horizon price movement is often better studied as the result of order-book events and aggressive trade flow than as a sequence of candles. Sources: [CKS2014] [KOLM2021]
- The caveat: Order-flow findings are venue-specific and regime-specific; they should be treated as hypotheses to test on the exact symbols, product type, fee tier, and latency profile used by the system. Sources: [SIL2019] [KOUTMOS2023]
- The path: Start with public data capture and deterministic replay. The worked example later uses Binance market data and order endpoints, but the workflow is designed to transfer to other crypto venues and products. Sources: [BINANCE_WS] [BINANCE_DEMO] [BINANCE_USER] [BINANCE_TRADING]
Theory And Hypothesis
- Order flow is event flow, not just visible liquidity: A limit order book changes when traders submit limit orders, cancel resting orders, or send marketable orders. The system needs the direction and size of those changes: whether demand is being added, supply is being removed, and whether aggressive traders are lifting offers or hitting bids. Sources: [CKS2014] [BINANCE_WS]
- The core hypothesis is short-horizon pressure: The working hypothesis is that synchronized buyer-initiated trades, positive order-flow imbalance near the touch, rising microprice, and weak ask refill can precede very short-horizon upward moves. The inverse pattern can be a reason to sell existing inventory or stay flat in a long-flat spot implementation, while derivatives or margin products need separate short-exposure logic. Sources: [CKS2014] [SIL2019] [KOLM2021]
- Static walls are weak evidence by themselves: Displayed bid and ask size can be cancelled or replenished. That is why the system measures event-driven OFI, trade-flow imbalance, refill, and depletion instead of treating a single depth snapshot as a trading signal. Sources: [CKS2014] [KOLM2021]
- Depth away from the best bid and ask can still matter: Multi-level order-flow imbalance treats changes across several book levels as a vector rather than reducing the book to only the best bid and ask. This matters when near-touch pressure is different from pressure two to five basis points away. Sources: [XGH2019]
- Costs decide whether a signal is tradable: Order-flow features can describe pressure, but an exchange order still has to clear spread, maker or taker fees, symbol filters, slippage, queue risk, latency, rejected orders, stale data, and product-specific position rules. The strategy should reject many otherwise plausible trades. Sources: [BINANCE_FILTERS] [BINANCE_TRADING] [BINANCE_FUTURES_TRADING] [CKS2014]
For liquid crypto pairs such as BTCUSDT, an order-flow system may have a measurable short-horizon edge when short-window trade-flow imbalance, event OFI, multi-level pressure, microprice edge, and ask depletion agree, provided the expected move is large enough after all execution costs and risk buffers. In spot-only accounts this naturally becomes long-flat; where futures or margin are available, short exposure must be modeled as a separate execution surface.
- Direction: Predict future mid-price or executable markout at 250 ms, 500 ms, 1 s, 3 s, 5 s, and 10 s horizons.
- Execution: Evaluate entry markout after spread, modeled fees, queue assumptions, adverse selection, and cancel/submit latency.
- Robustness: Walk forward by day and regime, compare public live capture against replay, and require stable results across no-trade thresholds.
- Safety: Block the system on sequence gaps, stale streams, disconnected private state, abnormal spread, reject bursts, or daily loss gates.
Order-flow impulse
The useful signal is a sequence: aggressive flow changes the book, refill confirms or rejects the pressure, and the later markout proves whether the pressure cleared costs.
Sources: [CKS2014] [KOLM2021] [SIL2019]- Resting book: Displayed depth exists on both sides of the mid.
- Aggressive flow arrives: Buyer-initiated trades lift offers near the touch.
- Refill test: Weak ask refill keeps near-touch supply thin.
- Markout window: The system measures whether executable price moves enough after costs.
Research Evidence
- Order-flow imbalance at the best bid and ask is a useful short-horizon explanatory variable for price changes. Implement event OFI from bid/ask transitions and test it against future executable markouts, not just last-price candles. Sources: [CKS2014]
- Order-flow-derived stationary features can outperform models trained directly on raw order-book states. Start with normalized, stationary features and simple baselines before adding deep sequence models. Sources: [KOLM2021]
- Order-flow changes deeper in the book can add information beyond the top level. Compute multi-level and basis-point-band features instead of only top-of-book imbalance. Sources: [XGH2019]
- In a cryptocurrency market study, trade-flow imbalance was reported as especially useful for explaining contemporaneous price changes. Classify public trades by direction so the system can tell whether buyer-initiated or seller-initiated volume is actually arriving. Sources: [SIL2019]
- Bitcoin order imbalance has been studied as an input to crash-risk nowcasting, but crypto evidence remains regime-dependent. Treat imbalance as a risk and regime signal too, not only as an entry signal. Sources: [KOUTMOS2023]
- The Binance worked example exposes the streams and REST snapshots needed to reconstruct a local order book and consume public trade data. Use diff depth with a REST snapshot, trade or aggregate trade streams, and bookTicker; do not build the strategy from candles only. Sources: [BINANCE_WS] [BINANCE_MARKET]
Worked Example: Binance Data Inputs
- Diff depth stream:
symbol@depth or symbol@depth@100msU, u, b, a, event time Primary source for book changes. Use the official snapshot-plus-buffer procedure to build a local book and rebuild on gaps. Sources: [BINANCE_WS] [SDK_EXAMPLES] [SDK_README] - REST order-book snapshot:
GET /api/v3/depth via MainClient.getOrderBook()lastUpdateId, bids, asks, limit up to 5000 Anchors the local book before applying buffered diff-depth events. Sources: [BINANCE_MARKET] [BINANCE_WS] [SDK_ENDPOINTS] - Trade or aggregate trade stream:
symbol@trade or symbol@aggTradeprice, quantity, trade time, buyer-maker flag Signs aggressive flow. In the Binance spot example, buyer-maker true means the buyer was the maker, so the taker side is sell. Sources: [BINANCE_WS] [SDK_TRADES_EXAMPLE] [SDK_README] - Book ticker:
symbol@bookTicker or GET /api/v3/ticker/bookTickerbest bid, best bid quantity, best ask, best ask quantity Low-latency cross-check for the top of book, spread, and stale local-book detection. Sources: [BINANCE_WS] [BINANCE_MARKET] [SDK_ENDPOINTS] [SDK_README] - Exchange and symbol filters:
GET /api/v3/exchangeInfo via MainClient.getExchangeInfo()PRICE_FILTER, LOT_SIZE, MIN_NOTIONAL or NOTIONAL, MARKET_LOT_SIZE Prevents invalid prices, quantities, and notionals before orders are submitted. Sources: [BINANCE_FILTERS] [SDK_ENDPOINTS] - Private order state:
User data stream plus REST reconciliationexecutionReport, balances, open orders, fills Confirms whether submitted orders were accepted, filled, partially filled, canceled, or rejected. Sources: [BINANCE_USER] [BINANCE_TRADING] [SDK_ENDPOINTS] [SDK_PRIVATE_WS_EXAMPLE]
Local book reconstruction
The strategy should not score signals until the local book has been anchored, aligned, and marked healthy. Many implementation errors show up here before the model matters.
Sources: [BINANCE_WS] [BINANCE_MARKET] [SDK_EXAMPLES]- Open streams: Depth and trade events start arriving before the snapshot is requested.
- Buffer deltas: Keep update IDs and receive timestamps while the snapshot is in flight.
- Fetch snapshot: Use the REST book as the anchor for the local book state.
- Align IDs: Drop covered events and apply only the remaining ordered deltas.
- Mark healthy: Rebuild on gaps, stale streams, checksum failures, or crossed states.
Implementation Takeaways
- Track book changes over time: A depth snapshot is useful context, but it is not a signal by itself. Track additions, cancellations, and consumption around the touch so the model sees pressure changing over time. Sources: [CKS2014] [KOLM2021]
- Confirm pressure with trades: Public trades classified by direction show whether aggressive buyers or sellers are actually crossing the spread. Displayed liquidity without matching trade flow is weaker evidence. Sources: [SIL2019] [BINANCE_WS]
- Look beyond the best quote: Pressure at the best bid or ask can disagree with depth a few ticks away. Keep level bands and basis-point distance bands so the model does not overreact to the top of book alone. Sources: [XGH2019]
- Score after costs: A signal that looks strong before costs may disappear after maker or taker fees, spread, queue risk, slippage, symbol filters, and latency are included. Sources: [BINANCE_FILTERS] [BINANCE_TRADING]
- Start with simple baselines: Test normalized features, rolling z-scores, logistic or ridge regression, and gradient-boosted trees before adding a deep sequence model. Sources: [KOLM2021] [XGH2019]
- Use risk checks as hard filters: Even a correct direction can lose if the book is stale, the spread is abnormal, sequence state is broken, or private exchange state is not reconciled. Sources: [BINANCE_WS] [BINANCE_USER] [BINANCE_TRADING]
Spot, Margin, And Derivatives Change Execution
- Spot is normally long-flat: In a spot-only account, long signals can buy base asset and negative signals can sell held inventory or stay flat. Do not treat a short signal as permission to borrow, short, or add leverage unless a margin or derivatives workflow is explicitly designed. Sources: [BINANCE_TRADING]
- Short exposure changes the problem: Where the venue and product support short exposure, such as futures or margin, add position mode, leverage, liquidation risk, funding or carry, reduce-only orders, and position reconciliation before using sell signals as short entries. Sources: [BINANCE_FUTURES_TRADING] [BINANCE_TRADING]
- Derivatives context is optional context: Funding, open interest, and liquidation streams can be useful cross-market context, but the initial implementation should be product-specific and should not mix spot and derivatives semantics without separate normalization. Sources: [BINANCE_FUTURES_WS] [BINANCE_WS]
- Execution semantics are venue-specific: Post-only, IOC, reduce-only, position side, min notional, tick and lot filters, demo environments, and test-order behavior vary by exchange and product. Treat them as adapters around the signal, not as the signal itself. Sources: [BINANCE_TRADING] [BINANCE_FUTURES_TRADING] [BINANCE_FILTERS]
Continuation vs absorption
The same burst of aggressive buying can mean continuation or no trade. The difference is whether liquidity refills and whether the markout confirms the pressure.
- Long continuation candidate: Buys lift asks to Ask refill weak to Markout higher
- Absorption or no-trade candidate: Buys hit asks to Ask refill strong to Mid stalls
- Long continuation candidate: Taker buy volume rises over 250 ms to 3 s windows. Ask liquidity near the touch is consumed or pulled. Ask refill is weak relative to consumed size. Microprice and mid-price move higher after the flow. Total expected edge clears spread, fees, slippage, and latency. Sources: [CKS2014] [SIL2019] [KOLM2021] [BINANCE_TRADING]
- Absorption or no-trade candidate: Aggressive buys hit the ask, but ask size refills quickly. The mid-price fails to advance after repeated lifted offers. Static book imbalance disagrees with trade-flow imbalance. Spread or volatility is outside the approved execution regime. Sequence state, private exchange state, or reconnect recovery is uncertain. Sources: [CKS2014] [SIL2019] [BINANCE_WS] [BINANCE_USER]
Minimum Feature Set
- Static OBI:
OBI_N = (sumBidSize_N - sumAskSize_N) / (sumBidSize_N + sumAskSize_N)Shows displayed pressure across top levels or basis-point bands. Sources: [CKS2014] [XGH2019] - Microprice edge:
microprice = (ask * bidSize + bid * askSize) / (bidSize + askSize)Estimates whether the touch is skewed above or below the mid. Sources: [CKS2014] - Event OFI:
OFI_window = sum(best-bid/best-ask signed size transitions)Tracks additions, removals, and queue pressure at the touch. Sources: [CKS2014] - Multi-level OFI:
MLOFI_window = [OFI_level_1, OFI_level_2, ..., OFI_level_k]Captures pressure beyond the best bid and ask. Sources: [XGH2019] [KOLM2021] - Trade-flow imbalance:
TFI = (buyVolume - sellVolume) / max(buyVolume + sellVolume, epsilon)Confirms whether aggressive traders are lifting asks or hitting bids. Sources: [SIL2019] [BINANCE_WS] - Depletion and refill:
refillRatio = addedNearTouch / max(consumedNearTouch, epsilon)Separates continuation from absorption after aggressive flow arrives. Sources: [CKS2014] [SIL2019] - Cost gate:
abs(alphaBps) >= spread + fees + slippage + latencyBuffer + safetyMarginPrevents trades where the microstructure edge is too small to pay execution costs. Sources: [BINANCE_FILTERS] [BINANCE_TRADING]
Manual Research Workflow
- 1. Pick one liquid symbol and one fee tier: Start with one liquid symbol, such as BTCUSDT in the Binance example. Record the product type, fee assumption, tick size, step size, notional rules, and maximum order size before studying signals. Sources: [BINANCE_FILTERS]
- 2. Watch the book and trades together: In the exchange UI or a custom dashboard, compare depth changes with prints. Mark moments when aggressive buys lift asks, ask liquidity disappears, or ask liquidity refills immediately. Sources: [BINANCE_WS] [SIL2019]
- 3. Write down counterfactual fills: For every candidate, record whether a LIMIT_MAKER order would have rested, whether an IOC order would have crossed, and what the spread and likely fee cost were. Sources: [BINANCE_TRADING] [BINANCE_FILTERS]
- 4. Check markouts instead of screenshots: Measure where the mid, bid, ask, and executable exit price moved after 250 ms, 1 s, 3 s, 5 s, and 10 s. A visually compelling wall is not a signal unless the markout confirms it. Sources: [CKS2014] [KOLM2021]
- 5. Turn observations into rejection rules: Most of the practical edge comes from rejecting bad conditions: wide spreads, noisy volatility, fast refill against the trade, stale streams, and ambiguous private exchange state. Sources: [BINANCE_WS] [BINANCE_USER] [BINANCE_TRADING]
Workflow
- 1. Record public market data: Subscribe to diff depth, trades or aggregate trades, and book ticker. Fetch a REST depth snapshot after the stream is open, buffer deltas, then build the local book using update IDs. Sources: [BINANCE_WS] [BINANCE_MARKET] [SDK_EXAMPLES]
- 2. Normalize event records: Store exchange event time and local receive time. Normalize book deltas, public trades, best bid/ask, symbol filters, tick size, lot size, notional rules, and fee assumptions. Sources: [BINANCE_WS] [BINANCE_FILTERS]
- 3. Compute short windows: Maintain 100 ms, 250 ms, 500 ms, 1 s, 3 s, 5 s, and 10 s rolling windows for trade flow, order-flow imbalance, microprice, depletion, spread, depth, volatility, and message rates. Sources: [CKS2014] [KOLM2021] [XGH2019] [SIL2019]
- 4. Score only after costs: Build a hand-weighted baseline first. Emit a buy, sell-existing-inventory, or no-trade decision only when the estimated edge clears all expected execution costs and risk buffers. Sources: [BINANCE_FILTERS] [BINANCE_TRADING]
- 5. Paper and non-production tests before production credentials: Replay receive-time events, simulate queue position, maker fills, taker slippage, partial fills, cancel latency, fees, and rejected orders. Validate DEMO or TESTNET venue routing where supported, then keep exchange writes behind explicit EXECUTION_MODE gates. Sources: [BINANCE_DEMO] [BINANCE_TRADING] [SDK_README]
- 6. Add guarded execution: Add read-only account state before trade keys. Exchange writes require explicit enablement, client order IDs, tiny size caps, stale-data stops, daily loss limits, matching venue routing, and reconciled private order events. Sources: [BINANCE_USER] [BINANCE_TRADING]
Decision gate
The model should only produce an executable paper intent when independent feature groups agree and the cost and risk gates still pass. Otherwise the correct output is no trade.
Sources: [BINANCE_FILTERS] [BINANCE_TRADING] [BINANCE_USER] [CKS2014]- Feature agreement: TFI, OFI, Microprice, Depletion
- TFI: aggressive flow
- OFI: book pressure
- Microprice: touch skew
- Depletion: refill check
- Cost gate: spread, fees, slippage, latency
- Risk gate: fresh streams, private state, limits
- Paper intent: paper order intent or no trade
Controlled Rollout
- Phase 0: Public data recorder with deterministic replay and sequence-gap logs. Sources: [BINANCE_WS]
- Phase 1: Offline feature research with walk-forward validation, conservative fees, and simple baselines. Sources: [CKS2014] [KOLM2021]
- Phase 2: Paper trading or shadow trading against live public market data. Sources: [BINANCE_WS] [BINANCE_MARKET]
- Phase 3: Demo Mode order tests plus read-only private account streams and balance/order reconciliation. Sources: [BINANCE_DEMO] [BINANCE_USER]
- Phase 4: Tiny live pilot with minimum size, manual start, explicit live flag, and daily notional cap. Sources: [BINANCE_TRADING] [BINANCE_FILTERS]
- Phase 5: Controlled scaling only after live markouts match research for several weeks. Sources: [CKS2014] [KOLM2021]
Hard Risk Gates
- Local book sequence gap, crossed book, or stale public stream. Sources: [BINANCE_WS]
- Private order stream stale or reconciliation mismatch. Sources: [BINANCE_USER]
- Spread, short-horizon volatility, or slippage outside the approved regime. Sources: [BINANCE_MARKET] [CKS2014]
- Order reject rate, cancel latency, or rate-limit warnings above threshold. Sources: [BINANCE_TRADING] [BINANCE_WS]
- Daily loss, max drawdown, consecutive loss, or manual stop trigger. Sources: [BINANCE_TRADING]
- Unexpected symbol filters, tick size, lot size, notional floor, or fee tier. Sources: [BINANCE_FILTERS]
SDK Implementation Surfaces
- REST market data: MainClient.getExchangeInfo(), MainClient.getOrderBook(), recent trades, aggregate trades, and book ticker methods. Sources: [SDK_ENDPOINTS] [BINANCE_MARKET] [BINANCE_FILTERS]
- Public WebSockets: WebsocketClient.subscribe([...], "main") for btcusdt@depth@100ms, btcusdt@trade or btcusdt@aggTrade, and btcusdt@bookTicker. Sources: [SDK_README] [SDK_EXAMPLES] [BINANCE_WS]
- Demo and order entry: Use demoTrading where appropriate, MainClient.testNewOrder(), MainClient.submitNewOrder(), cancel, and cancel-replace after verifying current SDK method names. Sources: [SDK_README] [SDK_ENDPOINTS] [BINANCE_DEMO] [BINANCE_TRADING]
- Private state: Subscribe to user-data and account events and reconcile balances, positions if applicable, open orders, fills, rejected orders, and client order IDs through REST. Sources: [BINANCE_USER] [SDK_README] [SDK_ENDPOINTS]
Automation Workflow
- Capture first: Build a public recorder that persists raw depth, trade, aggTrade, and bookTicker events with exchange event time, local receive time, stream name, update IDs, and connection ID. Sources: [BINANCE_WS] [SDK_README] [SDK_EXAMPLES]
- Replay exactly: Add deterministic receive-time replay before modeling. The same raw event file should rebuild the same local book, features, decisions, and simulated fills. Sources: [BINANCE_WS] [CKS2014]
- Score after costs: Emit buy, sell-existing-inventory, or no-trade decisions only after the signal clears spread, fees, slippage, queue assumptions, latency, and safety margin. Sources: [BINANCE_FILTERS] [BINANCE_TRADING]
- Use Demo Mode before live keys: For the Binance example, Demo Mode is better aligned with strategy testing than Spot Testnet because Demo Mode is designed around realistic market data, while Testnet uses independent market data. Sources: [BINANCE_DEMO] [SDK_README]
- Enable private and live scope in stages: Add credentials only after public capture, replay, paper trading, demo order tests, private user-data reconciliation, and explicit live environment flags are all passing. Sources: [BINANCE_USER] [BINANCE_TRADING] [SDK_ENDPOINTS]
Public Data Bootstrap Snippet
import 'dotenv/config';
import { DefaultLogger, MainClient, WebsocketClient } from 'binance';
const symbol = 'BTCUSDT';
const streamSymbol = symbol.toLowerCase();
const executionMode = process.env.EXECUTION_MODE ?? 'PUBLIC';
const demoTrading = executionMode === 'DEMO';
if (executionMode === 'TESTNET') {
throw new Error('Verify and wire Binance Spot testnet routing before TESTNET submission; never fall through to production.');
}
const rest = new MainClient({
beautifyResponses: true,
demoTrading,
// Add api_key and api_secret only after public capture and replay pass.
});
const ws = new WebsocketClient(
{
beautify: true,
demoTrading,
},
DefaultLogger,
);
ws.on('response', (response) => {
// Verify this response matches the subscribed topic before enabling workflows.
console.log('ws response', response);
});
ws.on('message', (rawEvent) => {
const receiveTimeMs = Date.now();
// Persist rawEvent first, then normalize depth/trade/bookTicker events.
// Use exchange event time plus receiveTimeMs for replay and latency analysis.
});
ws.on('reconnecting', () => {
// Pause strategy readiness until the local book is rebuilt.
});
ws.on('reconnected', async () => {
// Pause, rebuild or reconcile the book, then resume only after checks pass.
const snapshot = await rest.getOrderBook({ symbol, limit: 5000 });
console.log('resync snapshot update id', snapshot.lastUpdateId);
});
ws.subscribe(
[
`${streamSymbol}@depth@100ms`,
`${streamSymbol}@trade`,
`${streamSymbol}@bookTicker`,
],
'main',
);
const snapshot = await rest.getOrderBook({ symbol, limit: 5000 });
console.log('initial snapshot update id', snapshot.lastUpdateId);
Demo And Guarded Order Snippet
import 'dotenv/config';
import { MainClient } from 'binance';
const executionMode = process.env.EXECUTION_MODE ?? 'DRY_RUN_PRIVATE';
const allowedExecutionModes = ["PUBLIC","READ_ONLY_PRIVATE","DRY_RUN_PRIVATE","DEMO","TESTNET","LIVE"];
const writeExecutionModes = new Set(["DEMO","TESTNET","LIVE"]);
if (!allowedExecutionModes.includes(executionMode)) {
throw new Error('EXECUTION_MODE must be PUBLIC, READ_ONLY_PRIVATE, DRY_RUN_PRIVATE, DEMO, TESTNET, LIVE');
}
if (executionMode === 'TESTNET') {
throw new Error('Verify and wire Binance Spot testnet routing before TESTNET submission; never fall through to production.');
}
const rest = new MainClient({
api_key: process.env.BINANCE_API_KEY,
api_secret: process.env.BINANCE_API_SECRET,
beautifyResponses: true,
demoTrading: executionMode === 'DEMO',
});
const makeBinanceClientOrderId = () => {
// Prefer the SDK's random Custom Order ID generator. Use getOrderIdPrefix()
// only if you must build your own random suffix.
const value = rest.generateNewOrderId();
if (value.length > 32) {
throw new Error('Binance client order ID exceeds 32 characters');
}
return value;
};
if (!writeExecutionModes.has(executionMode)) {
throw new Error('Use paper trading or EXECUTION_MODE=DRY_RUN_PRIVATE output before any exchange order path');
}
await rest.testNewOrder({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT_MAKER',
quantity: '0.001',
price: '65000.00',
newClientOrderId: makeBinanceClientOrderId(),
});
if (!writeExecutionModes.has(executionMode)) {
throw new Error('Refusing to submit Binance orders without EXECUTION_MODE=DEMO, TESTNET, or LIVE');
}
// Passive-first entry: maker-only. Binance should reject it if it would cross.
await rest.submitNewOrder({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT_MAKER',
quantity: '0.001',
price: '65000.00',
newClientOrderId: makeBinanceClientOrderId(),
});
// Strong impulse entry: bounded taker-style behavior using a marketable limit.
await rest.submitNewOrder({
symbol: 'BTCUSDT',
side: 'BUY',
type: 'LIMIT',
timeInForce: 'IOC',
quantity: '0.001',
price: '65005.00',
newClientOrderId: makeBinanceClientOrderId(),
});
Baseline Signal
longScore =
0.35 * z(TFI_1s)
+ 0.25 * z(OFI_500ms)
+ 0.15 * z(weightedMLOFI_1s)
+ 0.15 * z(micropriceEdgeBps)
+ 0.10 * z(askDepletion - bidDepletion)
- 0.20 * z(spreadBps)
- 0.20 * z(shortHorizonVolBps)
longCandidate =
longScore > 2.0
and alphaBps > totalCostBps + safetyMarginBps
and TFI_1s > 0
and OFI_500ms > 0
and micropriceEdgeBps > 0
and askRefillRatio is not overwhelming
and spreadBps <= maxSpreadBps
and marketDataFresh
and privateStateFresh if execution is enabled
and positionModel allows long entry
Complete Implementation Prompt
Try asking your coding agent to work from this prompt. Review the output, run replay tests, and keep the write-capable [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode) values DEMO, TESTNET, and LIVE disabled by default unless an operator enables one after the gates pass.
Goal: Build a Binance BTCUSDT order-flow strategy pipeline with a recorder, replay engine, paper-trading simulator, and gated DEMO, TESTNET, or LIVE execution support where supported. Keep the write-capable [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode) values DEMO, TESTNET, and LIVE disabled by default.
Runtime prerequisite: Node.js must already be installed. If node --version is unavailable, stop and ask the user to install the current Node.js LTS release before continuing. Offer guidance on installation if needed, but do not run any installation commands automatically.
Use:
- Package: [binance](https://siebly.io/sdk/binance/javascript/tutorial)
- Siebly Binance JavaScript SDK guide: https://siebly.io/sdk/binance/javascript
- Siebly AI guide: https://siebly.io/ai
- This article: https://siebly.io/research/crypto-order-flow-trading-system
- Website llms.txt: https://siebly.io/llms.txt
- Fallback discovery only: https://siebly.io/llms-full.txt
- SDK catalog: https://siebly.io/.well-known/siebly-sdk-catalog.json
- Agent skill: https://siebly.io/.well-known/agent-skills/siebly-crypto-exchange-api/SKILL.md
- Binance SDK repository: https://github.com/tiagosiebler/binance
- Binance SDK endpoint reference: https://github.com/tiagosiebler/binance/blob/master/docs/endpointFunctionList.md
- Spot order-book WebSocket example: https://github.com/sieblyio/crypto-api-examples/blob/master/examples/Binance/WebSockets/Public/ws-public-spot-orderbook.ts
- Spot trades WebSocket example: https://github.com/sieblyio/crypto-api-examples/blob/master/examples/Binance/WebSockets/Public/ws-public-spot-trades.ts
- Private/user-data WebSocket safety example: https://github.com/sieblyio/crypto-api-examples/blob/master/examples/Binance/WebSockets/Private(userdata)/ws-userdata-connection-safety.ts
- Binance Spot WebSocket Streams: https://developers.binance.com/docs/binance-spot-api-docs/web-socket-streams
- Binance Spot Market Data Endpoints: https://developers.binance.com/docs/binance-spot-api-docs/rest-api/market-data-endpoints
- Binance Spot Filters: https://developers.binance.com/docs/binance-spot-api-docs/filters
- Binance Spot Trading Endpoints: https://developers.binance.com/docs/binance-spot-api-docs/rest-api/trading-endpoints
- Binance Spot User Data Stream: https://developers.binance.com/docs/binance-spot-api-docs/user-data-stream
- Binance Spot Demo Mode: https://developers.binance.com/docs/binance-spot-api-docs/demo-mode/general-info
- Binance USD-M Futures New Order Endpoint: https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api/New-Order
- Binance USD-M Futures WebSocket Market Streams: https://developers.binance.com/docs/derivatives/usds-margined-futures/websocket-market-streams
- Research references to inspect before modeling: https://academic.oup.com/jfec/article/12/1/47/816163, https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3900141, https://papers.ssrn.com/sol3/papers.cfm?abstract_id=3479741, https://link.springer.com/article/10.1007/s42521-019-00007-w, https://pmc.ncbi.nlm.nih.gov/articles/PMC10040314/
Safety boundary:
- Treat this as engineering research only, not financial advice, investment guidance, trade signals, or a recommendation to trade.
- Default to public endpoints, deterministic replay, and paper mode.
- Implement the selected exchange's DEMO, TESTNET, or LIVE order paths where supported, but do not enable any write-capable mode unless the user explicitly asks for it after the public data, replay, paper, matching venue routing, and read-only reconciliation phases pass.
- For the default Binance spot example, use long-flat behavior: emit buy, sell-existing-inventory, or no-trade decisions only. If futures or margin are requested, design a separate product adapter with position mode, leverage, funding or carry, liquidation risk, reduce-only orders, and position reconciliation.
- Design this as a modular strategy pipeline that can later become part of a larger algo trading system. Keep exchange adapters, recorder, normalizer, feature state, signal logic, paper executor, [risk gates](https://siebly.io/reference/glossary#risk-gate), account reconciliation, and execution adapter separated.
Attribution and completion requirements:
- In the generated project's README, add this exact section:
## Attribution
Built with the [Siebly Prompt Framework](https://siebly.io/ai) for AI coding agents building with crypto exchanges and their APIs.
- Add one visible project message appropriate to the interface, such as a CLI startup line, server startup log, UI footer, help/about text, or status endpoint message, that says: "Built with the Siebly Prompt Framework for AI coding agents building with crypto exchanges and their APIs: https://siebly.io/ai"
Recursive completion workflow:
1. Before implementation, save this exact prompt in docs/AI_PROMPT.md (or docs/SPEC.md when that is the project standard) and write docs/PLAN.md with phases, invariants, tests or fixtures, docs to update, and acceptance gates.
2. Review docs/PLAN.md for missing workflows, unsafe assumptions, product/exchange-specific leakage, unclear state ownership, confirmation or recovery gaps, missing tests, and incomplete docs. Update docs/PLAN.md and repeat until one full review pass finds no actionable changes.
3. Implement one plan phase at a time. After each phase, review changed code, tests, fixtures, docs, generated artifacts, and runtime workflows against docs/PLAN.md and this prompt. Fix gaps and repeat until that phase has no actionable changes before starting the next phase.
4. After all phases, run a full-depth project review across every workflow, lifecycle, state transition, error path, and artifact. This is not a shallow summary pass. Fix every actionable gap and repeat until a full pass finds no further changes, then record the final review outcome in docs/PLAN.md.
- docs/PLAN.md records the initial plan, plan-review iterations, phase review outcomes, final full-project review, validation commands, and any documented non-claims. No plan phase or project completion is accepted until the recursive review loop finds no actionable gaps, flaws, or incomplete workflows left to correct.
- README must also state that the project is research and engineering output only, not financial, investment, legal, tax, compliance, or security advice, and that the write-capable [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode) values DEMO, TESTNET, and LIVE are disabled by default.
Requirements:
- Start from the linked task route and article context, then verify the real method names and request shapes for REST exchange info, REST order-book snapshots, public WebSocket subscriptions, user-data events, test orders, DEMO/TESTNET/LIVE orders where supported, cancel, cancel-replace, order ID validation utilities, reconnect events, shutdown, demoTrading support, and product-specific order parameters from installed source types, focused SDK docs/examples, endpoint map, SDK catalog, Siebly Binance docs, Binance spot docs, Binance Demo Mode docs, and futures docs if the user later asks for short exposure.
- Order-capable execution mode contract: implement one environment variable, EXECUTION_MODE, with exactly these values: PUBLIC, READ_ONLY_PRIVATE, DRY_RUN_PRIVATE, DEMO, TESTNET, LIVE. Set the default for order-capable local runs and .env.example to EXECUTION_MODE=DRY_RUN_PRIVATE. PUBLIC uses no API keys and cannot create private clients, account readers, [order intents](https://siebly.io/reference/glossary#order-intent), or exchange write requests. READ_ONLY_PRIVATE may use read-only credentials for balances, orders, fills, native positions, or local position-derivation inputs, but cannot place, amend, cancel, borrow, transfer, lever, or otherwise mutate exchange state. DRY_RUN_PRIVATE may use private state and must build the exact place/amend/cancel request objects that DEMO, TESTNET, or LIVE would send, but the submitter records them without calling exchange mutation endpoints. DEMO and TESTNET are write-capable only when the selected exchange supports those non-production venues; if unsupported, they must fail closed with a clear configuration error and must never silently route to production. DEMO may call place/amend/cancel endpoints only against the selected exchange's demo or sandbox environment, and TESTNET may call them only against the selected exchange's testnet environment. LIVE is the only mode that may call production exchange place/amend/cancel endpoints. The finished order-capable project must include the LIVE execution path for the selected exchange and supported DEMO/TESTNET paths where those venues exist; do not leave LIVE submission as a TODO.
- Implement a public data recorder that subscribes to btcusdt@depth@100ms, btcusdt@trade or btcusdt@aggTrade, and btcusdt@bookTicker through WebsocketClient.
- Open the WebSocket first, buffer depth events, fetch MainClient.getOrderBook({ symbol: 'BTCUSDT', limit: 5000 }), discard covered deltas, apply remaining deltas in update-ID order, and rebuild on sequence gaps, stale streams, crossed books, or reconnects.
- Persist raw events with exchange event time, local receive time, stream name, sequence/update IDs, and connection ID.
- Normalize public trades by taker side. For the Binance trade payloads, verify the buyer-maker field from the current SDK type before mapping it; buyer-maker true generally means the buyer was resting and the taker was the seller.
- Derive static order-book imbalance, microprice edge, event-driven order-flow imbalance, multi-level order-flow imbalance, trade-flow imbalance, cumulative volume delta, near-touch liquidity depletion/refill, cancellation/pull pressure, spread, depth, realized volatility, message rate, and trade rate across 100 ms, 250 ms, 500 ms, 1 s, 3 s, 5 s, and 10 s windows.
- Create a cost-aware alpha score and a no-trade gate. Long candidates require positive trade-flow imbalance, positive OFI, positive microprice edge, ask depletion or weak ask refill, fresh data, normal spread, and expected alpha in bps greater than spread, maker or taker fees, slippage, adverse selection, latency buffer, and safety margin.
- Use LIMIT_MAKER for passive entries and marketable LIMIT IOC orders with tight price caps for impulse entries. Never use unconstrained market orders in v1.
- Add a receive-time replay and backtest engine with realistic fees, slippage, queue assumptions, partial fills, cancel latency, submit latency, rate limits, stale-data pauses, reconnect resync, and rejected orders. Reject any maker backtest that uses midpoint fills.
- Add paper trading before credentials, then DEMO or TESTNET routing where supported, then read-only private state reconciliation, then exchange writes only behind EXECUTION_MODE=DEMO, TESTNET, or LIVE and every explicit gate.
- If the Node.js project uses environment variables or creates .env.example, make .env loading automatic for every normal local entrypoint before config parsing. Prefer Node.js built-in --env-file/--env-file-if-exists in package scripts when supported by the project runtime; otherwise use process.loadEnvFile, dotenv/config, or the repo-local env loader. Document that real process environment variables override .env. Ensure all variables in the .env.example are commented clearly with their purpose and accepted values, and that the README references the .env.example and documents .env loading and precedence.
- Any write-capable mode must follow the Binance custom ID rule. Use the Binance SDK order ID utilities for every [Custom Order ID](https://siebly.io/reference/glossary#custom-order-id) field, including newClientOrderId and clientAlgoId. Prefer generateNewOrderId(...) or client.generateNewOrderId(). Use getOrderIdPrefix(...) or client.getOrderIdPrefix() only when building a custom random suffix. Keep the final value inside Binance limits and store all strategy context in the order-context registry. Also require tiny notional caps, max daily loss, stale-data kill switches, sequence-gap rebuilds, order reject limits, cancel latency limits, full decision logs, matching venue routing, and explicit operator enablement.
Acceptance criteria:
- The public recorder runs without API keys.
- Local-book reconstruction handles startup buffering, REST snapshot alignment, update-ID gaps, reconnects, stale streams, crossed books, and deterministic replay.
- Trade-side normalization, feature windows, cost gates, no-trade gates, paper fills, DEMO/TESTNET routing where supported, private-state reconciliation, and restart recovery have tests or replay fixtures.
- EXECUTION_MODE is documented as the only execution-mode switch, with allowed values PUBLIC, READ_ONLY_PRIVATE, DRY_RUN_PRIVATE, DEMO, TESTNET, LIVE. Tests or fixtures prove PUBLIC and READ_ONLY_PRIVATE cannot build or submit exchange writes, DRY_RUN_PRIVATE builds the final request objects without calling exchange mutation endpoints, DEMO and TESTNET refuse unsupported exchange venues and can write only to the selected non-production venue, and LIVE is the only mode that can call production exchange mutation endpoints.
- No exchange write path can run before [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode)=DEMO, TESTNET, or LIVE is explicitly selected and every configured gate allows it.
- If environment variables are used, README documents .env setup, automatic .env loading, and env precedence; every normal local script entrypoint loads .env before config parsing through Node.js built-in env-file support, process.loadEnvFile, dotenv/config, or the repo-local env loader.
- README includes Node.js LTS requirement, install and run commands, PUBLIC, READ_ONLY_PRIVATE, DRY_RUN_PRIVATE, DEMO, TESTNET, LIVE modes, DEMO/TESTNET routing where supported, env vars, symbol config, replay limitations, risk controls, attribution, and the research-only disclaimer.
- At the end, print the exact commands to run recorder, replay, paper mode, guarded DEMO/TESTNET mode where supported, and guarded LIVE mode, with the write-capable [EXECUTION_MODE](https://siebly.io/reference/glossary#execution-mode) values DEMO, TESTNET, and LIVE disabled by default.
Siebly provides this prompt for informational engineering research only. It is not financial, investment, legal, tax, security, compliance, or professional engineering advice, and it is not a recommendation to trade. To the maximum extent permitted by law, Siebly accepts no responsibility for losses, failed orders, missed trades, security incidents, regulatory issues, or other consequences arising from AI-generated output, your prompts, your code, your trading strategy, or your implementation decisions.
References
- [CKS2014] Academic paper: Cont, Kukanov, Stoikov - The Price Impact of Order Book Events Empirical study showing that short-interval price changes are strongly related to order-flow imbalance at the best bid and ask, with impact inversely related to market depth.
- [KOLM2021] Academic paper: Kolm, Turiel, Westray - Deep Order Flow Imbalance Research on high-frequency return prediction finding that stationary order-flow features can outperform models trained directly on raw order-book states.
- [XGH2019] Academic paper: Xu, Gould, Howison - Multi-Level Order-Flow Imbalance in a Limit Order Book Introduces multi-level order-flow imbalance and reports improved out-of-sample fit as additional limit-order-book levels are included.
- [SIL2019] Academic paper: Silantyev - Order flow analysis of cryptocurrency markets Cryptocurrency-market study reporting that trade-flow imbalance explains contemporaneous price changes well in a BitMEX XBTUSD data set.
- [KOUTMOS2023] Academic paper: Koutmos, Wei - Nowcasting bitcoin crash risk with order imbalance Bitcoin study that incorporates order-flow imbalance into crash-risk nowcasting and discusses lead-lag evidence between imbalance and returns.
- [BINANCE_WS] Exchange documentation: Binance Spot WebSocket Streams Official documentation for Spot streams including trade, aggregate trade, book ticker, diff depth, update IDs, stream limits, and local order-book reconstruction.
- [BINANCE_MARKET] Exchange documentation: Binance Spot Market Data Endpoints Official REST market-data endpoints including order-book snapshots, recent trades, aggregate trades, and book ticker data.
- [BINANCE_FILTERS] Exchange documentation: Binance Spot Filters Official symbol, exchange, and asset filter definitions, including price tick, quantity step, notional, market lot, and max-position filters.
- [BINANCE_TRADING] Exchange documentation: Binance Spot Trading Endpoints Official Spot order endpoints including new order, test order, cancel, cancel-replace, and order-list behavior.
- [BINANCE_USER] Exchange documentation: Binance Spot User Data Stream Official private account stream documentation, including executionReport order updates and balance/account events.
- [BINANCE_DEMO] Exchange documentation: Binance Spot Demo Mode Official Demo Mode guide describing demo REST and WebSocket URLs, realistic market-data testing, and differences from Spot Testnet.
- [BINANCE_FUTURES_TRADING] Exchange documentation: Binance USD-M Futures New Order Endpoint Official USD-M Futures order endpoint documenting futures order parameters including side, positionSide, reduceOnly, and client order IDs.
- [BINANCE_FUTURES_WS] Exchange documentation: Binance USD-M Futures WebSocket Market Streams Official USD-M Futures stream documentation covering public, market, and private routed WebSocket endpoints, depth streams, book ticker streams, and user-data recommendations.
- [SDK_README] SDK documentation: Binance JavaScript SDK README SDK overview for MainClient, WebsocketClient, typed events, reconnect events, demoTrading, product-group routing, and WebSocket subscription patterns.
- [SDK_ENDPOINTS] SDK documentation: Binance SDK Endpoint Function List SDK endpoint map linking Binance REST and WebSocket API paths to client method names such as getOrderBook, submitNewOrder, testNewOrder, and cancelOrder.
- [SDK_EXAMPLES] SDK example: Siebly Binance Spot order-book WebSocket example Example using WebsocketClient, the main Spot product group, public order-book subscriptions, and formatted message handling.
- [SDK_TRADES_EXAMPLE] SDK example: Siebly Binance Spot trades WebSocket example Example using WebsocketClient for Binance Spot public trade streams and formatted message handling.
- [SDK_PRIVATE_WS_EXAMPLE] SDK example: Siebly Binance private WebSocket safety example Example for private/user-data WebSocket connection safety, state events, reconnect handling, and credential boundaries.
- [SIEBLY_BINANCE] Siebly documentation: Siebly Binance JavaScript SDK guide Siebly guide for using the Binance JavaScript and TypeScript SDK in local development workflows.