learning_ai_invt_trdg/backend/ADMIN_TRADE_CONTROL_ARCHITECTURE.md

24 KiB

Admin Trade Control - Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                         ADMIN TRADE CONTROL SYSTEM                          │
│                                                                             │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │                          FRONTEND (Dashboard)                         │ │
│  │                                                                       │ │
│  │  ┌─────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Header (All Pages)                                             │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  Trading Status Badge                                    │   │ │ │
│  │  │  │  ⏸️ Trading Paused  OR  ▶️ Trading Active               │   │ │ │
│  │  │  │  (Orange)              (Green)                           │   │ │ │
│  │  │  │  Tooltip: "Paused by admin@example.com"                  │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  └─────────────────────────────────────────────────────────────────┘ │ │
│  │                                                                       │ │
│  │  ┌─────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Admin Tab (Admin Users Only)                                   │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  Trading Control Panel                                   │   │ │ │
│  │  │  │  ┌────────────────────────────────────────────────────┐  │   │ │ │
│  │  │  │  │  Status Banner                                     │  │   │ │ │
│  │  │  │  │  AUTO-TRADING: PAUSED / RUNNING                    │  │   │ │ │
│  │  │  │  │  "No new positions will be opened..."              │  │   │ │ │
│  │  │  │  └────────────────────────────────────────────────────┘  │   │ │ │
│  │  │  │  ┌────────────────────┐  ┌────────────────────────────┐  │   │ │ │
│  │  │  │  │ ⏸️ Pause Auto     │  │ ▶️ Resume Auto Trading    │  │   │ │ │
│  │  │  │  │    Trading         │  │                            │  │   │ │ │
│  │  │  │  │ (disabled if       │  │ (disabled if running)      │  │   │ │ │
│  │  │  │  │  already paused)   │  │                            │  │   │ │ │
│  │  │  │  └────────────────────┘  └────────────────────────────┘  │   │ │ │
│  │  │  │  ┌────────────────────────────────────────────────────┐  │   │ │ │
│  │  │  │  │  Safety Notice                                     │  │   │ │ │
│  │  │  │  │  "Pausing blocks new entries only. Existing        │  │   │ │ │
│  │  │  │  │   positions continue to be managed."               │  │   │ │ │
│  │  │  │  └────────────────────────────────────────────────────┘  │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  └─────────────────────────────────────────────────────────────────┘ │ │
│  │                                                                       │ │
│  │  ┌─────────────────────────────────────────────────────────────────┐ │ │
│  │  │  WebSocket Connection                                           │ │ │
│  │  │  • Receives 'health_update' events                              │ │ │
│  │  │  • Updates botState.health.tradingControl                       │ │ │
│  │  │  • UI reflects backend state in real-time                       │ │ │
│  │  └─────────────────────────────────────────────────────────────────┘ │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
│                                      ▲                                      │
│                                      │                                      │
│                          WebSocket   │   HTTP API                           │
│                          health_update   POST /internal/trading/pause       │
│                                      │   POST /internal/trading/resume      │
│                                      │   GET  /internal/trading/status      │
│                                      │                                      │
│  ┌───────────────────────────────────────────────────────────────────────┐ │
│  │                       BACKEND (Bot Service)                           │ │
│  │                                                                       │ │
│  │  ┌─────────────────────────────────────────────────────────────────┐ │ │
│  │  │  API Server (apiServer.ts)                                      │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  Admin Control Endpoints                                 │   │ │ │
│  │  │  │  • requireAuth middleware                                │   │ │ │
│  │  │  │  • requireAdmin middleware                               │   │ │ │
│  │  │  │  • Audit logging                                         │   │ │ │
│  │  │  │  • Idempotent operations                                 │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  │                           │                                      │ │ │
│  │  │                           ▼                                      │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  healthTracker.recordTradingControl()                    │   │ │ │
│  │  │  │  • Updates in-memory state                               │   │ │ │
│  │  │  │  • Broadcasts health_update via WebSocket                │   │ │ │
│  │  │  │  • Persists to disk (bot_state.json)                     │   │ │ │
│  │  │  │  • Persists to Supabase                                  │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  └─────────────────────────────────────────────────────────────────┘ │ │
│  │                                                                       │ │
│  │  ┌─────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Health Tracker (healthTracker.ts)                              │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  Trading Control State                                   │   │ │ │
│  │  │  │  {                                                        │   │ │ │
│  │  │  │    mode: 'RUNNING' | 'PAUSED',                           │   │ │ │
│  │  │  │    lastChangedBy: string,                                │   │ │ │
│  │  │  │    lastChangedAt: number,                                │   │ │ │
│  │  │  │    reason?: string                                       │   │ │ │
│  │  │  │  }                                                        │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  isPaused(): boolean                                     │   │ │ │
│  │  │  │  • Returns true if mode === 'PAUSED'                     │   │ │ │
│  │  │  │  • Called by enforcement points                          │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  └─────────────────────────────────────────────────────────────────┘ │ │
│  │                                                                       │ │
│  │  ┌─────────────────────────────────────────────────────────────────┐ │ │
│  │  │  ENFORCEMENT POINTS                                             │ │ │
│  │  │                                                                 │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  AutoTrader.handleSignal() (line 106-109)               │   │ │ │
│  │  │  │  ┌────────────────────────────────────────────────────┐  │   │ │ │
│  │  │  │  │  if (healthTracker.isPaused()) {                   │  │   │ │ │
│  │  │  │  │    logger.info("Entry BLOCKED: Bot is PAUSED");    │  │   │ │ │
│  │  │  │  │    return; // ❌ Block new entry                   │  │   │ │ │
│  │  │  │  │  }                                                  │  │   │ │ │
│  │  │  │  └────────────────────────────────────────────────────┘  │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  │                                                                 │ │ │
│  │  │  ┌──────────────────────────────────────────────────────────┐   │ │ │
│  │  │  │  TradeExecutor.openPosition() (line 531-534)            │   │ │ │
│  │  │  │  ┌────────────────────────────────────────────────────┐  │   │ │ │
│  │  │  │  │  if (healthTracker.isPaused()) {                   │  │   │ │ │
│  │  │  │  │    logger.info("Entry BLOCKED: Bot is PAUSED");    │  │   │ │ │
│  │  │  │  │    return { success: false, error: '...' };        │  │   │ │ │
│  │  │  │  │  }                                                  │  │   │ │ │
│  │  │  │  └────────────────────────────────────────────────────┘  │   │ │ │
│  │  │  └──────────────────────────────────────────────────────────┘   │ │ │
│  │  │                                                                 │ │ │
│  │  │  ✅ CONTINUES WHEN PAUSED:                                     │ │ │
│  │  │  • closePosition() - Exit orders                               │ │ │
│  │  │  • monitorStopLoss() - SL monitoring                           │ │ │
│  │  │  • monitorTakeProfit() - TP monitoring                         │ │ │
│  │  │  • reconcilePositions() - Position sync                        │ │ │
│  │  │  • syncOrderStatus() - Order status updates                    │ │ │
│  │  └─────────────────────────────────────────────────────────────────┘ │ │
│  │                                                                       │ │
│  │  ┌─────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Persistence Layer                                              │ │ │
│  │  │  ┌──────────────────┐  ┌──────────────────┐  ┌──────────────┐  │ │ │
│  │  │  │  In-Memory       │  │  Disk            │  │  Supabase    │  │ │ │
│  │  │  │  (HealthTracker) │→ │  (bot_state.json)│→ │  (Database)  │  │ │ │
│  │  │  │  Singleton       │  │  Local file      │  │  Remote DB   │  │ │ │
│  │  │  └──────────────────┘  └──────────────────┘  └──────────────┘  │ │ │
│  │  │  • State restored on bot restart                                │ │ │
│  │  │  • Supabase preferred, disk fallback                            │ │ │
│  │  └─────────────────────────────────────────────────────────────────┘ │ │
│  └───────────────────────────────────────────────────────────────────────┘ │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                              CONTROL FLOW                                   │
└─────────────────────────────────────────────────────────────────────────────┘

