Skip to content

BOJO1992/cryptotrading

Repository files navigation

Crypto Trading System — phase-confirm-exec

A two-part crypto trading workspace:

  • app/ — Python analysis backend. Read-only. Indicators, patterns, strategies, backtesting, risk validation, market/account reads. Never submits orders.
  • bybit-trader/ — TypeScript control layer. The sole execution surface. Order submission is gated behind mode, kill-switch, risk re-validation, and a one-shot confirmation token.

Everything previously capable of auto-execution (scheduler, execution_service, telegram_polling, reconciliation_service, live CLI, MCP trade tools) has been moved to legacy/ and is not installed with the active package.


Architecture

┌──────────────────────────────────────────────────────────────┐
│                         Claude / CLI                          │
└───────────────┬──────────────────────────┬───────────────────┘
                │                          │
                │ analysis                 │ plan → confirm → execute
                ▼                          ▼
┌──────────────────────────────┐  ┌──────────────────────────────┐
│ Python app/  (read-only)     │  │ bybit-trader/  (TypeScript)  │
│                              │  │                              │
│  market_data_service         │  │  mode_manager                │
│  portfolio_service           │  │  risk_engine                 │
│  signal_service (strategies) │  │  order_planner               │
│  risk_service (validator)    │  │  confirmation_gate           │
│  backtesting                 │  │  executor ◄── only submitOrder│
│  exchanges/ (fetch_* only)   │  │  bybit_client (read-only)    │
└──────────────┬───────────────┘  └──────────────┬───────────────┘
               │ CCXT read                       │ bybit-api write
               ▼                                 ▼
                         Bybit exchange

No scheduler, no admin socket, no Telegram approvals, no MCP trade tools.


Stage ladder (enforced by bybit-trader/src/mode_manager.ts)

Mode Allowed operations
READ_ONLY READ_MARKET, READ_ACCOUNT
ANALYSIS + ANALYZE
ORDER_PREP + PREPARE_ORDER
CONFIRM_EXEC + CONFIRM_ORDER, EXECUTE_ORDER (token required)
AUTO_LIMITED + EXECUTE_ORDER without per-trade prompt (Phase 5; unused)

Default mode in bybit-trader/system_config.json is READ_ONLY with trading_enabled=false and kill_switch=true.


Quick start — Python backend

bash scripts/setup.sh            # creates .venv, installs deps, copies .env
source .venv/bin/activate
pytest tests/ -v                 # run analysis + guard tests

Edit .env with your Bybit API key/secret. Phase 1 keys should be Read-only (no Trade, no Withdraw).

There is no long-running Python process to start. The backend is a library called from tests, notebooks, or the TS control layer.

Quick start — bybit-trader

cd bybit-trader
npm install
cp .env.example .env             # fill BYBIT_API_KEY / SECRET / TESTNET
npm run typecheck
npm run dev                      # HTTP status on http://127.0.0.1:7420/
npm run cli -- status            # print mode + config summary

Execution flow (Phase 2+):

npm run cli -- plan   '{"symbol":"BTCUSDT","side":"Buy",...}'   # builds OrderPlan + risk validation
npm run cli -- confirm <plan_hash>                              # issues one-shot token (60s TTL)
npm run cli -- execute --token <uuid>                           # submits order; fires only if all invariants hold

executor.ts is the only file in the repo that calls RestClientV5.submitOrder. The guard test tests/test_no_exec_surface.py locks this in.


Project structure

