78 Commits

Author SHA1 Message Date
21in7
f488720ca2 fix: MTF bot code review — conditional slicing, caching, tests
- Add _remove_incomplete_candle() for timestamp-based conditional
  slicing on both 15m and 1h data (replaces hardcoded [:-1])
- Add MetaFilter indicator caching to eliminate 3x duplicate calc
- Fix notifier encapsulation (_send → notify_info public API)
- Remove DataFetcher.poll_update() dead code
- Fix evaluate_oos.py symbol typo (xrpusdtusdt → xrpusdt)
- Add 20 pytest unit tests for MetaFilter, TriggerStrategy,
  ExecutionManager, and _remove_incomplete_candle

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 11:11:26 +09:00
21in7
17742da6af feat: add algo order compatibility and orphan order cleanup
- exchange.py: cancel_all_orders() now cancels both standard and algo orders
- exchange.py: get_open_orders() merges standard + algo orders
- exchange.py: cancel_order() falls back to algo cancel on failure
- bot.py: store SL/TP prices for price-based close_reason re-determination
- bot.py: add _cancel_remaining_orders() for orphan SL/TP cleanup
- bot.py: re-classify MANUAL close_reason as SL/TP via price comparison
- bot.py: cancel orphan orders on startup when no position exists
- tests: fix env setup for testnet config and ML filter mocking
- docs: add backtest market context and algo order fix design specs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 20:46:59 +09:00
21in7
c29d3e0569 feat(ml): add purged gap (embargo=24) to walk-forward + ablation CLI
- Add LOOKAHEAD embargo between train/val splits in all 3 WF functions
  to prevent label leakage from 6h lookahead window
- Add --ablation flag to train_model.py for signal_strength/side
  dependency diagnosis (A/B/C experiment with drop analysis)
- Criteria: A→C drop ≤0.05=good, 0.05-0.10=conditional, ≥0.10=redesign

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:42:51 +09:00
21in7
30ddb2fef4 feat(ml): relax training thresholds for 5-10x more training samples
Add TRAIN_* constants (signal_threshold=2, adx=15, vol_mult=1.5, neg_ratio=3)
as dataset_builder defaults. Remove hardcoded negative_ratio=5 from all callers.
Bot entry conditions unchanged (config.py strict values).

WF 5-fold results (all symbols AUC 0.91+):
- XRPUSDT: 0.9216 ± 0.0052
- SOLUSDT:  0.9174 ± 0.0063
- DOGEUSDT: 0.9222 ± 0.0085

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:38:15 +09:00
21in7
fe99885faa fix(ml): align dataset_builder default SL/TP with config (2.0/2.0)
Module-level ATR_SL_MULT was 1.5, now 2.0 to match config.py and CLI defaults.
This ensures generate_dataset_vectorized() produces correct labels even without
explicit parameters.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 18:43:09 +09:00
21in7
c0da46c60a chore: deprecate legacy dataset generation, remove stale comparison test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 18:38:07 +09:00
21in7
5bad7dd691 refactor(ml): add MLFilter.from_model(), fix validator initial_balance
- MLFilter.from_model() classmethod eliminates brittle __new__() private-attribute
  manipulation in backtester walk-forward model injection
- backtest_validator._check_invariants() now accepts cfg and uses cfg.initial_balance
  instead of a hardcoded 1000.0 for the negative-balance invariant check
- backtester.py walk-forward injection block simplified to use the new factory method

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 18:36:30 +09:00
21in7
24f0faa540 fix(mlx): remove double normalization in walk-forward validation
Add normalize=False parameter to MLXFilter.fit() so external callers
can skip internal normalization. Remove the external normalization +
manual _mean/_std reset hack from walk_forward_auc() in train_mlx_model.py.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 18:31:11 +09:00
21in7
0fe87bb366 fix(backtest): include unrealized PnL in equity curve for accurate MDD
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 18:26:09 +09:00
21in7
75d1af7fcc feat(ml): parameterize SL/TP multipliers in dataset_builder
Add atr_sl_mult and atr_tp_mult parameters to _calc_labels_vectorized
and generate_dataset_vectorized, defaulting to existing constants (1.5,
2.0) for full backward compatibility. Callers (train scripts, backtester)
can now pass symbol-specific multipliers without modifying module-level
constants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 18:03:24 +09:00
21in7
41b0aa3f28 fix: address code review round 2 — 9 issues (2 critical, 3 important, 4 minor)
Critical:
- #2: Add _entry_lock in RiskManager to serialize concurrent entry (balance race)
- #3: Add startTime to get_recent_income + record _entry_time_ms (SYNC PnL fix)