PAUSE FLOW:
1. Admin clicks "Pause Auto Trading" button
2. Frontend calls POST /internal/trading/pause with auth token
3. API Server validates: requireAuth + requireAdmin
4. API Server calls healthTracker.recordTradingControl({ mode: 'PAUSED' })
5. HealthTracker updates in-memory state
6. API Server broadcasts health_update via WebSocket
7. API Server persists to disk and Supabase
8. Frontend receives health_update, updates UI
9. Header badge shows "⏸️ Trading Paused"
10. AutoTrader/TradeExecutor check isPaused() before new entries
11. New entries blocked, existing positions continue

RESUME FLOW:
1. Admin clicks "Resume Auto Trading" button
2. Frontend calls POST /internal/trading/resume with auth token
3. API Server validates: requireAuth + requireAdmin
4. API Server calls healthTracker.recordTradingControl({ mode: 'RUNNING' })
5. HealthTracker updates in-memory state
6. API Server broadcasts health_update via WebSocket
7. API Server persists to disk and Supabase
8. Frontend receives health_update, updates UI
9. Header badge shows "▶️ Trading Active"
10. AutoTrader/TradeExecutor allow new entries
11. Normal trading resumes

RESTART RECOVERY:
1. Bot starts up
2. API Server calls loadState()
3. Reads bot_state.json from disk
4. Restores tradingControl state
5. Calls healthTracker.recordTradingControl() with restored state
6. Trading resumes in last known mode (PAUSED or RUNNING)

