OctoBot Architecture: Plugins, Async Signals, and the Trading Pipeline
Summary
This architecture overview explains how OctoBot organizes a modular crypto trading bot. Framework packages define shared contracts for data flow, evaluation, trading, and backtesting, while strategy logic, evaluators, and exchange connectors are supplied as plugins called tentacles. Plugin discovery and separate reference and profile configurations let implementations vary without changing the framework core.
At runtime, exchange data feeds evaluators through asynchronous channels. Evaluators publish scores to a shared signal matrix; strategy evaluators aggregate those signals after checking their timestamps for freshness. Trading modes then divide responsibilities between producers, which decide what to trade, and consumers, which handle exchange execution. A synchronized channel mode supports deterministic, time-driven backtesting. The document also outlines standalone, serverless, and multi-instance deployment options. It is a system design explanation, not a trading strategy or performance study: it gives implementation concepts but no results about profitability, execution quality, or the accuracy of any particular evaluator.
Key ideas
- OctoBot separates framework contracts from strategy and exchange implementations through plugins called tentacles.
- Asynchronous channels carry data between exchange feeds, evaluators, strategy logic, and trading modes.
- Evaluators publish signal scores to a shared matrix, and strategies check signal freshness before aggregation.
- Trading mode producers decide what to trade while consumers manage exchange execution.
- Synchronized channel operation enables time-driven backtesting, but the overview reports no strategy performance evidence.
Tags
Full text
# Architecture Overview
---
title: Architecture Overview
description: OctoBot system architecture — package layers, tentacle plugin system, async channel backbone, evaluation pipeline, configuration, and deployment modes.
keywords: [octobot, architecture, monorepo, packages, tentacles, async-channel, trading, evaluators]
sidebar_position: 1
---
# Architecture Overview
OctoBot is a modular, async crypto trading bot. The codebase is a Python monorepo under `packages/`, built with [Pants](https://www.pantsbuild.org/). Every strategy, evaluator, exchange connector, and trading mode lives as a **tentacle** — a plugin that sits on top of the framework without modifying it. This separation is the central design decision: the core packages define contracts; tentacles fulfill them.
## Package layers
The stack has six layers. Each package depends only on packages in the layers below it.
**commons** and **async_channel** are the foundation — neither has any internal dependency. Commons provides configuration, databases, DSL interpreter, and shared utilities. Async_channel is the typed message bus: channels connect producers to consumers through async queues. Synchronized mode removes async tasks and drives execution deterministically, which is what makes backtesting possible.
**tentacles_manager** and **backtesting** form the next layer. Tentacles_manager handles plugin discovery, installation, and configuration. Backtesting provides the time-driven simulation engine. Trading_backend handles exchange-specific broker ID injection and API key permission validation.
**trading** and **evaluators** are the core framework. Trading owns exchange managers, order lifecycle, portfolio accounting, and the trading mode abstraction. Evaluators own the Matrix (the in-memory signal tree) and the factory that instantiates evaluator classes across symbol/time-frame combinations.
**services** sits above trading and backtesting. It integrates external interfaces — web dashboard, Telegram, notification dispatch, AI model backends.
**agents**, **flow**, and **sync** are higher-level packages. Agents orchestrates LLM-powered teams through services. Flow is the stateless automation runner that uses trading for exchange operations. Sync provides multi-instance coordination over HTTPS.
**node** depends on flow and wraps it in a durable scheduler with crash recovery.
**tentacles** sits at the top alongside the CLI. It contains no framework code — only concrete implementations that subclass what the framework defines.
## The tentacle plugin system
A tentacle is a directory in the `tentacles/` tree with a Python module, a `metadata.json` descriptor, and an optional `config/` subdirectory. The descriptor names the Python classes it exports and declares a minimum compatible version. `tentacles_manager` discovers these directories at startup by scanning for `metadata.json` files up to three levels deep — no registry, no explicit registration call.
The `__init__.py` files throughout the `tentacles/` tree are generated by `tentacles_manager`, not written by hand. Each calls `check_tentacle_version()` on import: if the tentacle's declared version is below the minimum, the import is silently skipped. A broken tentacle cannot crash OctoBot at startup.
This boundary matters because it keeps strategy code out of the core. A new trading mode, evaluator, or exchange connector is a new directory in `tentacles/`, not a patch to `octobot_trading` or `octobot_evaluators`. The framework packages stay stable across very different trading strategies.
Configuration follows the same separation. Every tentacle class has a **reference config** inside its own `config/` directory — the factory default, never modified at runtime — and an optional **profile-specific copy** written to the active profile's `specific_config/` folder. At runtime, the profile copy wins if present; otherwise the reference is used.
## The async channel backbone
All runtime data flow is channel-based. A `Channel` subclass names a data type and declares its producer and consumer classes. Producers enqueue; consumers dequeue and call a registered callback. Consumers can be filtered — a TA evaluator subscribes to `EvaluatorsChannel` filtered to its specific symbol and time frame, so it only receives relevant triggers.
Channels start paused and resume automatically when a consumer with a non-optional priority level registers. This prevents upstream processing when nothing meaningful is listening.
`MatrixChannel` is the most consequential channel: every time an evaluator finishes and calls `evaluation_completed()`, it publishes to `MatrixChannel`. Strategy evaluators and trading mode producers both subscribe here to be notified when new signal data is available.
## The evaluation to trading pipeline
```
╔══════════════════════════════════════════════════════════════╗
║ Exchange (WebSocket / REST) ║
╚════════════════════════════╤═════════════════════════════════╝
│
┌──────────▼──────────┐
│ ExchangeManager │
│ candles · tickers │
│ order book · fees │
└──────────┬──────────┘
│ EvaluatorsChannel
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌───────────┐ ┌─────────────┐ ┌───────────┐
│ TA │ │ RealTime │ │ Social │
│ Evaluator │ │ Evaluator │ │ Evaluator │
└─────┬─────┘ └──────┬──────┘ └─────┬─────┘
│ eval_note │ eval_note │ eval_note
└───────────────┐ │ ┌───────────────┘
▼ ▼ ▼
┌──────────────────┐
│ Matrix │
│ (signal tree) │
└────────┬─────────┘
│ MatrixChannel
┌────────▼─────────┐
│ Strategy │
│ Evaluator │
└────────┬─────────┘
│ TradingModeChannel
┌───────────┴───────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Producer │ │ Consumer │
│ what/when │─────►│ how/execute │
│ to trade │ │ on exchange │
└──────────────┘ └──────┬───────┘
│
┌──────▼───────┐
│ Orders │
│ Portfolio │
└──────────────┘
```
Exchange data arrives from REST polling or WebSocket and populates per-time-frame circular buffers (three thousand candles each). On each closed candle, `EvaluatorsChannel` triggers the relevant TA evaluators. Each evaluator writes its `eval_note` (a float in `[-1, 1]`) to the Matrix and broadcasts on `MatrixChannel`. A `StrategyEvaluator` subscribes to `MatrixChannel` and aggregates signals from all evaluators for its configured time frames — but only after verifying that every contributing TA evaluator's Matrix timestamp is fresh enough relative to exchange time. The strategy posts its own note, which trading mode producers pick up to decide what orders to create.
The producer/consumer split inside a trading mode is deliberate: the producer decides *what* to trade based on signals; the consumer decides *how* to execute it against the exchange. Multiple trading modes can share one exchange and operate against isolated sub-portfolios.
## Configuration and profiles
Configuration has two layers merged at runtime:
- `config/config.json` — exchange credentials and per-installation settings. Never travels with a profile.
- `user/profiles/<name>/profile.json` — everything that defines a strategy: active tentacles, evaluator parameters, trading mode config. Safe to share or commit.
The active profile also contains `tentacles_config.json` (which tentacle classes are active) and `specific_config/` (per-class parameter overrides). Profiles can be marked `auto_update` to poll an origin URL on a configurable interval, which is how managed strategy updates are distributed. A profile update triggers a graceful bot restart.
`update_config_fields` applies dot-path updates in-place without reloading from disk, so the web UI can save small changes without unnecessary churn.
## Deployment modes
**Standalone bot** — the default. `octobot/octobot.py` starts four producers (`ExchangeProducer`, `EvaluatorProducer`, `ServiceFeedProducer`, `InterfaceProducer`) on a single asyncio loop. Everything described in the pipeline section runs here.
**Node (master/consumer)** — `octobot_node` is a standalone FastAPI service backed by [DBOS](https://docs.dbos.dev/), a workflow engine that persists every step to SQLite or PostgreSQL. A node accepts automation tasks over its REST/WebSocket API and hands them to `octobot_flow` for execution. An instance can act as master (schedules tasks), consumer (executes them), or both. Multi-node deployments share a PostgreSQL database; SQLite is single-node only. Task payloads support end-to-end encryption via a hybrid RSA/AES-GCM/ECDSA scheme with directional key separation.
**Serverless flow** — `octobot_flow` is a stateless execution engine for individual automations. An `AutomationState` object is passed in, a DAG of DSL actions runs, and the updated state is returned. No memory is retained between invocations, which makes it safe to run as a serverless function. The flow engine is what the node invokes per task iteration.
**Sync** — `octobot_sync` lets multiple OctoBot instances share configurations, signals, and account data over HTTPS. Every request is authenticated via an EIP-191 EVM wallet signature. Access control is data-driven through on-chain ownership resolution. Primary servers use S3-compatible object storage; replica servers mirror a subset locally.Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.