OctoBot

GitHub

Free open source crypto trading bot to automate AI, Grid, DCA and TradingView strategies on Binance, Hyperliquid and 15+ exchanges, with a simple interface.

RAW Rules

CLAUDE.md

# OctoBot

## Environment

### Installing pants

Pants is not bundled. Install the `scie-pants` launcher once (bootstraps pants 2.30.0 from `pants.toml` on first run):

```bash
curl -fsSL https://github.com/pantsbuild/scie-pants/releases/latest/download/scie-pants-linux-x86_64 \
  -o /usr/local/bin/pants
chmod +x /usr/local/bin/pants
pants --version   # triggers bootstrap; prints 2.30.0 when done
```

If the GitHub releases URL is reachable but the internal pex download is not, point pants at a locally cached pex binary by adding to `pants.toml` temporarily (do not commit):

```toml
[pex-cli]
url_template = "file:///path/to/pex"
```

### Python virtualenv

Resolves are disabled (`enable_resolves = false` in `pants.toml`) โ€” no lockfile is committed. Pants resolves requirements directly from `python_requirement` targets each run.

Create a local virtualenv from the project requirements for IDE / debugging:

```bash
python3.13 -m venv .venv
.venv/bin/pip install -r requirements.txt -r full_requirements.txt
```

Use `.venv/bin/python` as the interpreter for running and debugging.

> **Interpreter constraint**: `pants.toml` pins `interpreter_constraints = ["==3.13.*"]` (wildcard required โ€” `==3.13` matches only 3.13.0 exactly and rejects 3.13.1+).

### PYTHONPATH

```bash
ROOT=$PWD
# Without tentacles (bare start.py, no tentacle features):
export PYTHONPATH="$ROOT:$ROOT/packages/agents:$ROOT/packages/async_channel:$ROOT/packages/backtesting:$ROOT/packages/binary:$ROOT/packages/commons:$ROOT/packages/copy:$ROOT/packages/evaluators:$ROOT/packages/flow:$ROOT/packages/node:$ROOT/packages/protocol:$ROOT/packages/services:$ROOT/packages/sync:$ROOT/packages/tentacles_manager:$ROOT/packages/trading"

# With tentacles (after python start.py tentacles install):
export PYTHONPATH="$ROOT:$ROOT/packages/agents:$ROOT/packages/async_channel:$ROOT/packages/backtesting:$ROOT/packages/binary:$ROOT/packages/commons:$ROOT/packages/copy:$ROOT/packages/evaluators:$ROOT/packages/flow:$ROOT/packages/node:$ROOT/packages/protocol:$ROOT/packages/services:$ROOT/packages/sync:$ROOT/packages/tentacles:$ROOT/packages/tentacles_manager:$ROOT/packages/trading"
```

PYTHONPATH must use absolute paths (`$PWD`-based) โ€” build subprocesses run from tentacle subdirectories and need to resolve all packages.

## Tentacles

- **Source of truth**: `packages/tentacles/` โ€” all tentacle changes go here.
- **Never edit `tentacles/` directly** โ€” it is generated from `packages/tentacles/` via export+install and will be overwritten.
- After any change to `packages/tentacles/`, run the **tentacle-manager** agent to export and install.

## Agents

Custom agents live in `.claude/agents/`. They are **not** dispatchable via `subagent_type` โ€” trigger them with `@agent-<name>` in your prompt (e.g. `@agent-test-runner run node tests`), or use `claude --agent <name>` to make one the session agent.

### tentacle-manager

Manages the OctoBot tentacles lifecycle: export from source, install from zip, and generate CCXT exchange tentacles. Mirrors the VSCode "Install tentacles zip" launch configuration.

Use when: installing tentacles after code changes, generating exchange tentacles from CCXT, or running any `python start.py tentacles` command.

Trigger: `@agent-tentacle-manager` ยท Definition: `.claude/agents/tentacle-manager.md`

### test-runner

Runs and debugs OctoBot Python tests. Handles root-level tests (`tests/`) and per-package tests (`packages/<name>/tests/`). On failure, reads the test and source code, diagnoses the issue, fixes it, and re-runs.

Use when: running tests, debugging test failures, or verifying changes after code modifications.

Trigger: `@agent-test-runner` ยท Definition: `.claude/agents/test-runner.md`

## Code Conventions

### Enums
Place enums in `<package_root>/enums.py` (e.g. `octobot/enums.py`, `packages/node/octobot_node/enums.py`). Never define enums inline in module files.

### Constants
Place module-wide constants in `<package_root>/constants.py`. Private module constants (used only within one file) may stay in that file, prefixed with `_`.

### Typed errors
Define a typed error hierarchy rather than raising bare `ValueError`/`KeyError`. Pattern:
```python
# errors.py (sibling to the feature module)
class FeatureError(Exception): pass
class SpecificError(FeatureError): pass
```
Re-export from the package `__init__.py`. Use typed catches everywhere โ€” never inspect `str(err).lower()`, and never catch bare `ValueError`/`KeyError` for domain errors.

### TypedDicts for structured dicts
When a dict has a fixed schema (e.g. wallet info returned to callers), define a `typing.TypedDict`. Place it in the module that owns the data, before the class that produces it.

### Import priority in tentacle files
Prefer the installed `tentacles.Services.Interfaces.*` path first; fall back to bare direct imports (build-time fallback). Pattern:
```python
try:
    from tentacles.Services.Interfaces.node_api_interface.api.deps import X
except ImportError:
    from api.deps import X  # type: ignore[no-redef]
```
All files within a tentacle package should use the same priority order.

### Log levels
- `debug`: verbose diagnostics, expected no-ops.
- `info`: normal operational events (startup, shutdown, config loaded).
- `warning`: unexpected but recoverable (auto-unlock skipped, optional feature unavailable).
- `error`: configuration/state errors that affect functionality (wallet missing, key wrong).
- `exception`: unexpected exceptions โ€” always re-raise after logging unless the function is a top-level "best-effort" path that must not crash the caller.

### Shared filter helpers
Filtering logic used in multiple places belongs in a shared utility module (e.g. `workflows_util.py`), not duplicated inline. Name with a verb: `filter_by_wallet`, not `_filter`.

## Documentation

Documentation lives in `docs/content/` and is built with Docusaurus 3. Package docs go under `docs/content/developers/packages/<pkg-name>/`.

### Tone

Write descriptive prose that explains what things do and why, not how they're implemented line by line. Favor plain-language explanations over technical detail. Reference class or function names when they help anchor the explanation, but don't build the doc around them โ€” the reader should understand the concepts even if names change. The style should be descriptive yet grounded in code, explains design decisions and non-obvious behavior, mentions concrete names only when they clarify the concept.

### What to include

- Architecture and design decisions
- How components interact and why
- Important concepts and patterns
- Code snippets that illustrate non-obvious behavior
- Configuration that users/developers need to know about

### What NOT to include

The code is the source of truth. Don't duplicate anything that can be read from source or will break on the next refactor:

- **API surfaces**: function signatures, parameter lists, return types, class hierarchies, enum/constant values, error classes
- **Project structure**: directory trees, package layouts, dependency lists, requirements, version numbers
- **Categorized lists**: sections that just group and list code elements (helpers, classes, endpoints) without explaining why they exist
- **Implementation details**: build config specifics, hidden imports, lifecycle step-by-step sequences

### File format

Each `.md` file must have Docusaurus frontmatter:

```yaml
---
title: <Title>
description: <One-line description>
sidebar_position: <number>
---
```

The sidebar uses `autogenerated` for `developers/packages`, so new files appear automatically.