Commit Graph

94 Commits

Author SHA1 Message Date
21in7
42e53b9ae4 perf: optimize kill switch - tail-read only last N lines on boot
- Replace full file scan with _tail_lines() that reads from EOF
- Only load max(FAST_KILL=8, SLOW_KILL=15) lines on boot
- Trim in-memory trade history to prevent unbounded growth
- No I/O bottleneck regardless of history file size

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:50:17 +09:00
21in7
4930140b19 feat: add dual-layer kill switch (Fast Kill + Slow Kill)
- Fast Kill: 8 consecutive net losses → block new entries for symbol
- Slow Kill: last 15 trades PF < 0.75 → block new entries for symbol
- Trade history persisted to data/trade_history/{symbol}.jsonl (survives restart)
- Boot-time retrospective check restores kill state from history
- Manual reset via RESET_KILL_SWITCH_{SYMBOL}=True in .env + restart
- Entry blocked, exits (SL/TP/manual) always work normally
- Discord alert on kill switch activation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:48:52 +09:00
21in7
5b3f6af13c feat: add symbol comparison and position sizing analysis tools
- Add payoff_ratio and max_consecutive_losses to backtester summary
- Add compare_symbols.py: per-symbol parameter sweep for candidate evaluation
- Add position_sizing_analysis.py: robust Monte Carlo position sizing
- Fetch historical data for SOL, LINK, AVAX candidates (365 days)
- Update existing symbol data (XRP, TRX, DOGE)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 23:35:42 +09:00
21in7
55c20012a3 feat: add per-symbol strategy params with sweep-optimized values
Support per-symbol strategy parameters (ATR_SL_MULT_XRPUSDT, etc.)
via env vars, falling back to global defaults. Sweep results:
- XRPUSDT: SL=1.5 TP=4.0 ADX=30 (PF 2.39, Sharpe 61.0)
- TRXUSDT: SL=1.0 TP=4.0 ADX=30 (PF 3.87, Sharpe 62.8)
- DOGEUSDT: SL=2.0 TP=2.0 ADX=30 (PF 1.80, Sharpe 44.1)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:28:14 +09:00
21in7
106eaf182b fix: accumulate partial fills in UserDataStream for accurate PnL
MARKET orders can fill in multiple trades (PARTIALLY_FILLED → FILLED).
Previously only the last fill's rp/commission was captured, causing
under-reported PnL. Now accumulates rp and commission across all
partial fills per order_id and sums them on final FILLED event.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 13:47:34 +09:00
21in7
64f56806d2 fix: resolve 6 warning issues from code review
5. Add daily PnL reset loop — UTC midnight auto-reset via
   _daily_reset_loop in main.py, prevents stale daily_pnl accumulation
6. Fix set_base_balance race condition — call once in main.py before
   spawning bots, instead of each bot calling independently
7. Remove realized_pnl != 0 from close detection — prevents entry
   orders with small rp values being misclassified as closes
8. Rename xrp_btc_rs/xrp_eth_rs → primary_btc_rs/primary_eth_rs —
   generic column names for multi-symbol support (dataset_builder,
   ml_features, and tests updated consistently)
9. Replace asyncio.get_event_loop() → get_running_loop() — fixes
   DeprecationWarning on Python 3.10+
10. Parallelize candle preload — asyncio.gather for all symbols
    instead of sequential REST calls, ~3x faster startup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:44:40 +09:00
21in7
8803c71bf9 fix: resolve 4 critical bugs from code review
1. Margin ratio calculated on per_symbol_balance instead of total balance
   — previously amplified margin reduction by num_symbols factor
2. Replace Algo Order API (algoType=CONDITIONAL) with standard
   futures_create_order for SL/TP — algo API is for VP/TWAP, not
   conditional orders; SL/TP may have silently failed
3. Fallback PnL (SYNC close) now sums all recent income rows instead
   of using only the last entry — prevents daily_pnl corruption in
   multi-fill scenarios
