Critical fix for grid auto-adjustment:
- Recalculate grid bounds (UpperPrice, LowerPrice, GridSpacing) centered
on current price before reinitializing grid levels
- Preserve filled positions during adjustment by saving and restoring
them to the closest new level after reinitialization
- Hold mutex lock for the entire adjustment operation to ensure atomicity
- Add locked variants of calculateDefaultBounds, calculateATRBounds, and
initializeGridLevels to use during adjustment
Without this fix, autoAdjustGrid was using old boundaries when creating
new grid levels, defeating the purpose of auto-adjustment when price
moved significantly.
- New checkGridSkew() detects imbalanced grid
- autoAdjustGrid() reinitializes around current price
- Prevents grid from becoming ineffective after drift
- Triggers when one side is 3x more filled than other
The updateDailyPnL() function was added but never called, leaving
DailyPnL always at 0 and preventing daily loss limit checks from
triggering.
This fix updates DailyPnL and TotalProfit directly in checkAndExecuteStopLoss()
when a stop loss is executed. We update directly rather than calling
updateDailyPnL() because the mutex is already held in that function.
- Add checkDailyLossLimit() function to check if daily loss exceeds limit
- Track daily PnL with auto-reset at midnight
- Pause grid when DailyLossLimitPct exceeded
- Add updateDailyPnL() helper for realized PnL tracking
- Prevent excessive single-day losses
CRITICAL: Add drawdown protection
- New checkMaxDrawdown() function tracks peak equity
- emergencyExit() closes all positions and cancels orders
- Auto-pause grid when MaxDrawdownPct exceeded
- Protect capital from excessive losses
CRITICAL: Detect price breakout from grid range
- New checkBreakout() function to detect upper/lower breakouts
- Auto-pause grid on significant breakout (>2%)
- Cancel all orders when breakout detected
- Prevent continued losses in trending market
- Minor breakouts (1-2%) logged for AI consideration
CRITICAL: Add code-level stop loss protection
- New checkAndExecuteStopLoss() function
- Checks each filled level against StopLossPct
- Automatically closes positions exceeding stop loss
- Called during every grid state sync
CRITICAL: Prevent excessive position accumulation
- New checkTotalPositionLimit() function
- Checks current + pending + new order value
- Rejects orders that would exceed TotalInvestment x Leverage
- Logs clear error messages when limit exceeded
CRITICAL BUG FIX:
- CancelOrder no longer calls CancelAllOrders
- Try exchange-specific CancelOrder if available
- Return error if individual cancellation not supported
- Add error logging for OKX/Bitget API failures (was silently swallowed)
- Fix Lighter position side logic to handle reduce-only orders
- Change verbose debug logs from Infof to Debugf level
- Changed response field from 'data' to 'orders' to match Lighter API
- Updated OrderResponse struct to match Lighter's actual field names
- Fixed field types: price/quantity as strings, is_ask for side
- Fix PostgreSQL placeholder conversion (? to $1, $2...) in all SQL queries
- Fix int4 overflow for timestamp columns (ALTER to BIGINT)
- Fix notional calculation bug in position Close() using proportional entry
- Fix potential panic in DecisionTimestamp with bounds check
- Fix nil pointer dereference in sliceUpTo with defensive checks
- Fix race condition in releaseLock using sync.Once
- Fix UnrealizedPnLPct always 0 in convertPositions
- Improve Sharpe ratio calculation with proper negative return handling
- LoginPage: call completeRegistration instead of verifyOTP when qrCodeURL exists
- This ensures otp_verified is set to true for users completing OTP setup
- Backend: reorder maxUsers check to allow existing incomplete users to continue
- Backend: return OTP info when login with incomplete OTP setup
- Fix milliseconds to seconds conversion in parseCustomTime (AdvancedChart & ChartWithOrders)
- Add GetTraderOrdersFiltered to filter orders at database level by symbol/status
- Increase order limit from 50 to 200 for more historical orders
- Group multiple orders at same candle time and show count (B3, S5, etc.)
- Buy markers shown below bar (green), sell markers above bar (red)
- Use multi-stage build from existing backend/frontend images
- Remove supervisord, use simple shell script
- Single process model: backend runs in background, nginx foreground
- Auto-generate encryption keys on startup
- Add Dockerfile.railway for all-in-one container
- Add railway.toml configuration
- Add railway/nginx.conf and supervisord.conf
- Update README with Deploy on Railway button
- Update Chinese README with deployment instructions
- Convert all time.Time fields to int64 Unix milliseconds (UTC)
- Add PostgreSQL migration to convert timestamp columns to bigint
- Reduce Binance sync window from 7 days to 24 hours
- Fix dashboard trader name visibility (add nofx-text-main color)
- Add position value column to history table
- Remove hardcoded API keys from test files
- Add NowFunc to GORM config for UTC auto-generated timestamps
- Add .UTC() to all time.UnixMilli() calls in trader files
- Add .UTC() to all time.Now() calls in store and api files
- Fix TypeScript unused imports in frontend
- Add DeepVoidBackground component with animated gradient effects
- Apply nofx theme classes to StrategyStudioPage, AITradersPage, etc.
- Update styling for consistent dark theme with gold accents
- Add PageNotFound and TraderDashboardPage components
- RemoveTrader now stops running trader before removing from memory
- handleUpdateTrader auto-restarts trader if it was running before update
- Add debug logs to trace scan_interval_minutes through update/save/load flow