Commit Graph

20 Commits

Author SHA1 Message Date
mindesbunister
881a99242d feat: Add per-symbol trading controls for SOL and ETH
- 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
2025-11-03 10:28:48 +01:00
mindesbunister
0ea8773bdc fix: detect exit reason using trade state flags instead of current price
CRITICAL BUG: Position Manager was using current price to determine exit reason,
but on-chain orders filled at a DIFFERENT price in the past!

Example: LONG entry $184.55, TP1 filled at $184.66, but when Position Manager
checked later (price dropped), it saw currentPrice < TP1 and defaulted to 'SL'

Result: Profitable trades incorrectly labeled as SL exits in database

Fix:
- Use trade.tp1Hit and trade.tp2Hit flags to determine exit reason
- If no TP flags set, use realized P&L to distinguish:
  - Profit >0.5% = TP1 filled
  - Negative P&L = SL filled
- Remove duplicate P&L calculation

This ensures exit reasons match actual on-chain order fills
2025-11-03 00:02:19 +01:00
mindesbunister
ee7558b47c fix: remove duplicate line from MAE/MFE implementation 2025-11-02 23:01:34 +01:00
mindesbunister
12d874ff93 feat: implement MAE/MFE tracking for trade optimization
Added Maximum Favorable/Adverse Excursion tracking:
- Track maxFavorableExcursion: best profit % reached during trade
- Track maxAdverseExcursion: worst loss % reached during trade
- Track maxFavorablePrice and maxAdversePrice
- Update every price check (2s interval)
- Save to database on trade exit for optimization analysis

Benefits:
- Identify if TP levels are too conservative (MFE consistently higher)
- Determine if SL is too tight (MAE < SL but trade recovers)
- Optimize runner size based on how often MFE >> TP2
- Data-driven exit strategy tuning after collecting 10-20 trades

Display in monitoring logs: Shows MFE/MAE % every 20 seconds
2025-11-02 23:00:21 +01:00
mindesbunister
7c18e81164 fix: update on-chain SL orders after TP1 hits
CRITICAL: After TP1 closes 75%, the on-chain stop loss orders were NOT being updated
- Position Manager was tracking new SL price internally but not updating Drift orders
- Old SL orders (e.g., $181.69) remained active even after TP1 at $185.28
- This prevented the 'move SL to breakeven after TP1' logic from working

Fix:
- After TP1 hits, cancel ALL old orders on-chain
- Place new SL orders at updated price (breakeven + configured %)
- Place remaining TP2 order for the 25% runner position
- Maintains dual-stop system if enabled

Result: SL will now actually move up on Drift UI after TP1 fires
2025-11-02 22:41:06 +01:00
mindesbunister
bcd1cd0c76 fix(position-manager): correctly handle partial close size conversions to USD
- Convert closePosition.closedSize (base asset) to USD when updating trade.currentSize
- Fix conversion when position.size detected from Drift: set currentSize = position.size * currentPrice
- Prevent trade.currentSize from being reduced to tiny values due to unit mismatch
2025-11-02 20:34:59 +01:00
mindesbunister
466c0c8001 fix: runner tracking bug - detect TP fills by size reduction
- Position Manager now detects TP1/TP2 fills by monitoring position size reductions instead of entry price mismatches
- When position size reduces by ~75%, marks TP1 as filled and updates currentSize
- When position size reduces by ~95%, marks TP2 as filled and activates trailing stop for 5% runner
- Entry price mismatch check now skipped after TP fills (Drift shows weighted average entry price after partial closes)
- Fixes bug where runners were incorrectly closed after TP1/TP2 fired on-chain
- Adds grace period for new trades (<30s) to avoid false positives during blockchain propagation delays
- This unblocks Phase 1 data collection for signal quality optimization (need 10+ trades with MAE/MFE data)
2025-11-01 20:06:14 +01:00
mindesbunister
056440bf8f feat: add quality score display and timezone fixes
- Add qualityScore to ExecuteTradeResponse interface and response object
- Update analytics page to always show Signal Quality card (N/A if unavailable)
- Fix n8n workflow to pass context metrics and qualityScore to execute endpoint
- Fix timezone in Telegram notifications (Europe/Berlin)
- Fix symbol normalization in /api/trading/close endpoint
- Update Drift ETH-PERP minimum order size (0.002 ETH not 0.01)
- Add transaction confirmation to closePosition() to prevent phantom closes
- Add 30-second grace period for new trades in Position Manager
- Fix execution order: database save before Position Manager.addTrade()
- Update copilot instructions with transaction confirmation pattern
2025-11-01 17:00:37 +01:00
mindesbunister
8a17c2cf90 Fix Position Manager bug: prevent cancelling orders when tracking old trades
Bug: Position Manager was comparing ANY position on the symbol to the trade being
tracked, without verifying entry price match. When a new position opened, it would
think the old tracked trade 'closed externally' and cancel ALL orders - including
the new position's exit orders.

Fix: Added entry price verification (0.5% tolerance). If position entry price doesn't
match the tracked trade, mark the old trade as 'lost tracking' and remove from
monitoring WITHOUT cancelling orders (they belong to the new position).

This prevents the catastrophic scenario where exit orders are repeatedly cancelled,
leaving positions unprotected.
2025-10-31 09:34:48 +01:00
mindesbunister
37ce94d8f1 Restore context metrics in execute endpoint and clean up test files 2025-10-31 09:09:26 +01:00
mindesbunister
25d31ff75a Fix: Save MAE/MFE values when trades exit
Bug: MAE/MFE was tracked in memory during trades but not saved to database on exit
Cause: updateTradeExit() wasn't receiving or saving MAE/MFE parameters