Important:
- #1: Add threading.Lock + _run_api() helper for thread-safe Client access
- #4: Convert reset_daily to async with lock
- #8: Add 24h TTL to exchange_info_cache

Minor:
- #7: Remove duplicate Indicators creation in _open_position (use ATR directly)
- #11: Add input validation for LEVERAGE, MARGIN ratios, ML_THRESHOLD
- #12: Replace hardcoded corr[0]/corr[1] with dict-based dynamic access
- #14: Add fillna(0.0) to LightGBM path for NaN consistency with ONNX

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 17:26:15 +09:00
21in7
9f0057e29d fix(dashboard): address code review — auth, DB stability, idempotency, UI fixes
C1: /api/reset에 API key 인증 추가 (DASHBOARD_RESET_KEY 환경변수)
C2: /proc 스캐닝 제거, PID file + SIGHUP 기반 파서 재파싱으로 교체
C3: daily_pnl 업데이트를 trades 테이블에서 재계산하여 idempotent하게 변경
I1: CORS origins를 CORS_ORIGINS 환경변수로 설정 가능하게 변경
I2: offset 파라미터에 ge=0 검증 추가
I3: 매 줄 commit → 파일 단위 배치 commit으로 성능 개선
I4: _pending_candles 크기 제한으로 메모리 누적 방지
I5: bot.log glob 중복 파싱 제거 (sorted(set(...)))
I6: /api/health 에러 메시지에서 내부 경로 미노출
I7: RSI 차트(데이터 없음)를 OI 변화율 차트로 교체
M1: pnlColor 변수 shadowing 수정 (posPnlColor)
M2: 거래 목록에 API total 필드 사용
M3: dashboard/ui/.dockerignore 추가
M4: API Dockerfile Python 3.11→3.12
M5: 테스트 fixture에서 temp DB cleanup 추가
M6: 누락 테스트 9건 추가 (health, daily, reset 인증, offset, pagination)
M7: 파서 SIGTERM graceful shutdown + entrypoint.sh signal forwarding
DB: 양쪽 busy_timeout=5000 + WAL pragma 설정

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 00:00:16 +09:00
21in7
181f82d3c0 fix: address critical code review issues (PnL double recording, sync HTTP, race conditions)
- fix(bot): prevent PnL double recording in _close_and_reenter using asyncio.Event
- fix(bot): prevent SYNC detection PnL duplication with _close_handled_by_sync flag
- fix(notifier): move sync HTTP call to background thread via run_in_executor
- fix(risk_manager): make is_trading_allowed async with lock for thread safety
- fix(exchange): cache exchange info at class level (1 API call for all symbols)
- fix(exchange): use `is not None` instead of truthy check for price/stop_price
- refactor(backtester): extract _calc_trade_stats to eliminate code duplication
- fix(ml_features): apply rolling z-score to OI/funding rate in serving (train-serve skew)
- fix(bot): use config.correlation_symbols instead of hardcoded BTCUSDT/ETHUSDT
- fix(bot): expand OI/funding history deque to 96 for z-score window
- cleanup(config): remove unused stop_loss_pct, take_profit_pct, trailing_stop_pct fields

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:03:52 +09:00
21in7
652990082d fix(weekly-report): calculate combined metrics directly from trades
The combined summary (PF, MDD, win_rate) was indirectly reconstructed
from per-symbol averages using round(win_rate * n), which introduced
rounding errors. MDD was max() of individual symbol MDDs, ignoring
simultaneous drawdowns across the correlated crypto portfolio.

Now computes all combined metrics directly from the trade list:
- PF: sum(wins) / sum(losses) from actual trade PnLs
- MDD: portfolio equity curve from time-sorted trades
- Win rate: direct count from trade PnLs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 20:12:42 +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
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
9b76313500 feat: add quantstats HTML report to weekly strategy report
- generate_quantstats_report() converts backtest trades to daily returns
  and generates full HTML report (Sharpe, Sortino, drawdown chart, etc.)
