How the Bot Reads Charts: From Candles to Conviction
What actually happens inside a 4-hour analysis cycle — from raw exchange data to a reasoned decision with stop-loss and take-profit levels.
The input: more than candles
Most trading bots reduce the market to a handful of indicator values. Semantic Signal starts wider. Every cycle, CCXT pulls 999 candles, a 50-level order book, the funding rate, 24-hour volume and the last 500 trades from the exchange.
The order book matters because it shows where counterparties are actually sitting: bid/ask depth by level, imbalance, spread. That is real market microstructure — information a line chart cannot carry.
97 Numba-compiled functions, microseconds each
The indicator engine is NumPy plus Numba with @njit(cache=True): each function compiles to machine code on first call and caches the result. src/indicators/ holds 97 of them (134 across the whole src/ tree) — EMA, ADX, Supertrend, Ichimoku, Parabolic SAR, CMF, OBV, RSI, custom support/resistance and volatility work — with zero external technical-analysis dependencies.
That matters for the next step: the model receives a numeric stack that was computed deterministically and can be cross-checked line by line, instead of a black-box library's output.
The chart image: seeing like a human trader
Plotly renders a 1920×1080 candlestick chart with SMA, RSI, CMF and OBV panels plus swing-point annotations. That PNG is sent to the multimodal model alongside the raw indicator values.
Why an image at all? Because geometry carries information: the shape of a wick, a volume spike at support, a wedge that is about to break. A human trader sees these at a glance and struggles to express them as floats. The model reads both channels — picture and numbers — and every numeric claim it makes is checked against the computed values. Nothing is taken on faith.
One honest engineering note: Plotly's image export (Kaleido) hangs often enough that the exporter runs in a thread with a hard timeout and exponential-backoff retries. It is in the repo, not hidden.
Fresh context: news and community sentiment
Before reasoning, the cycle ingests up to 5 articles from live RSS feeds (CoinDesk, CoinTelegraph, Decrypt, CryptoSlate) — optionally enriched with the full article body via Crawl4AI — plus Reddit community sentiment from public Atom feeds. On-chain fundamentals come from CoinGecko and DefiLlama, and the mood index from Alternative.me's Fear & Greed. No paid API keys are involved.
X/Twitter is not used: without paid authentication it is not reliably scrapable, so the bot runs on Reddit + RSS rather than pretending otherwise.
The debate: bull vs bear
The multimodal model argues the bull case, then the bear case, then evaluates the trade against the bot's own closed-trade history:
If EV comes out negative, the signal is rejected. This is deterministic arithmetic that overrides the model — it is not a suggestion in a prompt. Expected value is currently the only hard entry gate: the old fixed 1.5 R/R floor was removed in September 2026 (min_rr_entry = 0.0) and replaced by one shared floor policy that combines the configured value with the brain's own borderline R/R, so that the number shown to the model is the same number the executor enforces.
The falsification check
Before a signal is accepted, the model has to write an explicit price invalidation trigger: the exact condition under which the trade is wrong. If it cannot defend the call against its own invalidation test, the signal is rejected. A missed +EV opportunity is treated as mathematically identical to a realized loss, so the bot is penalised for not trading when it should have.
The final output is a decision with reasoning, risk parameters and stop-loss/take-profit levels — logged to the audit trail and visible on the live dashboard. Stops are ATR-scaled rather than fixed percentages, and exits poll live prices every 15 minutes in hard mode.
Watch a real cycle end-to-end — the dashboard streams every prompt and decision.
Live dashboard ↗Next: vector memory →