┌─────────────────────────────────────────────────────────────────────────────┐
│                           SECURITY LAYERS                                   │
└─────────────────────────────────────────────────────────────────────────────┘

Layer 1: Authentication
├─ All endpoints require valid JWT token
├─ Token verified via Supabase auth
└─ Unauthorized requests → 401

Layer 2: Authorization
├─ Pause/Resume require role = 'admin'
├─ Role checked in user profile
└─ Non-admin requests → 403

Layer 3: UI Guards
├─ Trading Control Panel hidden for non-admin
├─ Header badge visible to all (read-only)
└─ Buttons disabled when already in target state

Layer 4: Audit Trail
├─ All pause/resume actions logged
├─ Includes: userId, timestamp, reason
└─ Logs written to console and observability

┌─────────────────────────────────────────────────────────────────────────────┐
│                         DATA FLOW DIAGRAM                                   │
└─────────────────────────────────────────────────────────────────────────────┘

Admin Action
    │
    ▼
Frontend (AdminTab.tsx)
    │
    │ HTTP POST /internal/trading/pause
    │ Authorization: Bearer <token>
    │ Body: { reason: "..." }
    ▼
API Server (apiServer.ts)
    │
    ├─ requireAuth middleware → Verify JWT
    ├─ requireAdmin middleware → Check role
    │
    ▼
healthTracker.recordTradingControl()
    │
    ├─ Update in-memory state
    ├─ Broadcast WebSocket health_update
    ├─ Persist to bot_state.json
    └─ Persist to Supabase
    │
    ▼
WebSocket Broadcast
    │
    ▼
Frontend (useWebSocket.ts)
    │
    ├─ Receive health_update event
    ├─ Update botState.health.tradingControl
    │
    ▼
UI Updates
    │
    ├─ Header badge: "⏸️ Trading Paused"
    ├─ Admin panel: Status banner updates
    └─ Buttons: Pause disabled, Resume enabled

Trading Loop
    │
    ▼
AutoTrader.handleSignal()
    │
    ├─ Check: healthTracker.isPaused()
    │   ├─ if true → Block entry, return
    │   └─ if false → Continue to entry logic
    │
    ▼
TradeExecutor.openPosition()
    │
    ├─ Check: healthTracker.isPaused()
    │   ├─ if true → Return { success: false, error: '...' }
    │   └─ if false → Place order on exchange
    │
    ▼
Exchange Order Placement