Changes:
- Added MAE/MFE fields to UpdateTradeExitParams interface
- Modified updateTradeExit() to save maxFavorableExcursion, maxAdverseExcursion, maxFavorablePrice, maxAdversePrice
- Updated both updateTradeExit() calls in Position Manager to pass MAE/MFE values
- Enhanced exit logging to show final MAE/MFE percentages

Impact: Future trades will now properly save MAE/MFE data for analytics
Note: Past 2 trades (from before this fix) don't have MAE/MFE saved
2025-10-30 07:37:10 +01:00
mindesbunister
65e6a8efed Phase 1: Add MAE/MFE tracking and analytics schema
- Added 20+ analytics fields to Trade model (MAE/MFE, fill tracking, timing, market context, slippage)
- Implemented real-time MAE/MFE tracking in Position Manager (updates every 5s)
- Enhanced database schema with comprehensive trade analytics
- Updated all API endpoints to initialize MAE/MFE fields
- Modified updateTradeState() to persist MAE/MFE in configSnapshot

Database changes:
- maxFavorableExcursion/maxAdverseExcursion track best/worst profit %
- maxFavorablePrice/maxAdversePrice track exact price levels
- Fill tracking: tp1Filled, tp2Filled, softSlFilled, hardSlFilled
- Timing metrics: timeToTp1, timeToTp2, timeToSl
- Market context: atrAtEntry, adxAtEntry, volumeAtEntry, fundingRateAtEntry, basisAtEntry
- Slippage tracking: expectedEntryPrice, entrySlippagePct, expectedExitPrice, exitSlippagePct

Position Manager changes:
- Track MAE/MFE on every price check (2s interval)
- Throttled database updates (5s interval) via updateTradeMetrics()
- Persist MAE/MFE in trade state snapshots for recovery

Next: Phase 2 (market context capture) or Phase 3 (analytics API)
2025-10-29 20:34:03 +01:00
mindesbunister
d4d2883af6 Fix: Prevent Position Manager from closing runner after on-chain TP2
- Detect on-chain TP2 fills in size mismatch logic and set tp2Hit flag
- Position size thresholds: <30% = TP1, <10% = TP2 (prevents runner from being closed)
- Ensures runner (5-20%) trails properly instead of being market-closed immediately
2025-10-29 20:04:33 +01:00
mindesbunister
f7cf9ec63b Fix database race condition and Drift initialization errors
- Remove saveTradeState() call from addTrade() to avoid P2025 error
- Add initialization check in checkTradeConditions() to skip when Drift not ready
- Silence 'not initialized' errors during startup (expected behavior)
- Trade state is now saved only by API endpoint after DB record created
2025-10-29 16:00:06 +01:00
mindesbunister
fe4d9bc954 Fix: Calculate P&L correctly for external closures
- Save currentSize before it becomes 0 in external closure detection
- Use sizeBeforeClosure for P&L calculation instead of trade.currentSize
- Prevents /bin/bash.00 P&L for TP2 exits when position closes externally
- Ensures win/loss analytics counts TP trades correctly
2025-10-28 20:10:38 +01:00
mindesbunister
19f5b7ab14 Fix: Critical Position Manager monitoring issues
**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
2025-10-28 07:51:40 +01:00
mindesbunister
9bf83260c4 Add /close command and auto-flip logic with order cleanup
- Added /close Telegram command for full position closure
- Updated /reduce to accept 10-100% (was 10-90%)
- Implemented auto-flip logic: automatically closes opposite position when signal reverses
- Fixed risk check to allow opposite direction trades (signal flips)
- Enhanced Position Manager to cancel orders when removing trades
- Added startup initialization for Position Manager (restores trades on restart)
- Fixed analytics to show stopped-out trades (manual DB update for orphaned trade)
- Updated reduce endpoint to route 100% closes through closePosition for proper cleanup
- All position closures now guarantee TP/SL order cancellation on Drift
2025-10-27 23:27:48 +01:00
mindesbunister
4ae9c38ad8 Add trailing stop feature for runner position + fix settings persistence
- 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
2025-10-27 12:11:10 +01:00
mindesbunister
d3c04ea9c9 feat: Position Manager persistence + order cleanup + improved stop loss
- Add Position Manager state persistence to survive restarts
  - Auto-restore open trades from database on startup
  - Save state after TP1, SL adjustments, profit locks
  - Persist to configSnapshot JSON field

- Add automatic order cancellation
  - Cancel all TP/SL orders when position fully closed
  - New cancelAllOrders() function in drift/orders.ts
  - Prevents orphaned orders after manual closes

- Improve stop loss management
  - Move SL to +0.35% after TP1 (was +0.15%)
  - Gives more breathing room for retracements
  - Still locks in half of TP1 profit

- Add database sync when Position Manager closes trades
  - Auto-update Trade record with exit data
  - Save P&L, exit reason, hold time
  - Fix analytics showing stale data

- Add trade state management functions
  - updateTradeState() for Position Manager persistence
  - getOpenTrades() for startup restoration
  - getInitializedPositionManager() for async init

- Create n8n database analytics workflows
  - Daily report workflow (automated at midnight)
  - Pattern analysis (hourly/daily performance)
  - Stop loss effectiveness analysis
  - Database analytics query workflow
  - Complete setup guide (N8N_DATABASE_SETUP.md)
2025-10-27 10:39:05 +01:00
mindesbunister
2405bff68a feat: Complete Trading Bot v4 with Drift Protocol integration
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
2025-10-24 14:24:36 +02:00