4. Explicit state transition in _close_and_reenter — clear local
   position state after close order to prevent race with User Data
   Stream callback on position count

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 22:39:51 +09:00
21in7
805f1b0528 fix: fetch actual PnL from Binance income API on SYNC close detection
When the position monitor detects a missed close via API fallback, it
now queries Binance futures_income_history to get the real realized PnL
and commission instead of logging zeros. Exit price is estimated from
entry price + PnL/quantity. This ensures the dashboard records accurate
profit data even when WebSocket events are missed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 12:43:27 +09:00
21in7
363234ac7c fix: add fallback position sync check to detect missed WebSocket closes
The position monitor now checks Binance API every 5 minutes to verify
the bot's internal state matches the actual position. If the bot thinks
a position is open but Binance shows none, it syncs state and logs a
SYNC close event. This prevents the bot from being stuck in a phantom
position when User Data Stream misses a TP/SL fill event.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 12:41:23 +09:00
21in7
c6c60b274c fix: use dynamic quantity/price precision per symbol from exchange info
Hardcoded round(qty, 1) caused -1111 Precision errors for TRXUSDT and
DOGEUSDT (stepSize=1, requires integers). Now lazily loads quantityPrecision
and pricePrecision from Binance futures_exchange_info per symbol. SL/TP
prices also use symbol-specific precision instead of hardcoded 4 decimals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 13:07:23 +09:00
21in7
60510c026b fix: resolve critical/important bugs from code review (#1,#2,#4,#5,#6,#8)
- #1: OI division by zero — already fixed (prev_oi == 0.0 guard exists)
- #2: cumulative trade count used max() instead of sum(), breaking ML trigger
- #4: fetch_history API calls now retry 3x with exponential backoff
- #5: parquet upsert now deduplicates timestamps before sort
- #6: record_pnl() is now async with Lock for multi-symbol safety
- #8: exit_price=0.0 skips close handling with warning log

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 03:06:48 +09:00
21in7
0a8748913e feat: add signal score detail to bot logs for HOLD reason debugging
get_signal() now returns (signal, detail) tuple with long/short scores,
ADX value, volume surge status, and HOLD reason for easier diagnosis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 02:20:44 +09:00
21in7
072910df39 fix: WalkForward ignores use_ml=False flag, always injecting trained models
WF backtester always passed trained models to Backtester.run(ml_models=...),
overriding ml_filters even when use_ml=False. This caused 0 trades in
--no-ml mode because underfitted models (trained on ~27 samples) blocked
all entries with proba < 0.55 threshold.

- Skip model training when use_ml=False (saves computation)
- Only inject ml_models when use_ml=True

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:19:27 +09:00
21in7
89f44c96af fix: resolve ML filter dtype error and missing BTC/ETH correlation features
- Fix LightGBM predict_proba ValueError by filtering FEATURE_COLS and casting to float64
- Extract BTC/ETH correlation data from embedded parquet columns instead of missing separate files
- Disable ONNX priority in ML filter tests to use mocked LightGBM correctly
- Add NO_ML_FILTER=true to .env.example (ML adds no value with current signal thresholds)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:08:23 +09:00
21in7
dbc900d478 chore: trigger bot rebuild 2026-03-06 23:59:29 +09:00
21in7
02e41881ac feat: strategy parameter sweep and production param optimization
- Add independent backtest engine (backtester.py) with walk-forward support
- Add backtest sanity check validator (backtest_validator.py)
- Add CLI tools: run_backtest.py, strategy_sweep.py (with --combined mode)
- Fix train-serve skew: unify feature z-score normalization (ml_features.py)
- Add strategy params (SL/TP ATR mult, ADX filter, volume multiplier) to
  config.py, indicators.py, dataset_builder.py, bot.py, backtester.py
- Fix WalkForwardBacktester not propagating strategy params to test folds
- Update production defaults: SL=2.0x, TP=2.0x, ADX=25, Vol=2.5
  (3-symbol combined PF: 0.71 → 1.24, MDD: 65.9% → 17.1%)
- Retrain ML models with new strategy parameters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 23:39:43 +09:00
21in7
15fb9c158a feat: add multi-symbol dashboard support (parser, API, UI)
- Add [SYMBOL] prefix to all bot/user_data_stream log messages
- Rewrite log_parser.py with multi-symbol regex, per-symbol state tracking, symbol columns in DB schema
- Rewrite dashboard_api.py with /api/symbols endpoint, symbol query params on all endpoints, SQL injection fix
- Update App.jsx with symbol filter tabs, multi-position display, dynamic header
- Add tests for log parser (8 tests) and dashboard API (7 tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:19:16 +09:00
21in7
ae5692cde4 feat: MLFilter falls back to models/ root if symbol-specific dir not found
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:14:38 +09:00
21in7
e7620248c7 feat: TradingBot accepts symbol and shared RiskManager, removes config.symbol dependency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:13:04 +09:00
21in7
2e09f5340a feat: exchange client accepts explicit symbol parameter, removes config.symbol dependency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:07:44 +09:00
21in7
9318fb887e feat: shared RiskManager with async lock, same-direction limit, per-symbol tracking
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:06:41 +09:00
21in7
7aef391b69 feat: add multi-symbol config (symbols list, correlation_symbols, max_same_direction)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 23:05:22 +09:00
21in7
39e55368fd feat: log technical indicators on entry and add dashboard docs to README
- Include RSI, MACD_H, ATR in bot entry log so the log parser can
  extract and store them in the trades DB for dashboard display
- Update log parser regex and _handle_entry() to persist indicator values
- Add dashboard section to README (tech stack, screens, API endpoints)
- Add dashboard/ directory to project structure in README

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 21:08:16 +09:00
21in7
565414c5e0 feat: add trading dashboard with API and UI components
- Introduced a new trading dashboard consisting of a FastAPI backend (`dashboard-api`) for data retrieval and a React frontend (`dashboard-ui`) for visualization.
- Implemented a log parser to monitor and store bot logs in an SQLite database.
- Configured Docker setup for both API and UI, including necessary Dockerfiles and a docker-compose configuration.
- Added setup documentation for running the dashboard and accessing its features.
- Enhanced the Jenkins pipeline to build and push the new dashboard images.
2026-03-05 20:25:45 +09:00
21in7
c555afbddc chore: update .gitignore, CLAUDE.md, training_log.json, and ml_filter.py
- Added .worktrees/ to .gitignore to prevent tracking of worktree files.
- Marked `optuna-precision-objective-plan` as completed in CLAUDE.md.
- Added new training log entry for a LightGBM model with updated parameters and performance metrics in training_log.json.
- Updated error handling in ml_filter.py to return False on prediction errors instead of True, improving the robustness of the ML filter.
2026-03-05 20:04:15 +09:00
21in7
448b3e016b feat: add OI history deque, cold start init, and derived features to bot runtime
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:17:37 +09:00
21in7
ff9e639142 feat: add OI derived features (oi_change_ma5, oi_price_spread) to dataset builder and ML features
Add two new OI-derived features to improve ML model's market microstructure
understanding:
- oi_change_ma5: 5-candle moving average of OI change rate (short-term trend)
- oi_price_spread: z-scored OI minus z-scored price return (divergence signal)

Both features use 96-candle rolling z-score window. FEATURE_COLS expanded from
24 to 26. Existing tests updated to reflect new feature counts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:07:40 +09:00
21in7
9c6f5dbd76 feat: remove ADX hard filter from dataset builder, add ADX as ML feature
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 21:17:49 +09:00
21in7
0aeb15ecfb feat: remove ADX hard filter, delegate to ML
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 21:14:50 +09:00
21in7
0b18a0b80d feat: add ADX as 24th ML feature for trend strength learning
Migrate ADX from hard filter (ADX < 25 blocks entry) to ML feature so
the model can learn optimal ADX thresholds from data. Updates FEATURE_COLS,
build_features(), and corresponding tests from 23 to 24 features.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 21:11:04 +09:00
21in7
a33283ecb3 feat: add position monitor logging with real-time price tracking
Log current price and unrealized PnL every 5 minutes while holding a position,
using the existing kline WebSocket's unclosed candle data for real-time price updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 20:36:46 +09:00
21in7
292ecc3e33 feat: update ML threshold and configuration for improved model performance
- Added ML_THRESHOLD to .env.example and updated Config class to include ml_threshold with a default value of 0.55.
- Modified MLFilter initialization in bot.py to utilize the new ml_threshold configuration.
- Updated Jenkinsfile to change the registry URL for Docker image management.

These changes enhance the model's adaptability by allowing for a configurable machine learning threshold, improving overall performance.
2026-03-03 20:09:03 +09:00
21in7
0af138d8ee feat: add stratified_undersample helper function
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:58:15 +09:00
21in7
b7ad358a0a fix: make HOLD negative sampling tests non-vacuous
The two HOLD negative tests (test_hold_negative_labels_are_all_zero,
test_signal_samples_preserved_after_sampling) were passing vacuously
because sample_df produces 0 signal candles (ADX ~18, below threshold
25). Added signal_producing_df fixture with higher volatility and volume
surges to reliably generate signals. Removed if-guards so assertions
are mandatory. Also restored the full docstring for
generate_dataset_vectorized() documenting btc_df/eth_df,
time_weight_decay, and negative_ratio parameters.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:45:10 +09:00
21in7
8e56301d52 feat: add HOLD negative sampling to dataset_builder
Add negative_ratio parameter to generate_dataset_vectorized() that
samples HOLD candles as label=0 negatives alongside signal candles.
This increases training data from ~535 to ~3,200 samples when enabled.

- Split valid_rows into base_valid (shared) and sig_valid (signal-only)
- Add 'source' column ("signal" vs "hold_negative") for traceability
- HOLD samples get label=0 and random 50/50 side assignment
- Default negative_ratio=0 preserves backward compatibility
- Fix incorrect column count assertion in existing test

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 23:34:45 +09:00
21in7
eeb5e9d877 feat: add ADX filter to block sideways market entries
ADX < 25 now returns HOLD in get_signal(), preventing entries during
trendless (sideways) markets. NaN ADX values fall through to existing
weighted signal logic. Also syncs the vectorized dataset builder with
the same ADX filter to keep training data consistent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:55:12 +09:00
21in7
c8a2c36bfb feat: add ADX calculation to indicators
Add ADX (Average Directional Index) with period 14 to calculate_all()
for sideways market filtering. Includes test verifying the adx column
exists and contains non-negative values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:47:18 +09:00
21in7
b8b99da207 feat: update training log and enhance ML filter functionality
- Added a new entry to the training log with detailed metrics for a LightGBM model, including AUC, precision, recall, and tuned parameters.
- Enhanced the MLFilter class to include a guard clause that prevents execution if the filter is disabled, improving robustness.
2026-03-02 18:24:38 +09:00
21in7
77590accf2 feat: add architecture documentation for CoinTrader
- Introduced a comprehensive architecture document detailing the CoinTrader system, including an overview, core layer architecture, MLOps pipeline, and key operational scenarios.
- Updated README to reference the new architecture document and added a configuration option to disable the ML filter.
- Enhanced the ML filter to allow for complete signal acceptance when the NO_ML_FILTER environment variable is set.
2026-03-02 18:02:05 +09:00
21in7
a8cba2cb4c docs: enhance README with detailed listenKey auto-renewal process and error handling
- Updated the README to clarify the listenKey auto-renewal mechanism, including the use of `stream.recv()` for message reception.
- Added information on immediate reconnection upon detecting internal error payloads to prevent zombie connections.
2026-03-02 16:43:45 +09:00
21in7
05ae88dc61 fix: remove manual listenKey mgmt, add symbol filter, fix reenter race condition
Made-with: Cursor
2026-03-02 16:31:40 +09:00
21in7
4e8e61b5cf fix: guard against None current_trade_side in _calc_estimated_pnl
Made-with: Cursor
2026-03-02 16:27:17 +09:00
21in7
4ffee0ae8b feat: run UserDataStream in parallel with candle stream
Made-with: Cursor
2026-03-02 16:25:13 +09:00
21in7
7e7f0f4f22 fix: restore entry_price and entry_quantity on position recovery
Made-with: Cursor
2026-03-02 16:24:27 +09:00
21in7
c4f806fc35 feat: add entry state tracking and _on_position_closed callback
- __init__에 _entry_price, _entry_quantity 상태 변수 추가 (None 초기화)
- _open_position()에서 current_trade_side 저장 직후 진입가/수량 저장
- _calc_estimated_pnl() 헬퍼: LONG/SHORT 방향별 예상 PnL 계산
- _on_position_closed() 콜백: UDS 청산 감지 시 PnL 기록·알림·상태 초기화

Made-with: Cursor
2026-03-02 16:21:59 +09:00
21in7
22f1debb3d fix: re-raise CancelledError in UserDataStream for proper task cancellation
Made-with: Cursor
2026-03-02 16:20:37 +09:00
21in7
4f3183df47 feat: add UserDataStream with keepalive and reconnect loop
Made-with: Cursor
2026-03-02 16:17:38 +09:00
21in7
223608bec0 refactor: remove duplicate pnl/notify from _close_position (handled by callback)
Made-with: Cursor
2026-03-02 16:16:25 +09:00
21in7
e72126516b feat: extend notify_close with close_reason, net_pnl, diff fields
Made-with: Cursor
2026-03-02 16:14:26 +09:00
21in7
63c2eb8927 feat: add listenKey create/keepalive/delete methods to exchange
Made-with: Cursor
2026-03-02 16:11:33 +09:00