trading/
├── app/                          # Python analysis backend (READ-ONLY)
│   ├── indicators/               # trend, momentum, volatility, volume
│   ├── patterns/                 # candle, breakout, divergence, market structure
│   ├── strategies/               # ema_rsi_macd, breakout_confirmation, mean_reversion
│   ├── exchanges/
│   │   ├── base.py               # ExchangeBase (no place/cancel)
│   │   ├── ccxt_client.py        # fetch_* only
│   │   └── bybit_adapter.py
│   ├── services/
│   │   ├── market_data_service.py
│   │   ├── portfolio_service.py
│   │   ├── signal_service.py     # runs strategies → signals; no execution
│   │   ├── risk_service.py       # validator library
│   │   └── telemetry_service.py
│   ├── backtesting/              # engine, metrics, loaders
│   ├── models/                   # pydantic schemas
│   ├── storage/                  # SQLAlchemy + repositories
│   ├── utils/                    # time, ids, math, validations
│   └── settings.py               # execution_mode is pinned to "readonly"
├── configs/
│   ├── base.yaml                 # defaults
│   ├── paper.yaml                # analysis-only override
│   ├── testnet.yaml
│   ├── risk.yaml                 # validator caps
│   └── symbols.yaml              # allowed trading pairs
├── scripts/
│   ├── setup.sh                  # env bootstrap
│   └── run_backtest.sh           # backtests
├── bybit-trader/                 # TypeScript control layer (EXECUTION)
│   ├── risk_config.json
│   ├── system_config.json        # READ_ONLY / trading_enabled=false / kill_switch=true
│   ├── .env.example              # BYBIT_API_KEY / SECRET / TESTNET
│   ├── package.json
│   └── src/
│       ├── types.ts
│       ├── config_loader.ts
│       ├── mode_manager.ts       # enforces stage ladder
│       ├── risk_engine.ts        # validateTrade()
│       ├── bybit_client.ts       # read-only RestClientV5 wrapper
│       ├── order_planner.ts      # buildPlan() + plan_hash
│       ├── confirmation_gate.ts  # one-shot, 60s tokens
│       ├── executor.ts           # ONLY file that submits orders
│       ├── cli.ts                # status | plan | confirm | execute --token
│       └── index.ts              # status HTTP server on :7420
├── tests/
│   ├── test_indicators.py
│   ├── test_patterns.py
│   ├── test_backtesting.py
│   ├── test_risk.py
│   └── test_no_exec_surface.py   # locks in the refactor
├── state/                        # local runtime files (HALT, sqlite)
├── logs/
├── .env.example                  # EXCHANGE_API_KEY / SECRET / TESTNET
├── .mcp.json                     # {"mcpServers": {}} — no MCP trade tools
└── legacy/                       # archived pre-refactor; excluded from install

Risk controls

Two independent layers:

  1. Python risk_service.py — used by tests and analysis. Validates OrderRequest against kill-switch, symbol allow-list, stop-loss, position cap, notional cap, daily-loss cap, cooldown. Fail-closed on DB error. Not wired to any executor.

  2. TS risk_engine.tsvalidateTrade() runs inside order_planner.ts and again inside executor.ts immediately before submitOrder. Rejects on kill-switch, trading-disabled, pair allow-list, leverage cap, stop-loss validity, position cap, per-trade risk cap, daily-loss cap, hard-stop drawdown.

Kill switches:

# Python side — existence of this file is checked by risk_service
touch state/HALT

# TS side — flip system_config.json
jq '.kill_switch=true' bybit-trader/system_config.json > tmp && mv tmp bybit-trader/system_config.json

Execution also refuses if bybit-trader/system_config.json:trading_enabled=false.


Tests

source .venv/bin/activate
pytest tests/ -v --tb=short
File Covers
test_indicators.py EMA, SMA, RSI, MACD, ATR, Bollinger, VWAP
test_patterns.py Doji, hammer, engulfing, inside bar, market structure
test_backtesting.py Engine mechanics, stop/TP hits, metrics
test_risk.py All rejection cases + position sizing
test_no_exec_surface.py Locks in removal of order methods and MCP trade tools

TypeScript:

cd bybit-trader && npm run typecheck

Telegram Bot Commands

The Telegram execution bot provides 14 commands for order placement, position management, and order management.

Order Commands

/order SYMBOL buy|sell type=market|limit qty= price= leverage= sl= tp= reduce_only=true|false post_only=true|false

  • Create a pending order (requires /confirm)
  • Market orders must not include price
  • Limit orders must include price
  • qty is mandatory (no risk-based sizing)
  • reduce_only and post_only are optional (default false)
  • Example: /order BTCUSDT buy type=market qty=0.01 leverage=2 sl=50000 tp=55000

/confirm

  • Execute the pending order
  • Only works if mode=CONFIRM_EXEC, trading_enabled=true, kill_switch=false
  • Pending order expires after 120 seconds

/cancel

  • Cancel the pending order
  • Does not cancel open orders on the exchange

Position Management Commands

/close SYMBOL qty=

  • Close a position (full or partial)
  • If qty is not provided, closes the full position
  • If qty is provided, closes partial position
  • Executes immediately (no /confirm required)
  • Example: /close BTCUSDT (full close)
  • Example: /close BTCUSDT qty=0.005 (partial close)

/close_all SYMBOL=

  • Close all positions
  • If symbol is not provided, closes all positions for all symbols
  • If symbol is provided, closes all positions for that symbol
  • Executes immediately
  • Example: /close_all (close all positions)
  • Example: /close_all BTCUSDT (close all BTCUSDT positions)

