- Add ATR-based dynamic TP2 scaling from 0.7% to 3.0% based on volatility
- New config options: useAtrBasedTargets, atrMultiplierForTp2, minTp2Percent, maxTp2Percent
- Enhanced settings UI with ATR controls and updated risk calculator
- Fix external closure P&L calculation using unrealized P&L instead of volatile current price
- Update execute and test endpoints to use calculateDynamicTp2() function
- Maintain 25% runner system for capturing extended moves (4-5% targets)
- Add environment variables for ATR-based configuration
- Better P&L accuracy for manual position closures
- Fix P&L calculation in Position Manager to use actual entry vs exit price instead of SDK's potentially incorrect realizedPnL
- Calculate actual profit percentage and apply to closed position size for accurate dollar amounts
- Update database record for last trade from incorrect 6.58 to actual .66 P&L
- Update .github/copilot-instructions.md to reflect TP2-as-runner system changes
- Document 25% runner system (5x larger than old 5%) with ATR-based trailing
- Add critical P&L calculation pattern to common pitfalls section
- Mark Phase 5 complete in development roadmap
- Added signalQualityVersion field to Trade model
- Tracks which scoring logic version was used for each trade
- v1: Original logic (price position < 5% threshold)
- v2: Added volume compensation for low ADX
- v3: CURRENT - Stricter logic requiring ADX > 18 for extreme positions (< 15%)
This enables future analysis to:
- Compare performance between logic versions
- Filter trades by scoring algorithm
- Data-driven improvements based on clean datasets
All new trades will be marked as v3. Old trades remain null/v1 for comparison.
**Root Causes:**
1. Auto-flip logic could create phantom trades if close failed
2. Position size mismatches (0.01 SOL vs 11.92 SOL expected) not caught
3. Multiple trades for same symbol+direction in database
**Preventive Measures:**
1. **Startup Validation (lib/startup/init-position-manager.ts)**
- Validates all open trades against Drift positions on startup
- Auto-closes phantom trades with <50% expected size
- Logs size mismatches for manual review
- Prevents Position Manager from tracking ghost positions
2. **Duplicate Position Prevention (app/api/trading/execute/route.ts)**
- Blocks opening same-direction position on same symbol
- Returns 400 error if duplicate detected
- Only allows auto-flip (opposite direction close + open)
3. **Runtime Phantom Detection (lib/trading/position-manager.ts)**
- Checks position size every 2s monitoring cycle
- Auto-closes if size ratio <50% (extreme mismatch)
- Logs as 'manual' exit with AUTO_CLEANUP tx
- Removes from monitoring immediately
4. **Quality Score Fix (app/api/trading/check-risk/route.ts)**
- Hardcoded minScore=60 (removed non-existent config reference)
**Prevention Summary:**
- ✅ Startup validation catches historical phantoms
- ✅ Duplicate check prevents new phantoms
- ✅ Runtime detection catches size mismatches <30s after they occur
- ✅ All three layers work together for defense-in-depth
Issue: User had LONG (phantom) + SHORT (undersized 0.01 SOL vs 11.92 expected)
Fix: Both detected and closed, bot now clean with 0 active trades
- Add SymbolSettings interface with enabled/positionSize/leverage fields
- Implement per-symbol ENV variables (SOLANA_*, ETHEREUM_*)
- Add SOL and ETH sections to settings UI with enable/disable toggles
- Add symbol-specific test buttons (SOL LONG/SHORT, ETH LONG/SHORT)
- Update execute and test endpoints to check symbol enabled status
- Add real-time risk/reward calculator per symbol
- Rename 'Position Sizing' to 'Global Fallback' for clarity
- Fix position manager P&L calculation for externally closed positions
- Fix zero P&L bug affecting 12 historical trades
- Add SQL scripts for recalculating historical P&L data
- Move archive TypeScript files to .archive to fix build
Defaults:
- SOL: 10 base × 10x leverage = 100 notional (profit trading)
- ETH: base × 1x leverage = notional (data collection)
- Global: 10 × 10x for BTC and other symbols
Configuration priority: Per-symbol ENV > Market config > Global ENV > Defaults
- New button in analytics page to clear orphaned trades
- API endpoint /api/trading/clear-manual-closes
- Intelligently checks Drift positions before deleting
- Only removes trades with no matching position or mismatched entry price
- Safe operation: keeps trades on error (false positives better than deletions)
- User-friendly confirmation dialog
- Added getConnection() method to DriftService
- Added proper transaction confirmation in openPosition()
- Check confirmation.value.err to detect on-chain failures
- Return error if transaction fails instead of assuming success
- Prevents phantom trades that never actually execute
This fixes the issue where bot was recording trades with transaction
signatures that don't exist on-chain (like 2gqrPxnvGzdRp56...).
- Added minQualityScore to TradingConfig (default: 60)
- Updated settings UI with slider control (0-100, step 5)
- Updated check-risk endpoint to use config value
- Made scoreSignalQuality function accept minScore parameter
- Updated API to read/write MIN_QUALITY_SCORE env variable
- Allows users to adjust quality threshold from settings page
Added 5 context metrics to alert messages:
- ATR% (volatility as % of price)
- ADX (trend strength)
- RSI (momentum)
- VOL (volume ratio vs 20-bar MA)
- POS (price position in 20-bar range 0-100%)
Changes to Pine Script:
- Always calculate ADX (needed for context even if filter disabled)
- Extract ta.rma() calls outside ternary operators (Pine Script requirement)
- Use alert() instead of alertcondition() for dynamic message support
- Changed to single-line string concatenation for compatibility
Alert message format:
OLD: 'Buy SOL 15 | Profile=Hours ATR=10 Mult=3.0'
NEW: 'SOL buy .P 15 | ATR:1.85 | ADX:28.3 | RSI:62.5 | VOL:1.45 | POS:75.3'
Next: Update n8n to parse these metrics, implement signal quality scoring in bot
- Updated minTimeBetweenTrades config to use minutes instead of seconds
- Changed default from 600 seconds to 10 minutes
- Updated Settings UI label from 'seconds' to 'minutes' and adjusted range (0-60 min)
- Updated .env comments to reflect new unit
- No functional change since cooldown enforcement not yet implemented (TODO in check-risk route)
**ROOT CAUSE:** placeExitOrders() calculated position size using TP/SL prices instead of entry price
**Problem:**
- TP1 order size: 85 / TP1_price (00.746) = 2.914 SOL
- Actual position: 80 / entry_price (99.946) = 3.901 SOL
- TP1 should close: 3.901 * 75% = 2.926 SOL
- But it only closed: 2.914 SOL = 74.7% ❌ WRONG!
**Result:** TP1 closed ~25% instead of 75%, no runner left
**Fix:**
- Changed usdToBase() to use entryPrice for ALL size calculations
- Added entryPrice param to PlaceExitOrdersOptions interface
- Updated all API routes to pass entryPrice
**Testing:** Next trade will have correctly sized TP/SL orders
**Root Cause:** Position Manager didn't detect when on-chain TP/SL orders closed positions externally, causing endless error loops and stale position data.
**Issues Fixed:**
1. Position Manager now checks if on-chain position still exists before attempting to close
2. Detects external closures (by on-chain orders) and updates database accordingly
3. Determines likely exit reason based on price vs TP/SL levels
4. Automatically cancels leftover orders when position detected as closed
5. Analytics now properly shows stopped-out trades
**Technical Changes:**
- Added position existence check at start of checkTradeConditions()
- Calls DriftService.getPosition() to verify on-chain state
- Updates database with exitPrice, exitReason, realizedPnL when external closure detected
- Removes trade from monitoring after external closure
- Handles size mismatches for partial closes (TP1 hit externally)
**Database Fix:**
- Manually closed orphaned trade (stopped out 9 hours ago but still marked 'open')
- Calculated and set realizedPnL = -$12.00 for stopped-out SHORT position
- Analytics now shows 3 total trades instead of missing the SL exit
**Testing:**
- Bot starts cleanly with no error loops
- Position monitoring active with 0 trades (as expected)
- Analytics correctly shows stopped-out trade in statistics
- Updated .env with correct TELEGRAM_BOT_TOKEN and N8N_WEBHOOK_URL
- Added env_file directive to docker-compose.telegram-bot.yml
- Telegram bot now starts successfully with /validate command working
- Implemented trailing stop logic in Position Manager for remaining position after TP2
- Added new ActiveTrade fields: tp2Hit, trailingStopActive, peakPrice
- New config settings: useTrailingStop, trailingStopPercent, trailingStopActivation
- Added trailing stop UI section in settings page with explanations
- Fixed env file parsing regex to support numbers in variable names (A-Z0-9_)
- Settings now persist correctly across container restarts
- Added back arrow navigation on settings page
- Updated all API endpoints and test files with new fields
- Trailing stop activates when runner reaches configured profit level
- SL trails below peak price by configurable percentage
- Fixed Prisma client not being available in Docker container
- Added isTestTrade flag to exclude test trades from analytics
- Created analytics views for net positions (matches Drift UI netting)
- Added API endpoints: /api/analytics/positions and /api/analytics/stats
- Added test trade endpoint: /api/trading/test-db
- Updated Dockerfile to properly copy Prisma client from builder stage
- Database now successfully stores all trades with full details
- Supports position netting calculations to match Drift perpetuals behavior
- Added telegram_command_bot.py with slash commands (/buySOL, /sellBTC, etc)
- Docker compose setup with DNS configuration
- Sends trades as plain text to n8n webhook (same format as TradingView)
- Improved Telegram success message formatting
- Only responds to authorized chat ID (579304651)
- Commands: /buySOL, /sellSOL, /buyBTC, /sellBTC, /buyETH, /sellETH
- Split test trade button into separate LONG and SHORT buttons
- Update testTrade function to accept direction parameter
- Add confirmation dialog showing the specific direction
- Green button for LONG, red button for SHORT
- Success message includes executed direction
- Add PostgreSQL database with Prisma ORM
- Trade model: tracks entry/exit, P&L, order signatures, config snapshots
- PriceUpdate model: tracks price movements for drawdown analysis
- SystemEvent model: logs errors and system events
- DailyStats model: aggregated performance metrics
- Implement dual stop loss system (enabled by default)
- Soft stop (TRIGGER_LIMIT) at -1.5% to avoid wicks
- Hard stop (TRIGGER_MARKET) at -2.5% to guarantee exit
- Configurable via USE_DUAL_STOPS, SOFT_STOP_PERCENT, HARD_STOP_PERCENT
- Backward compatible with single stop modes
- Add database service layer (lib/database/trades.ts)
- createTrade(): save new trades with all details
- updateTradeExit(): close trades with P&L calculations
- addPriceUpdate(): track price movements during trade
- getTradeStats(): calculate win rate, profit factor, avg win/loss
- logSystemEvent(): log errors and system events
- Update execute endpoint to use dual stops and save to database
- Calculate dual stop prices when enabled
- Pass dual stop parameters to placeExitOrders
- Save complete trade record to database after execution
- Add test trade button to settings page
- New /api/trading/test endpoint for executing test trades
- Displays detailed results including dual stop prices
- Confirmation dialog before execution
- Shows entry price, position size, stops, and TX signature
- Generate Prisma client in Docker build
- Update DATABASE_URL for container networking
- Changed from LIMIT to TRIGGER_LIMIT for proper stop loss display
- SL now shows correctly on Drift UI with trigger line
- Added 0.5% buffer on limit price below trigger for safety
- Widened default SL to -2% for safer testing
- Tested and verified all 3 exit orders (TP1, TP2, SL) working
- Add placeExitOrders() to create reduce-only LIMIT orders for TP1, TP2, and SL
- Orders now visible in Drift UI
- Tested with real tiny position (0 base x 5x = 0)
- All 3 exit orders placed successfully on-chain
- Position manager continues monitoring as backup
- Added test script and results documentation
**New Features:**
- Added TAKE_PROFIT_1_SIZE_PERCENT (default: 50%)
- Added TAKE_PROFIT_2_SIZE_PERCENT (default: 50%)
- Users can now control WHAT % to close at each TP level
- Risk calculator now shows actual TP sizes dynamically
**Bug Fixes:**
- Fixed settings save failure by mounting .env file to container
- Added .env volume mount in docker-compose.yml
- Fixed permission issues (.env must be chmod 666)
**UI Changes:**
- Split TP controls into Price % and Size %
- TP1 Price: When to exit first partial
- TP1 Size: What % of position to close (1-100%)
- TP2 Price: When to exit second partial
- TP2 Size: What % of remaining to close (1-100%)
- Risk calculator displays dynamic percentages
**Example:**
- TP1 at +1% price, close 60% of position
- TP2 at +2% price, close 40% of remaining (24% of original)
- Total exit: 84% of position at TP levels
Features:
- Autonomous trading system with Drift Protocol on Solana
- Real-time position monitoring with Pyth price feeds
- Dynamic stop-loss and take-profit management
- n8n workflow integration for TradingView signals
- Beautiful web UI for settings management
- REST API for trade execution and monitoring
- Next.js 15 with standalone output mode
- TypeScript with strict typing
- Docker containerization with multi-stage builds
- PostgreSQL database for trade history
- Singleton pattern for Drift client connection pooling
- BN.js for BigNumber handling (Drift SDK requirement)
- Configurable stop-loss and take-profit levels
- Breakeven trigger and profit locking
- Daily loss limits and trade cooldowns
- Slippage tolerance controls
- DRY_RUN mode for safe testing
- Real-time risk calculator
- Interactive sliders for all parameters
- Live preview of trade outcomes
- Position sizing and leverage controls
- Beautiful gradient design with Tailwind CSS
- POST /api/trading/execute - Execute trades
- POST /api/trading/close - Close positions
- GET /api/trading/positions - Monitor active trades
- GET /api/trading/check-risk - Validate trade signals
- GET /api/settings - View configuration
- POST /api/settings - Update configuration
- Fixed Borsh serialization errors (simplified order params)
- Resolved RPC rate limiting with singleton pattern
- Fixed BigInt vs BN type mismatches
- Corrected order execution flow
- Improved position state management
- Complete setup guides
- Docker deployment instructions
- n8n workflow configuration
- API reference documentation
- Risk management guidelines
- Runs on port 3001 (external), 3000 (internal)
- Uses Helius RPC for optimal performance
- Production-ready with error handling
- Health monitoring and logging