- Weekly report now saves report_YYYY-MM-DD.html alongside JSON
- Added quantstats to requirements.txt
- 2 new tests (HTML generation + empty trades handling)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 03:14:30 +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
2a767c35d4 feat(weekly-report): implement weekly report generation with live trade data and performance tracking
- Added functionality to fetch live trade data from the dashboard API.
- Implemented weekly report generation that includes backtest results, live trade statistics, and performance trends.
- Enhanced error handling for API requests and improved logging for better traceability.
- Updated tests to cover new features and ensure reliability of the report generation process.
2026-03-07 01:13:03 +09:00
21in7
1b1542d51f feat(weekly-report): add main orchestration, CLI, JSON save
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:49:16 +09:00
21in7
90d99a1662 feat(weekly-report): add Discord report formatting and sending
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:46:03 +09:00
21in7
58596785aa feat(weekly-report): add ML trigger check and degradation sweep
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:42:53 +09:00
21in7
3b0335f57e feat(weekly-report): add trend tracking from previous reports
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:40:39 +09:00
21in7
35177bf345 feat(weekly-report): add live trade log parser
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:39:03 +09:00
21in7
9011344aab feat(weekly-report): add data fetch and WF backtest core
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 00:34:48 +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
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
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
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
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
fce4d536ea feat: implement HOLD negative sampling and stratified undersampling in ML pipeline
Added HOLD candles as negative samples to increase training data from ~535 to ~3,200 samples. Introduced a negative_ratio parameter in generate_dataset_vectorized() for sampling HOLD candles alongside signal candles. Implemented stratified undersampling to ensure signal samples are preserved during training. Updated relevant tests to validate new functionality and maintain compatibility with existing tests.

- Modified dataset_builder.py to include HOLD negative sampling logic
- Updated train_model.py to apply stratified undersampling
- Added tests for new sampling methods

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 00:13:42 +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
3c3c7fd56b feat: add upsert_parquet to accumulate OI/funding data incrementally
바이낸스 OI 히스토리 API가 최근 30일치만 제공하는 제약을 우회하기 위해
upsert_parquet() 함수를 추가. 매일 실행 시 기존 parquet의 oi_change/funding_rate가
0.0인 구간만 신규 값으로 덮어써 점진적으로 과거 데이터를 채워나감.
--no-upsert 플래그로 기존 덮어쓰기 동작 유지 가능.

Made-with: Cursor
2026-03-02 14:09:36 +09:00
21in7
aa52047f14 fix: prevent OI API failure from corrupting _prev_oi state and ML features
- _fetch_market_microstructure: oi_val > 0 체크 후에만 _calc_oi_change 호출하여
  API 실패(None/Exception) 시 0.0으로 폴백하고 _prev_oi 상태 오염 방지
- README: ML 피처 수 오기재 수정 (25개 → 23개)
- tests: _calc_oi_change 첫 캔들 및 API 실패 시 상태 보존 유닛 테스트 추가

Made-with: Cursor
2026-03-02 14:01:50 +09:00
21in7
b57b00051a fix: update test to force LONG signal so build_features is called
Made-with: Cursor
2026-03-02 13:57:08 +09:00
21in7
dfd4990ae5 feat: fetch realtime OI and funding rate on candle close for ML features
- Add asyncio import to bot.py
- Add _prev_oi state for OI change rate calculation
- Add _fetch_market_microstructure() for concurrent OI/funding rate fetch with exception fallback
- Add _calc_oi_change() for relative OI change calculation
- Always call build_features() before ML filter check in process_candle()
- Pass oi_change/funding_rate kwargs to build_features() in both process_candle() and _close_and_reenter()
- Update _close_and_reenter() signature to accept oi_change/funding_rate params

Made-with: Cursor
2026-03-02 13:55:29 +09:00
21in7
4669d08cb4 feat: build_features accepts oi_change and funding_rate params
Made-with: Cursor
2026-03-02 13:50:39 +09:00
21in7
2b315ad6d7 feat: add get_open_interest and get_funding_rate to BinanceFuturesClient
Made-with: Cursor
2026-03-02 13:46:25 +09:00
21in7
c89374410e feat: enhance trading bot functionality and documentation
- Updated README.md to reflect new features including dynamic margin ratio, model hot-reload, and multi-symbol streaming.
- Modified bot logic to ensure raw signals are passed to the `_close_and_reenter` method, even when the ML filter is loaded.
- Introduced a new script `run_tests.sh` for streamlined test execution.
- Improved test coverage for signal processing and re-entry logic, ensuring correct behavior under various conditions.
2026-03-02 01:51:53 +09:00