/update_sl SYMBOL sl=

  • Update stop loss for an open position
  • Executes immediately
  • Example: /update_sl BTCUSDT sl=49000

/update_tp SYMBOL tp=

  • Update take profit for an open position
  • Executes immediately
  • Example: /update_tp BTCUSDT tp=56000

Order Management Commands

/cancel_order SYMBOL order_id=

  • Cancel a specific open order by ID
  • Executes immediately
  • Use /open_orders to get order IDs
  • Example: /cancel_order BTCUSDT order_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

/cancel_all SYMBOL=

  • Cancel all open orders
  • If symbol is not provided, cancels all orders for all symbols
  • If symbol is provided, cancels all orders for that symbol
  • Executes immediately
  • Example: /cancel_all (cancel all orders)
  • Example: /cancel_all BTCUSDT (cancel all BTCUSDT orders)

/open_orders SYMBOL=

  • Show open orders
  • Displays symbol, side, qty, price, and order_id for each order
  • Example: /open_orders (all orders)
  • Example: /open_orders BTCUSDT (BTCUSDT orders only)

Leverage Command

/set_leverage SYMBOL leverage=

  • Set leverage for a symbol
  • Validates against max_leverage in risk_config.json
  • Executes immediately
  • Example: /set_leverage BTCUSDT leverage=5

Status Commands

/positions

  • Show current positions with PnL

/status

  • Show bot status and pending order

/preflight

  • Check if execution is currently allowed
  • Reports mode, trading_enabled, kill_switch, allowed symbols, max leverage
  • Lists block reasons if execution is not allowed

Confirmation Rules

  • Only /order requires /confirm
  • All management commands execute immediately
  • New order placement is gated for safety
  • Position management actions are immediate for speed

Safety Model

  • Only TELEGRAM_ALLOWED_CHAT_ID can execute commands
  • Respects system_config mode, trading_enabled, kill_switch
  • All actions are logged to audit trail
  • No natural language parsing
  • One pending order at a time (120s TTL)

Telegram Bot Execution Control

The Telegram execution bot provides controlled live trading with explicit safety gates.

Exact Sequence to Enable Live Execution

Step 1: Verify risk configuration

cd bybit-trader
npm run check:config

Review risk_config.json:

  • allowed_pairs: symbols you want to trade
  • max_risk_per_trade: typically 0.01-0.02 (1-2%)
  • max_leverage: typically 1-10
  • max_daily_loss: typically 0.05 (5%)
  • hard_stop_loss: typically 0.10 (10%)

Step 2: Set system mode to CONFIRM_EXEC

jq '.mode="CONFIRM_EXEC"' system_config.json > tmp && mv tmp system_config.json

Step 3: Enable trading

jq '.trading_enabled=true' system_config.json > tmp && mv tmp system_config.json

Step 4: Disable kill switch

jq '.kill_switch=false' system_config.json > tmp && mv tmp system_config.json

Step 5: Verify configuration

npm run check:config

Step 6: Start Telegram bot

npm run telegram

Step 7: Verify execution is allowed Send /preflight command in Telegram. Expected output:

Execution Status: ✅ ALLOWED

Exact Sequence to Disable Immediately

Emergency Kill (instant):

jq '.kill_switch=true' system_config.json > tmp && mv tmp system_config.json

This blocks ALL executions immediately, even for pending orders.

Safe Disable (allows pending orders to complete):

jq '.trading_enabled=false' system_config.json > tmp && mv tmp system_config.json

This blocks new orders but allows pending confirmations.

Mode Downgrade (restricts operations):

jq '.mode="ORDER_PREP"' system_config.json > tmp && mv tmp system_config.json

This allows planning but blocks execution.

Required system_config.json Values for Execution

{
  "mode": "CONFIRM_EXEC",
  "trading_enabled": true,
  "require_confirmation": true,
  "kill_switch": false
}

Preflight Check

Always run /preflight before attempting execution. It reports:

  • System mode
  • Trading enabled status
  • Kill switch status
  • Allowed symbols
  • Max leverage
  • Max risk per trade
  • Whether execution is currently possible
  • Block reasons if any

Going live (future phases)

