Shows real-time portfolio metrics:
- Cash Balance
- Position (quantity and value)
- Entry Price / Current Price
- Unrealized P&L
- Total Value
- P&L (absolute and percentage)
Updates as simulation runs and trades are executed.
- Tests now run with candle_delay=0 for fast execution
- Simulation defaults to candle_delay based on interval (e.g., 30s for 1m)
- Progress saved to DB every 5 seconds during simulation
- User can now see real-time updates while simulation runs
Tests: 14 passing in 0.15s
test_full_simulation_workflow_generates_signals_and_trades:
- Creates klines with clear price movements
- Uses very low threshold (0.1%) to ensure signals generated
- Verifies signals are NOT empty
- Verifies trade_log is NOT empty
- Verifies BUY signals are generated
- Verifies results contain signals and trade_log
This test ensures the simulation engine is working correctly.
Shows what happened at each candle:
- BUY/SELL/HOLD actions
- Price at that time
- Reason for action
- Entry price for positions
Trade log is stored in DB and displayed in frontend.
- Use ResizeObserver to handle width changes
- Use tick() to ensure DOM is ready before drawing
- Access reactive values in effect to trigger on changes
- Fixed canvas sizing to use percentage width
The simulation engine completes in seconds, but the background task wasn't
saving klines to DB. Now we fetch klines first synchronously so the user
can see the price chart immediately after starting a simulation.
Also marks any stuck 'running' simulations as 'stopped'.
Unit tests (13 passing):
- Kline fetching and processing
- Price drop condition triggers buy
- Stop loss and take profit risk management
- Multiple positions (buy again after sell)
- Max candles limit
- Stop interruption handling
Frontend:
- SignalChart now shows price movement even before signals
- Shows candle count even with no signals
- Chart displays buy/sell markers when signals exist
- Canvas-based chart with gradient fill
Backend:
- Simulation stores klines for chart display
- Returns klines in API response
- Simplified simulation run (no periodic saving)
- Fetch historical klines once from AVE API (10 CU per request)
- Process each candle as a time step
- Limit to 500 candles max per simulation
- No continuous polling - processes all data in seconds
- Frontend now selects kline interval (1m, 5m, 15m, 1h)
- Much more efficient API usage
- Status was only updated if engine was in memory (race condition)
- Now always sets status to 'stopped' in DB
- Returns 'stopped' instead of 'stopping'
- Cleaned up 3 stale running simulations in DB
Frontend:
- Load simulations now prioritizes running simulation over most recent
- Clear signals before loading new simulation
Backend:
- When starting new simulation, stop any existing running simulation first
- Previously would return existing running simulation (confusing UX)
- No duration limit - runs forever until user stops
- Only 1 running simulation per bot (returns existing if already running)
- Always paper trade (no auto-execute option)
- Removed Pro upgrade banner
- Removed duration and auto-execute config options
- Simplified API to only require token, chain, check_interval
Frontend:
- Added duration selector (1min, 5min, 10min, 30min)
- Added check interval selector (10s, 30s, 60s)
Backend:
- Signals are now saved to database every 30 seconds during simulation
- Can stop simulation early to see partial signals
The AVE API returns status: 1 for success, not status: 200.
This was causing get_token_price to always return None, resulting
in no signals being generated during simulation.
Like the backtest page, simulation now extracts the token from the
bot's strategy config instead of requiring user input. Shows token
name and truncated address.
Backend:
- Added pagination to /trades endpoint with page and per_page params
- Returns paginated trades with metadata (page, total_pages, has_next, has_prev)
Frontend:
- Added pagination controls for trade history (Prev/Next buttons)
- Shows current page info and total trades
- Trades are loaded on-demand when expanded
API changes:
- GET /bots/{id}/backtest/{runId}/trades?page=1&per_page=5
- Response includes: trades, total_trades, page, per_page, total_pages, has_next, has_prev
Add test case that ensures when stop loss is triggered after multiple
DCA buys with decreasing prices, the final balance is always less
than the initial balance.
Three bugs fixed:
1. **Weighted average entry price for risk management**:
- Previously, entry_price was overwritten on each buy, causing stop loss
to be calculated from the latest buy price instead of average
- Added cost_basis tracking and average_entry_price property
- Stop loss now correctly uses weighted average across all buys
2. **Portfolio value accumulation in _calculate_metrics**:
- Bug: running_position = trade['quantity'] was OVERWRITING position
- Fix: running_position += trade['quantity'] to properly accumulate DCA
3. **Risk management exit reset**:
- Added cost_basis reset when position is closed
Max drawdown is now correctly bounded by stop loss percentage (~5%)
instead of showing inflated values like 59%.
Two bugs fixed:
1. final_balance was incorrectly calculated as balance + balance when position=0 due to expression structure
2. take_profit check needed epsilon for floating point precision (95 * 1.10 = 104.50000000000001 instead of 104.5)
Bug: The expression was evaluating incorrectly due to operator precedence:
final_balance = balance + (position * price if condition else balance)
When condition=False (position=0), this became: balance + balance = 2x balance!
Fixed by restructuring to if/else block.
The bug was that running_balance was set to trade['amount'] which is
the amount SPENT on a buy (not remaining balance), causing inflated
portfolio values and incorrect max drawdown calculation.
Now properly tracks:
- After BUY: balance decreases by amount spent
- After SELL: balance increases by amount received
- Track last_kline_price during kline processing
- Use last_kline_price instead of entry price for open position valuation
- Add final marked-to-market value to portfolio_values for max_drawdown calculation
- This fixes the issue where max_drawdown exceeded stop_loss percentage