# AI Agent Instructions for Trading Bot v4 ## Architecture Overview **Type:** Autonomous cryptocurrency trading bot with Next.js 15 frontend + Solana/Drift Protocol backend **Data Flow:** TradingView → n8n webhook → Next.js API → Drift Protocol (Solana DEX) → Real-time monitoring → Auto-exit **Key Design Principle:** Dual-layer redundancy - every trade has both on-chain orders (Drift) AND software monitoring (Position Manager) as backup. ## Critical Components ### 1. Position Manager (`lib/trading/position-manager.ts`) **Purpose:** Software-based monitoring loop that checks prices every 2 seconds and closes positions via market orders **Singleton pattern:** Always use `getPositionManager()` - never instantiate directly ```typescript const positionManager = getPositionManager() await positionManager.addTrade(activeTrade) ``` **Key behaviors:** - Tracks `ActiveTrade` objects in a Map - Dynamic SL adjustments: Moves to breakeven at +0.5%, locks profit at +1.2% - Closes positions via `closePosition()` market orders when targets hit - Acts as backup if on-chain orders don't fill ### 2. Drift Client (`lib/drift/client.ts`) **Purpose:** Solana/Drift Protocol SDK wrapper for order execution **Singleton pattern:** Use `initializeDriftService()` and `getDriftService()` - maintains single connection ```typescript const driftService = await initializeDriftService() const health = await driftService.getAccountHealth() ``` **Wallet handling:** Supports both JSON array `[91,24,...]` and base58 string formats from Phantom wallet ### 3. Order Placement (`lib/drift/orders.ts`) **Critical function:** `placeExitOrders()` - places TP/SL orders on-chain **Dual Stop System** (USE_DUAL_STOPS=true): ```typescript // Soft stop: TRIGGER_LIMIT at -1.5% (avoids wicks) // Hard stop: TRIGGER_MARKET at -2.5% (guarantees exit) ``` **Order types:** - Entry: MARKET (immediate execution) - TP1/TP2: LIMIT reduce-only orders - Soft SL: TRIGGER_LIMIT reduce-only - Hard SL: TRIGGER_MARKET reduce-only ### 4. Database (`lib/database/trades.ts` + `prisma/schema.prisma`) **Purpose:** PostgreSQL via Prisma ORM for trade history and analytics **Models:** Trade, PriceUpdate, SystemEvent, DailyStats **Singleton pattern:** Use `getPrismaClient()` - never instantiate PrismaClient directly **Key functions:** - `createTrade()` - Save trade after execution (includes dual stop TX signatures) - `updateTradeExit()` - Record exit with P&L - `addPriceUpdate()` - Track price movements (called by Position Manager) - `getTradeStats()` - Win rate, profit factor, avg win/loss ## Configuration System **Three-layer merge:** 1. `DEFAULT_TRADING_CONFIG` (config/trading.ts) 2. Environment variables (.env) via `getConfigFromEnv()` 3. Runtime overrides via `getMergedConfig(overrides)` **Always use:** `getMergedConfig()` to get final config - never read env vars directly in business logic **Symbol normalization:** TradingView sends "SOLUSDT" → must convert to "SOL-PERP" for Drift ```typescript const driftSymbol = normalizeTradingViewSymbol(body.symbol) ``` ## API Endpoints Architecture **Authentication:** All `/api/trading/*` endpoints (except `/test`) require `Authorization: Bearer API_SECRET_KEY` **Pattern:** Each endpoint follows same flow: 1. Auth check 2. Get config via `getMergedConfig()` 3. Initialize Drift service 4. Check account health 5. Execute operation 6. Save to database 7. Add to Position Manager if applicable **Key endpoints:** - `/api/trading/execute` - Main entry point from n8n (production) - `/api/trading/test` - Test trades from settings UI (no auth required) - `/api/trading/close` - Manual position closing - `/api/trading/positions` - Query open positions - `/api/settings` - Get/update config (writes to .env file) ## Critical Workflows ### Execute Trade (Production) ``` n8n webhook → /api/trading/execute ↓ normalize symbol (SOLUSDT → SOL-PERP) ↓ getMergedConfig() ↓ openPosition() [MARKET order] ↓ calculate dual stop prices if enabled ↓ placeExitOrders() [on-chain TP/SL orders] ↓ createTrade() [save to database] ↓ positionManager.addTrade() [start monitoring] ``` ### Position Monitoring Loop ``` Position Manager every 2s: ↓ getPythPriceMonitor().getLatestPrice() ↓ Calculate current P&L ↓ Check TP1 hit → closePosition(75%) ↓ Check TP2 hit → closePosition(100%) ↓ Check SL hit → closePosition(100%) ↓ Check dynamic adjustments (breakeven, profit lock) ↓ addPriceUpdate() [save to database] ``` ### Settings Update ``` Web UI → /api/settings POST ↓ Validate new settings ↓ Write to .env file using string replacement ↓ Return success ↓ User clicks "Restart Bot" → /api/restart ↓ Creates /tmp/trading-bot-restart.flag ↓ watch-restart.sh detects flag ↓ Executes: docker restart trading-bot-v4 ``` ## Docker Context **Multi-stage build:** deps → builder → runner (Node 20 Alpine) **Critical Dockerfile steps:** 1. Install deps with `npm install --production` 2. Copy source and `npx prisma generate` (MUST happen before build) 3. `npm run build` (Next.js standalone output) 4. Runner stage copies standalone + static + node_modules + Prisma client **Container networking:** - External: `trading-bot-v4` on port 3001 - Internal: Next.js on port 3000 - Database: `trading-bot-postgres` on 172.28.0.0/16 network **DATABASE_URL caveat:** Use `trading-bot-postgres` (container name) in .env for runtime, but `localhost:5432` for Prisma CLI migrations from host ## Project-Specific Patterns ### 1. Singleton Services Never create multiple instances - always use getter functions: ```typescript const driftService = await initializeDriftService() // NOT: new DriftService() const positionManager = getPositionManager() // NOT: new PositionManager() const prisma = getPrismaClient() // NOT: new PrismaClient() ``` ### 2. Price Calculations Direction matters for long vs short: ```typescript function calculatePrice(entry: number, percent: number, direction: 'long' | 'short') { if (direction === 'long') { return entry * (1 + percent / 100) // Long: +1% = higher price } else { return entry * (1 - percent / 100) // Short: +1% = lower price } } ``` ### 3. Error Handling Database failures should not fail trades - always wrap in try/catch: ```typescript try { await createTrade(params) console.log('💾 Trade saved to database') } catch (dbError) { console.error('❌ Failed to save trade:', dbError) // Don't fail the trade if database save fails } ``` ### 4. Reduce-Only Orders All exit orders MUST be reduce-only (can only close, not open positions): ```typescript const orderParams = { reduceOnly: true, // CRITICAL for TP/SL orders // ... other params } ``` ## Testing Commands ```bash # Local development npm run dev # Build production npm run build && npm start # Docker build and restart docker compose build trading-bot docker compose up -d --force-recreate trading-bot docker logs -f trading-bot-v4 # Database operations npx prisma generate # Generate client DATABASE_URL="postgresql://...@localhost:5432/..." npx prisma migrate dev docker exec trading-bot-postgres psql -U postgres -d trading_bot_v4 -c "\dt" # Test trade from UI # Go to http://localhost:3001/settings # Click "Test LONG" or "Test SHORT" ``` ## Common Pitfalls 1. **Prisma not generated in Docker:** Must run `npx prisma generate` in Dockerfile BEFORE `npm run build` 2. **Wrong DATABASE_URL:** Container runtime needs `trading-bot-postgres`, Prisma CLI from host needs `localhost:5432` 3. **Symbol format mismatch:** Always normalize with `normalizeTradingViewSymbol()` before calling Drift 4. **Missing reduce-only flag:** Exit orders without `reduceOnly: true` can accidentally open new positions 5. **Singleton violations:** Creating multiple DriftClient or Position Manager instances causes connection/state issues 6. **Type errors with Prisma:** The Trade type from Prisma is only available AFTER `npx prisma generate` - use explicit types or `// @ts-ignore` carefully ## File Conventions - **API routes:** `app/api/[feature]/[action]/route.ts` (Next.js 15 App Router) - **Services:** `lib/[service]/[module].ts` (drift, pyth, trading, database) - **Config:** Single source in `config/trading.ts` with env merging - **Types:** Define interfaces in same file as implementation (not separate types directory) - **Console logs:** Use emojis for visual scanning: 🎯 🚀 ✅ ❌ 💰 📊 🛡️ ## When Making Changes 1. **Adding new config:** Update DEFAULT_TRADING_CONFIG + getConfigFromEnv() + .env file 2. **Adding database fields:** Update prisma/schema.prisma → migrate → regenerate client → rebuild Docker 3. **Changing order logic:** Test with DRY_RUN=true first, use small position sizes ($10) 4. **API endpoint changes:** Update both endpoint + corresponding n8n workflow JSON 5. **Docker changes:** Rebuild with `docker compose build trading-bot` then restart container ## Integration Points - **n8n:** Expects exact response format from `/api/trading/execute` (see n8n-complete-workflow.json) - **Drift Protocol:** Uses SDK v2.75.0 - check docs at docs.drift.trade for API changes - **Pyth Network:** WebSocket + HTTP fallback for price feeds (handles reconnection) - **PostgreSQL:** Version 16-alpine, must be running before bot starts --- **Key Mental Model:** Think of this as two parallel systems (on-chain orders + software monitoring) working together. The Position Manager is the "backup brain" that constantly watches and acts if on-chain orders fail. Both write to the same database for complete trade history.