Live execution requires all of:

  • Phases 1–3 stable locally for ≥ 30 days with zero accidental orders
  • pytest tests/ -v green
  • npm run typecheck green in bybit-trader/
  • Risk parameters reviewed in configs/risk.yaml and bybit-trader/risk_config.json
  • Bybit API key is Trade + Read onlynever enable withdrawal
  • API key is IP-restricted to the machine that will execute
  • bybit-trader/system_config.json flipped to CONFIRM_EXEC + trading_enabled=true + kill_switch=false
  • BYBIT_TESTNET=false tested with a minimum-size test order

VPS deployment becomes necessary only at Phase 5 (unattended stop management). Phase 1–4 is local-only.


VPS Deployment

The trading system can be deployed to a VPS for manual trading workflow (analysis + trade plan generation only). Execution commands are disabled.

Prerequisites

  • Ubuntu 20.04+ or similar Linux distribution
  • Node.js 18+ and npm
  • Git
  • PM2 (process manager)

Secret Handling Rules

NEVER commit these to Git:

  • .env files (use .env.example as template)
  • API keys or secrets
  • logs/ directory
  • state/ directory
  • .tokens.json files
  • node_modules/ directory
  • dist/ directory

ALWAYS:

  • Copy .env.example to .env and fill in values
  • Set file permissions: chmod 600 .env
  • Use environment variables for all secrets

VPS Folder Structure

/home/ubuntu/cryptotrading/
├── .env.example              # Template for secrets
├── .gitignore                # Ignores secrets, logs, state
├── deploy.sh                 # Deployment script
├── README.md                 # This file
├── app/                      # Python analysis backend
├── bybit-trader/             # TypeScript control layer
│   ├── .env                  # Secrets (NEVER commit)
│   ├── .env.example          # Template
│   ├── .gitignore            # Ignores logs, state, .tokens.json
│   ├── node_modules/         # Ignored
│   ├── logs/                 # Ignored
│   ├── package.json
│   └── src/
└── logs/                     # Ignored

Deployment Steps

  1. Clone repository:
git clone https://github.com/BOJO1992/cryptotrading.git
cd cryptotrading
  1. Configure environment:
# Copy example env file
cp bybit-trader/.env.example bybit-trader/.env

# Edit with your API keys (READ-ONLY for manual trading)
nano bybit-trader/.env
  1. Run deployment script:
chmod +x deploy.sh
./deploy.sh

The deploy script:

  • Pulls latest changes from GitHub
  • Runs npm ci in bybit-trader/
  • Runs type check
  • Builds the project
  1. PM2 Setup (Manual Trading Mode):

Create PM2 ecosystem file:

nano ecosystem.config.js

Add this content:

module.exports = {
  apps: [{
    name: 'bybit-trader',
    script: './bybit-trader/src/cli.ts',
    interpreter: 'node',
    interpreter_args: '--loader tsx',
    env: {
      NODE_ENV: 'production',
      BYBIT_TESTNET: 'true',
    },
    error_file: './logs/pm2-error.log',
    out_file: './logs/pm2-out.log',
    log_date_format: 'YYYY-MM-DD HH:mm:ss Z',
    autorestart: true,
    max_restarts: 10,
    min_uptime: '10s',
  }]
};

Start with PM2:

pm2 start ecosystem.config.js
pm2 save
pm2 startup

Manual Trading Workflow

The system operates in READ_ONLY mode for manual trading:

# Generate trade signal
cd bybit-trader
npm run cli -- decision --symbol BTCUSDT

# Output includes:
# - Direction (LONG/SHORT)
# - Entry price
# - Stop Loss
# - Take Profit
# - Position size
# - Risk %
# - MANUAL ORDER INPUT section for copying to exchange

Execution commands (confirm, execute) are disabled for manual trading workflow.

Monitoring

# Check PM2 status
pm2 status

# View logs
pm2 logs bybit-trader

# Restart
pm2 restart bybit-trader

# Stop
pm2 stop bybit-trader

Safety Notes

  • System remains in READ_ONLY mode
  • No automatic execution
  • Trading must be done manually using the generated signals
  • All API keys are READ-ONLY (no trade permissions)
  • Logs and state files are excluded from Git

Coding standards

  • Python 3.11+, type hints on public functions
  • Pydantic v2 models at service boundaries
  • Decimal for prices/quantities, np.ndarray[float64] for indicator arrays
  • UTC-aware datetimes via app.utils.time_utils.utcnow() — never datetime.utcnow()
  • Structlog JSON logging, service=<name> bound on every service
  • No bare except: — catch specific exception types
  • New strategies extend app.strategies.base.Strategy

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors