Memory-aware algorithmic trading
The same signal gets a different trade because the graph remembers.
Trading systems are stateless. They take the same setup, in the same regime, that already cost them money, and they take it again. We put a graph of every past decision and its outcome inside the entry path, so an order is allowed, resized, or refused on the strength of what actually happened last time.
- 2–8 ms
- recall inside the entry path
- 0 LLM
- on the money path
- fail-closed
- no memory, no entry
Built on
- freqtrade
- FalkorDB
- LaserData
- RocketRide.ai
- Guild.ai
- FastAPI
- Bybit / ccxt
The problem
Stateless bots repeat their mistakes, precisely and at speed.
Every part of a modern trading stack is engineered except its memory. Execution is measured in microseconds; the institutional knowledge of what already failed lives in a Postgres table nobody queries at decision time.
- 01
It cannot recall its own trades
A strategy evaluates the present candle. It has no representation of the fact that it took this exact setup four times this week and closed every one of them red. The loss is in your database; it is not in the decision.
- 02
Aggregate statistics flatten the regime
“RSI cross on BTC” is a weak question. “RSI cross on BTC, high volatility, downtrend, after the last four attempts lost” is the useful one. A backtest average answers the first and hides the second.
- 03
Nothing links a decision to what it was thinking
When the book moves against you, the log gives you a timestamp and a fill. It does not give you the rationale that produced the order, or the evidence that rationale was built on.
- 04
Learning happens in the retro, not in the loop
The lesson from Tuesday reaches the system on Friday, as a parameter a human edited by hand. Between those two points the system keeps paying to relearn it.
The fix is not a better predictor. It is a system that asks one narrow historical question before it commits capital, and is willing to stand down when the answer is bad.
How it works
Six stages. Every one of them inspectable.
What follows is the mechanism as built — the real query, the real thresholds, the real prompt. Nothing here is an illustration of something we intend to write.
- 01Inputsticks · funding · OI · sentiment
- 02Graph memorydecisions + outcomes, written live
- 03Recall3-hop Cypher, worst loss first
- 04Decisiongate · analyst · risk manager
- 05Executiongoverned, sized, fail-closed
- 06Outcomecompounds back into the graph
Stage 01
What goes in
Four streams, all of them live. The system computes a Signal and a Regime from them — and the regime is what makes memory situational rather than a flat average.
| Input | Source | Cadence | Used for |
|---|---|---|---|
| Price ticks | Exchange public API via ccxt → LaserData market.ticks | 2 s, batched | signal detection, regime tagging |
| Funding rate + open interest | Same venue, same batch | 60 s refresh | funding_extreme signal, context |
| Market sentiment | Fear & Greed index → LaserData market.context | 10 min | sentiment bucket on the regime |
| Trade events | Engine webhooks → bridge → LaserData bot.events | per event | writing outcomes back into memory |
A regime is the volatility bucket plus the trend, optionally carrying the sentiment bucket — high-vol/down for example. “This signal on this pair” is a weak question. “This signal, on this pair, in this market regime” is the one worth asking.
Stage 02
What gets remembered
Every decision and every outcome is written to the graph as it happens. Memory compounds during the session and survives restarts.
(:Signal {type, ts, strength}) -[:ON]-> (:Pair {symbol})(:Signal) -[:IN]-> (:Regime {vol_bucket, trend, sentiment})(:Decision {action, rationale, confidence, vetoed}) -[:TRIGGERED_BY]-> (:Signal)(:Decision) -[:MADE_BY]-> (:Agent {name})(:Decision) -[:VETOED_BY]-> (:Agent) only when blocked(:Decision) -[:RESULTED_IN]-> (:Trade {id, side, size, pnl, status})The property that matters: a Decision keeps its rationale text and links to the Trade that resulted from it. So memory does not store “we lost money” — it stores what we believed at the time, and what happened next.
Stage 03
The question asked of memory
One parameterised Cypher query, roughly three hops. Worst loss first, so the most cautionary evidence is what the decider sees.
MATCH (d:Decision)-[:TRIGGERED_BY]->(prior:Signal {type: $sig})-[:ON]->(p:Pair {symbol: $pair}), (prior)-[:IN]->(r:Regime {vol_bucket: $vol}), (d)-[:RESULTED_IN]->(t:Trade)WHERE t.status = 'closed'RETURN t.pnl AS pnl, d.rationale AS rationale, d.confidence AS confidenceORDER BY t.pnl ASCLIMIT $limitIn English: every time this same signal fired on this same pair in this same volatility regime, and we acted on it — how did those trades end, and what were we thinking?
Recall is exact-match on those three keys. No embeddings, no fuzzy similarity. A regime it has never seen returns an empty list, and every consumer is told plainly that an empty list means no memory yet.
Stage 04
The decision — path A, the entry gate
The deterministic path. A strategy subclass overrides exactly one engine hook, confirm_trade_entry(), called after the strategy decides to enter and before the order goes out.
POST /gate — called from confirm_trade_entry(), before the order goes out{ "pair": "BTC/USDT:USDT", "side": "long", "entry_tag": "rsi_cross_up", "stake_proposed": 950.0 } response — 2–8 ms, deterministic, no model in the path{ "allow": false, "stake_factor": 0.0, "rationale": "Memory gate BLOCK: 5 of 5 recalled closed trades for BTC/USDT in high-vol regime (any signal) lost (-459.69 USDT)" }| Recalled closed losses | Verdict | Effect |
|---|---|---|
| ≥ 3 recalled closed losses | block | entry refused |
| ≥ 2 recalled closed losses | resize | stake × 0.5 |
| otherwise | allow | full stake |
| recall unavailable (DB down, timeout) | block | fail closed — never a silent allow |
The gate writes its own Decision node, so every consult — allow, block, or resize — appears in the graph and on the live feed. The thresholds are configuration, not folklore; we tune them against your book.
Stage 05
The decision — path B, judgment and governance
Off the money path, where latency is affordable, the recalled rows are handed to an analyst agent and then to a risk manager with final authority.
Analyst system prompt, verbatim
You are the Signal Analyst of a governed crypto trading pipeline … You receive one computed market signal and the graph memory of prior closed trades triggered by the same signal type on the same pair in the same volatility regime. Weigh the recalled outcomes heavily: repeated past losses in this regime must lower confidence and size_factor, or flip bias to flat.
The reply is validated against a strict schema. An invalid reply is retried once and then raises — the system never trades on an unparsed verdict.
| 1 | session drawdown over the configured limit | stop — halt the engine (circuit breaker) |
| 2 | proposal is flat | hold, no trade |
| 3 | recalled losses at the veto threshold, position cap reached, or confidence under the floor | veto |
| 4 | size or confidence outside the governed band | ask a human — blocks until answered; timeout is a veto |
| 5 | recalled losses at the cut threshold | resize |
| 6 | otherwise | approve |
Execution is mirrored so the gated and ungated legs are dispatched from the same approved decision, and a gate block is a recorded outcome rather than an error. Human approval, when it fires, is blocking; an unanswered prompt is a fail-closed veto.
Stage 06
The outcome compounds back
The fill and the final PnL arrive back through webhooks and become a Trade node linked to the decision that opened it — which is what the next recall will find.
- 01A detector fires on the tick batch; anti-churn guards check strength, per-pair cooldown and minimum hold.
- 02Recall runs the Cypher above and returns the prior closed trades, worst first, with the rationale each one was opened on.
- 03The gate allows, resizes or refuses. Off the money path, the analyst and risk manager reason over the same rows.
- 04The decision — action, rationale, confidence, veto edges — is written to the graph and streamed to the live UI.
- 05The trade's fill and final PnL return through webhooks and attach to that decision.
- 06The next identical signal is judged with more history than the last.
What memory does not do: it does not predict prices, it does not read order books or news, and it only ever makes the system more conservative — block, shrink, or step aside.
Architecture
A layered system, not a script with an API key.
Streaming, memory, reasoning, governance and execution are separate layers with typed boundaries between them. That is what makes it portable into an environment we did not build.
Every service, and what it is load-bearing for
Market data and sentiment enter through the stream layer; the decision loop recalls from the graph, consults the deciders, and acts on the engine; webhooks carry outcomes back into the graph. Remove the stream layer and the system is blind. Remove the graph and it is amnesiac — and it fails closed rather than silently reverting to amnesia.

