Audits › Drakkar-Software/OctoBot
Open-source trading bot · audit
Drakkar-Software/OctoBot
Free open source crypto trading bot to automate AI, Grid, DCA and TradingView strategies on Binance, Hyperliquid and 15+ exchanges, with a simple interface.
Repository facts
- Repository
- Drakkar-Software/OctoBot
- Stars
- 6,585
- Forks
- 1,276
- Open issues
- 168
- Language
- Python
- License
- GPL-3.0
- Created
- 2018-02-23
- Last push
- 2026-09-17
- README names exchanges
- binance, coinbase, kucoin, bybit, okx, bitget, gate.io, hyperliquid, ccxt
- README mentions an LLM
- yes
- Backtesting mentioned
- yes
- Paper trading / dry run mentioned
- yes
Source: GitHub API, 2026-09-17. Exchange and LLM mentions come from a keyword scan of the README, not from running the bot.
Risk-control audit
Audited 2026-09-17 · 6e46c8bd2ba5
21 of 21 controls checked: 4 present, 6 partial, 6 absent, 0 not applicable, 5 not verified.
- Main risk
- The AI Trading Mode lets an LLM-based agent team set live portfolio allocation targets that are applied with no code-level cap on order size, total exposure, or daily loss (no circuit breaker found anywhere in the repo) — the only stated risk limit is a prompt instruction to the model, not an enforced control.
- Summary
- OctoBot (Drakkar-Software) is a free, open-source, GPL-3.0 Python crypto trading bot supporting Binance, Hyperliquid and 15+ exchanges via CCXT, with paper-trading support confirmed by its own GitHub topics. It offers many trading modes including a new LLM-driven 'AI Trading Mode' built on LangChain Deep Agents (a multi-agent bull/bear/risk-judge/distribution debate). The clearest strength found is Telegram remote control: every command, including sell_all and set_risk, is gated by an authorized-user check. The clearest weakness is that the AI trading mode's portfolio-allocation output is applied almost directly (only floored at zero and renormalized to 100%) with no deterministic maximum-order-size, maximum-exposure, or circuit-breaker control in code between the LLM's decision and real order placement — the only 'respect risk limits' instruction found is a sentence inside the prompt sent to the model itself, not enforced code.
| Control | Verdict | Evidence | Note |
|---|---|---|---|
| Size and exposure | |||
| Maximum order size | Absent | packages/tentacles/Trading/Mode/index_trading_mode/index_trading.py: only 'min_order_size_margin' (Decimal('2'), a MINIMUM order size margin) and 'rebalance_trigger_min_ratio' (0.05, a MINIMUM % move to trigger) exist. No maximum per-order size cap (currency or % of balance) was found in this mode nor in the AI distribution layer (ai_index_distribution.py). | Found a minimum-size guard, never a maximum. Could not check the other 12 trading-mode tentacles (grid, dca, staggered_orders, daily, arbitrage, dip_analyser, dsl, script) individually due to time budget; each implements its own sizing independently. |
| Execution paths that skip the cap | Absent | No unified/deterministic order-size-cap module was found that all trading modes route through; each Trading/Mode tentacle (ai_trading_mode, index_trading_mode, grid_trading_mode, dca_trading_mode, etc.) computes and submits its own order sizes independently (packages/tentacles/Trading/Mode/ directory listing). | Since no tope_orden exists centrally, there is structurally no single choke point all routes pass through; a new trading mode/tentacle is free to size orders however it wants. |
| Total exposure and leverage | Absent | ai_index_distribution.py apply_ai_instructions(): the only bound applied to AI-proposed allocations is max(0, ...) (floor at zero) and a final normalize-to-100% step. No cap on total leveraged exposure, number of open positions, or max allocation change per cycle was found. | On futures/options (index_trading.py checks self.exchange_manager.is_future/is_option) the same unlimited-normalize logic applies; leverage is set per-symbol elsewhere (exchange_config_data.py) but no global exposure ceiling was located. |
| Brakes and stops | |||
| Global circuit breaker | Absent | GitHub code search (GET /search/code) for 'circuit_breaker', 'daily_loss', 'max_drawdown', 'kill_switch' scoped to repo:Drakkar-Software/OctoBot returned 0 real matches (2026-09-17). | No global loss-based or consecutive-failure-based kill switch was found anywhere in the codebase under any of the common names. |
| Orphaned stop-loss | Not verified | Order type implementations live under packages/trading/octobot_trading/personal_data/orders/ (order.py, order_factory.py, and an unread 'types' subfolder); not opened due to time/budget. | Could not confirm whether stop-loss orders are placed exchange-side (native STOP order) or simulated locally by the running process. |
| Orderly shutdown | Not verified | Telegram /stop command (telegram_bot.py) triggers AbstractBotInterface.set_command_stop in a background thread, but the shutdown sequence itself (whether it cancels open orders) was not opened due to time/budget. | Not confirmed whether SIGINT/SIGTERM/Ctrl+C or the /stop command cancel open orders before exiting. |
| Connection and operation | |||
| Websocket and data reconnection | Not verified | packages/trading/octobot_trading/exchanges/abstract_websocket_exchange.py (8521 bytes) exists as a dedicated websocket abstraction, confirmed via directory listing, but its reconnect/backoff/429-handling logic was not read due to time/budget. | Presence of a dedicated abstraction is a positive signal but not evidence of correct reconnect behavior. |
| Duplicate-process lock | Absent | GitHub code search for 'lockfile' scoped to repo:Drakkar-Software/OctoBot returned only unrelated hits (CLAUDE.md text, a web interface README) - no PID/flock/singleton mechanism (2026-09-17). | No evidence of a mechanism preventing two OctoBot instances from running against the same account simultaneously. |
| Heartbeat and alerts | Partial | index_trading.py._send_alert_notification() sends a notification via octobot_services.api.send_notification on each rebalance trigger; telegram_bot.py exposes a /ping command; requirements.txt pins sentry-sdk==2.56.0 for exception tracking. | Event-based alerts and crash reporting to the maintainers (Sentry) exist, but no externally-pollable health/heartbeat endpoint was found for a user's own uptime monitor. |
| Accounting and reconciliation | |||
| Position reconciliation with the exchange | Present | index_trading.py IndexTradingModeProducer.ensure_index() calls _wait_for_positions_init() and _wait_for_orders_init() at startup before rebalancing, loading real exchange positions/orders; telegram_bot.py exposes /refresh_portfolio ('Forces OctoBot's real trader portfolio refresh using exchange data'). | Both an automatic startup sync and a manual on-demand resync from the exchange exist. |
| Fees and funding in the accounting | Partial | telegram_bot.py exposes a /fees command ('Displays the total amount of fees I paid since I started'), confirming fee tracking/reporting exists. | Fee accounting for reporting is confirmed; whether fees (and, for perpetuals, funding) are deducted when SIZING new orders was not verified due to time budget. |
| Order rejection handling | Present | index_trading.py IndexTradingModeConsumer._rebalance_portfolio(): except (trading_errors.MissingMinimalExchangeTradeVolume, copy_errors.RebalanceAborted) as err: logs a warning/error and marks the rebalance as REBALANCING_SKIPPED instead of retrying blindly or crashing. | Structured typed-error handling with logging and a bounded abort path was found for at least the index/rebalance order path. |
| Auditable log | Present | Extensive self.logger.info/debug/warning/error/exception calls throughout index_trading.py and ai_index_trading.py log every rebalance step ('Step 1/3...3/3'), every AI distribution decision (asset, percentage, action, explanation), and full debate/judge history when log_ai_decisions is enabled. | Logging is per-decision and per-order-step, not just a generic catch-all. |
| Security | |||
| API key permissions | Partial | exchange_credentials_data.py only models api_key/secret/password/uid (CEX) or wallet_address/private_key (DEX) fields; GitHub code search for '.withdraw(' found a match only in a DEX blockchain_wallet_operators tentacle (on-chain, user-initiated), not in any CEX/CCXT order-execution path. | No code path was found that calls a CEX withdrawal/transfer endpoint, which reduces (but does not eliminate) the blast radius of a leaked key. Could not verify whether official setup docs instruct users to create a trade-only, IP-restricted key. |
| Secrets handling | Not verified | ExchangeCredentialsData is an in-memory dataclass only; the on-disk storage/encryption mechanism for saved exchange credentials (profile config) was not located within the time budget. | Not confirmed whether keys are stored in plaintext config, an OS keychain, or encrypted at rest. |
| Authenticated remote control | Present | telegram_bot_interface/telegram_bot.py: every single command handler (command_start, command_stop, command_restart, command_sell_all, command_sell_all_currencies, command_risk, command_pause_resume, etc.) begins with 'if TelegramBotInterface._is_valid_user(update):' and replies interfaces_bots.UNAUTHORIZED_USER_MESSAGE to unrecognized chats otherwise. | Every state-changing command, including sell_all and set_risk, is gated by an authorized-user check before execution. |
| AI and learning | |||
| The model cannot override the brakes | Absent | ai_index_distribution.py apply_ai_instructions(): the AI's proposed distribution instructions are applied directly to trading_mode.ratio_per_asset with only max(0, ...) floors and a final renormalize-to-100%; there is no check against a deterministic max-order-size, max-exposure, or circuit-breaker control before this feeds into ensure_index() and real order creation. | The only textual safeguard is a prompt instruction to the LLM itself ('Always respect risk limits and never recommend over-leveraging' in DISTRIBUTION_AGENT_INSTRUCTIONS, deep_agent_team.py) — not enforced in code. |
| Model output validation | Partial | deep_agent_team.py._parse_result() validates the LLM's JSON via models.DistributionOutput.model_validate()/models.ExecutionPlan.model_validate() (pydantic); ai_index_trading.py.run_with_portfolio() validates portfolio/strategy inputs via models.PortfolioState.model_validate(). | On validation failure, both paths fall back to using the raw/unvalidated data or raw content ('Fall through to create result with raw output', 'Proceeding with raw data') instead of rejecting the response outright — so malformed AI output is not always blocked from continuing downstream. |
| Learns from its mistakes | Partial | deep_agent_team.py instructs agents to save insights to '/memories/trading_insights/', '/memories/signals/', and '/memories/distributions/' for future reference (long-term agent memory), and ai_index_trading.py logs full debate_history and judge_decisions when log_ai_decisions is enabled. | The bot records outcomes and lets the LLM write persistent memory that will influence future runs, but no code-level review, backtest, or limit gate on that self-written memory before it affects live decisions was found. |
| Model poisoning | Not verified | The reviewed code (ai_index_trading.py._build_agent_state) only feeds internal structured data (portfolio holdings, open orders, evaluator strategy scores) into the LLM prompt; SIGNAL_AGENT_INSTRUCTIONS in deep_agent_team.py asks the model to consider 'Recent news and events impact' but no code ingesting raw third-party text (tweets, news feeds) was found in the files reviewed. | LangChain Deep Agents may have additional tool access (e.g. web search) not covered by the files read; could not confirm or rule out within the time budget. |
| Behavior without model quota or response | Partial | ai_index_trading.py._run_agents(): 'ai_service = await self._get_ai_service(); if ai_service is None: self.logger.error(...); return' — if the AI service/model is unavailable, the bot logs an error and simply skips that analysis cycle (no orders placed, no crash). | Graceful skip on missing AI service is confirmed; however only a per-response max_tokens config (MAX_TOKENS_KEY, 500-4000) was found — no daily/weekly token or dollar cost cap was located. |
Risk-control audit: Drakkar-Software/OctoBot
**What it is.** OctoBot is a free, open-source (GPL-3.0) crypto trading bot written in Python, with 6,585 GitHub stars. It connects to Binance, Hyperliquid and 15+ other exchanges through CCXT, and offers many "trading modes" a user can pick: grid trading, DCA, index rebalancing, a script/DSL mode, and — new in this codebase — an **AI Trading Mode** where a team of LLM agents (built on LangChain "Deep Agents") debates bullish vs. bearish cases and decides how to reallocate the portfolio. It also supports paper trading (simulated, no real money) per the project's own GitHub topics.
**What's solid.** Telegram remote control is properly locked down: every single command (including the dangerous ones — `/sell_all`, `/sell_all_currencies`, `/set_risk`, `/stop`, `/restart`) checks that the sender is an authorized user before doing anything. The bot also reconciles its view of the portfolio with the real exchange at startup and offers a manual `/refresh_portfolio` command, tracks and reports fees paid, and produces detailed step-by-step logs of every rebalance and every AI decision (including the full bull/bear debate history when enabled).
**What's missing.** We found no circuit breaker anywhere in the code — no mechanism that halts the bot after a daily loss limit, a large drawdown, or a string of consecutive failures. We also found no lockfile or similar guard against two copies of the bot running at once. Most importantly, in the new AI Trading Mode, the LLM agent team's proposed portfolio reallocation is applied almost as-is: the code only prevents negative allocations and rescales everything back to 100%, with no maximum change per cycle, no maximum exposure cap, and no deterministic check against the AI's decision before it turns into real orders. The only instruction telling the AI to "respect risk limits and never over-leverage" lives inside the text prompt sent to the model — it is not enforced in code. The AI's JSON output is validated for structure (via Pydantic), but when that validation fails, the code falls back to using the raw, unvalidated response rather than rejecting it.
**Not verified (ran out of time/budget, not because anything looked wrong):** whether stop-loss orders survive a bot restart (exchange-side vs. in-memory), whether a graceful shutdown cancels open orders, the exact websocket-reconnect logic, and how saved exchange API keys are stored on disk.
**Audited:** branch `master`, commit `6e46c8bd2ba5ac9693cfce455887db739ad121f8`, checked 2026-09-17, via GitHub's public API and raw file contents only (no code was installed or executed).
Badge for the README
<a href="https://saasfactoryagents.com/bots/drakkar-software-octobot/"><img src="https://saasfactoryagents.com/bots/drakkar-software-octobot/badge.svg" alt="risk controls audit: 4/21 present"></a>
Fixed a control? Request a free re-audit at the new commit.
Exchanges
Also named in the README: binance, kucoin, bybit, okx, bitget, gate.io, hyperliquid, ccxt.
Running OctoBot with real money?
The watchdog runs apart from the bot with a read-only key and alerts you when drawdown, position size or heartbeat cross your limits. Founding price 19 USD/month, early access open.