diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0d91ca8..647a50c 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,22 +8,32 @@ **Key Design Principle:** Dual-layer redundancy - every trade has both on-chain orders (Drift) AND software monitoring (Position Manager) as backup. +**Exit Strategy:** Three-tier scaling system: +- TP1 at +1.5%: Close 75% (configurable via `TAKE_PROFIT_1_SIZE_PERCENT`) +- TP2 at +3.0%: Close 80% of remaining = 20% total (configurable via `TAKE_PROFIT_2_SIZE_PERCENT`) +- Runner: 5% remaining with 0.3% trailing stop (configurable via `TRAILING_STOP_PERCENT`) + +**Signal Quality System:** Filters trades based on 5 metrics (ATR, ADX, RSI, volumeRatio, pricePosition) scored 0-100. Only trades scoring 60+ are executed. Scores stored in database for future optimization. + ## 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 +**Singleton pattern:** Always use `getInitializedPositionManager()` - never instantiate directly ```typescript -const positionManager = getPositionManager() +const positionManager = await getInitializedPositionManager() 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% +- Three-tier exits: TP1 (75%), TP2 (80% of remaining), Runner (with trailing stop) +- Dynamic SL adjustments: Moves to breakeven after TP1, locks profit at +1.2% +- Trailing stop: Activates after TP2, tracks `peakPrice` and trails by configured % - Closes positions via `closePosition()` market orders when targets hit - Acts as backup if on-chain orders don't fill +- State persistence: Saves to database, restores on restart via `configSnapshot.positionManagerState` ### 2. Drift Client (`lib/drift/client.ts`) **Purpose:** Solana/Drift Protocol SDK wrapper for order execution @@ -59,10 +69,17 @@ const health = await driftService.getAccountHealth() **Singleton pattern:** Use `getPrismaClient()` - never instantiate PrismaClient directly **Key functions:** -- `createTrade()` - Save trade after execution (includes dual stop TX signatures) +- `createTrade()` - Save trade after execution (includes dual stop TX signatures + signalQualityScore) - `updateTradeExit()` - Record exit with P&L - `addPriceUpdate()` - Track price movements (called by Position Manager) - `getTradeStats()` - Win rate, profit factor, avg win/loss +- `getLastTrade()` - Fetch most recent trade for analytics dashboard + +**Important fields:** +- `signalQualityScore` (Int?) - 0-100 score for data-driven optimization +- `maxFavorableExcursion` / `maxAdverseExcursion` - Track best/worst P&L during trade lifetime +- `configSnapshot` (Json) - Stores Position Manager state for crash recovery +- `atr`, `adx`, `rsi`, `volumeRatio`, `pricePosition` - Context metrics from TradingView ## Configuration System @@ -92,36 +109,46 @@ const driftSymbol = normalizeTradingViewSymbol(body.symbol) 7. Add to Position Manager if applicable **Key endpoints:** -- `/api/trading/execute` - Main entry point from n8n (production) +- `/api/trading/execute` - Main entry point from n8n (production, requires auth) +- `/api/trading/check-risk` - Pre-execution validation (duplicate check, quality score, rate limits) - `/api/trading/test` - Test trades from settings UI (no auth required) - `/api/trading/close` - Manual position closing -- `/api/trading/positions` - Query open positions +- `/api/trading/positions` - Query open positions from Drift - `/api/settings` - Get/update config (writes to .env file) +- `/api/analytics/last-trade` - Fetch most recent trade details for dashboard +- `/api/restart` - Create restart flag for watch-restart.sh script ## Critical Workflows ### Execute Trade (Production) ``` -n8n webhook → /api/trading/execute +TradingView alert → n8n Parse Signal Enhanced (extracts metrics) + ↓ /api/trading/check-risk [validates quality score ≥60, checks duplicates] + ↓ /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] + ↓ placeExitOrders() [on-chain TP1/TP2/SL orders] + ↓ calculateQualityScore() [compute 0-100 score from metrics] + ↓ createTrade() [save to database with signalQualityScore] ↓ positionManager.addTrade() [start monitoring] ``` ### Position Monitoring Loop ``` Position Manager every 2s: + ↓ Verify on-chain position still exists (detect external closures) ↓ getPythPriceMonitor().getLatestPrice() - ↓ Calculate current P&L - ↓ Check TP1 hit → closePosition(75%) - ↓ Check TP2 hit → closePosition(100%) + ↓ Calculate current P&L and update peakPrice + ↓ Check emergency stop (-2%) → closePosition(100%) ↓ Check SL hit → closePosition(100%) - ↓ Check dynamic adjustments (breakeven, profit lock) - ↓ addPriceUpdate() [save to database] + ↓ Check TP1 hit → closePosition(75%), move SL to breakeven + ↓ Check profit lock trigger (+1.2%) → move SL to +configured% + ↓ Check TP2 hit → closePosition(80% of remaining), activate runner + ↓ Check trailing stop (if runner active) → adjust SL dynamically based on peakPrice + ↓ addPriceUpdate() [save to database every N checks] + ↓ saveTradeState() [persist Position Manager state for crash recovery] ``` ### Settings Update @@ -234,6 +261,13 @@ docker exec trading-bot-postgres psql -U postgres -d trading_bot_v4 -c "\dt" 6. **Type errors with Prisma:** The Trade type from Prisma is only available AFTER `npx prisma generate` - use explicit types or `// @ts-ignore` carefully +7. **Quality score duplication:** Signal quality calculation exists in BOTH `check-risk` and `execute` endpoints - keep logic synchronized + +8. **Runner configuration confusion:** + - `TAKE_PROFIT_1_SIZE_PERCENT=75` means "close 75% at TP1" (not "keep 75%") + - `TAKE_PROFIT_2_SIZE_PERCENT=80` means "close 80% of REMAINING" (not of original) + - Actual runner size = (100 - TP1%) × (100 - TP2%) / 100 = 5% with defaults + ## File Conventions - **API routes:** `app/api/[feature]/[action]/route.ts` (Next.js 15 App Router) @@ -245,10 +279,24 @@ docker exec trading-bot-postgres psql -U postgres -d trading_bot_v4 -c "\dt" ## 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 +2. **Adding database fields:** Update prisma/schema.prisma → `npx prisma migrate dev` → `npx prisma generate` → 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 +4. **API endpoint changes:** Update both endpoint + corresponding n8n workflow JSON (Check Risk and Execute Trade nodes) 5. **Docker changes:** Rebuild with `docker compose build trading-bot` then restart container +6. **Modifying quality score logic:** Update BOTH `/api/trading/check-risk` and `/api/trading/execute` endpoints +7. **Exit strategy changes:** Modify Position Manager logic + update on-chain order placement in `placeExitOrders()` + +## Development Roadmap + +See `POSITION_SCALING_ROADMAP.md` for planned optimizations: +- **Phase 1 (CURRENT):** Collect data with quality scores (20-50 trades needed) +- **Phase 2:** ATR-based dynamic targets (adapt to volatility) +- **Phase 3:** Signal quality-based scaling (high quality = larger runners) +- **Phase 4:** Direction-based optimization (shorts vs longs have different performance) +- **Phase 5:** Optimize runner size (5% → 10-25%) and trailing stop (0.3% fixed → ATR-based) +- **Phase 6:** ML-based exit prediction (future) + +**Data-driven approach:** Each phase requires validation through SQL analysis before implementation. No premature optimization. ## Integration Points