Recall, verdict, governance branches, execution
The full sequence for a single signal: tick batch to detector, the three-hop recall, the analyst verdict, and the risk manager's branches — veto, human approval, circuit breaker, or an approval whose size the recalled losses have already halved.

Engineering posture
Fail-closed everywhere
A missing credential aborts the affected service at startup. An unreachable graph blocks the entry. An unparsed verdict raises. Nothing substitutes synthetic behaviour.
Deterministic on the money path
The entry gate is Cypher plus fixed rules. No model, no network round-trip to a vendor, no human — because it sits inside the live entry path.
Contracts before integrations
Every decider backend — local, cloud pipeline, governed agents, or a chain of them — implements the same typed contracts and the same strict parsing.
Auditable by construction
Every consult writes a node. Seeded history is labelled as seeded. The graph is the audit trail, and it is queryable while the system runs.
What it looks like running
The system, on screen, mid-flight.
These are captures from a live run — the operator UI, the gate verdicts, the graph itself. In a demonstration you drive this yourself, against instruments you choose.
The operator surface: memory effect, health, decision trace
Every decision card is read straight from the graph, not from a log file: the signal, the regime, the agent that ruled on it, the rationale it wrote, and whether a trade was linked. The live feed narrates the loop as it runs.

The evidence behind a refusal, one click away
A vetoed card with its drawer open — the recalled prior losses and the rationales they were opened on. Demo-seeded history is labelled seeded, in the graph and in the UI.

Block, veto, resize — all three are recorded outcomes
A block on five of five recalled closed losses, a risk-manager veto, and a resize cutting the stake by half. None of them is an error path; each writes its own node.

Decision, Signal, Trade and Agent nodes with their edges
The memory graph rendered live in the database browser. It is not a visualisation built for the demo — it is the store the recall queries, inspectable while the system trades.

Two engines, byte-identical strategy code
The gated and ungated bots side by side in one engine UI. The harness exists so the mechanism can be observed under control — see the note below on what the first epoch did and did not measure.

The ungated leg
The amnesiac baseline: it takes every entry it is handed, with no consult and no memory of the last time it took the same one.

The gated leg
The same strategy with one hook overridden. Its open position here was resized by the gate rather than taken at full stake.

Technology
Everything here is in the build. None of it is a logo on a slide.
Each layer is replaceable at its boundary — that is the point. In your environment the venue, the engine and the model are yours; the memory layer and its contracts are what we bring.
Execution
- freqtradeThe engine. A custom strategy subclass overrides one hook, confirm_trade_entry, which is where memory enters the order path.
- ccxt · Bybit futuresVenue connectivity and market data, running dry-run against real prices with simulated fills.
- DockerBoth engine legs and the graph run as containers, so an A/B harness is reproducible rather than ceremonial.
Memory
- FalkorDBThe graph store. Six node types, multi-hop Cypher recall over prior decisions and their closed trades, answered in single-digit milliseconds.
Streaming
- LaserDataApache Iggy streams carrying market ticks, funding rates, open interest, trade events and market sentiment on separate topics.
Pipeline
- RocketRide.aiThe decision pipeline executed in cloud, spoken over a DAP WebSocket protocol: authenticate, execute, process.
Governance
- Guild.aiTwo published TypeScript agents — a signal analyst and a risk manager — with human-in-the-loop approval and a drawdown circuit breaker. A timeout on approval is a veto.
Reasoning
- OpenAI gpt-5.4-miniThe analyst hop, off the money path. Schema-validated output; an unparsed verdict is never traded on.
Services
- Python 3.12 · uvRuntime and dependency management, locked and reproducible.
- FastAPIThe webhook bridge, the SSE live feed, the gate endpoint and the mission-control UI.
- pydanticThe typed contracts every integration codes against, so a decider backend can be swapped without touching the loop.
Verification
- pytest160+ tests, run live against real services wherever a live service exists rather than against mocks.
- PlaywrightBrowser-level checks of the operator surface.
- MermaidArchitecture diagrams generated from source that lives in the repository and moves when the system does.
What we don’t claim
We audited our own headline number and rewrote it.
We built the A/B harness ourselves: two engines, byte-identical strategy code, one of them consulting the graph. The first run produced a result that looked excellent. We went into the raw trade rows before anyone else could, and it did not survive.
- Finding 01
The trades were ours, not the strategy's
Every closed trade on the baseline leg was opened by our own decision overlay. Not one organic strategy entry was among them.
- Finding 02
They were churn, not positions
Median hold: six seconds. Each closed at roughly twice the taker fee. That figure was our overlay paying the exchange — it was not a strategy edge being lost.
- Finding 03
The gated leg was never asked
The loop force-entered the baseline only, so “no closed losses” on the memory-gated side partly meant it was never handed the trade. The advertised “identical entries, memory is the only variable” was not what that epoch measured.
- Finding 04
The fast pair was measuring nothing
At that cadence, in a flat market, edge-triggered detectors flip-flopped and the gate had no stable history to learn from. A comparison over that data answers no question worth asking.
What we do stand behind
The loop generated real losing setups. The graph learned them. The gate then refused those same setups, and “five of five recalled closed trades lost” is a true recall over trades that really happened. The mechanism works. The comparison was not a controlled study — so we fixed the harness, not the wording.
No backtested edge. No live profit-and-loss claim. No “outperforms”. This page sells a measured mechanism, the engineering around it, and an offer to run it in front of you. If you want a number, it should be one you watched us produce on your own data, under your controls — not one we printed in a deck.
Built to be verified, not asserted.
Talk to us
We would rather show you than tell you.
If you run a book and you suspect your system is paying twice for the same lesson, we will demonstrate this live — the graph, the recall, and a refusal you can trace to the trades that caused it.
- 01
A working session, not a deck
We stand the system up and drive it in front of you: signals firing, the recall running, the gate allowing, resizing and refusing, and the graph growing as it does.
- 02
Your setups, your instruments
Bring the entries you suspect you keep repeating. We seed the graph with your own history — honestly labelled as seeded — and you watch what the gate does with them.
- 03
Then it goes into your environment
Your engine, your venues, your models, your risk limits. What we bring is the memory layer, the contracts around it, and the discipline of failing closed.