Commit Graph
353 Commits
Author SHA1 Message Date
Justin Gu 06b2617ebc fix: CI coverage enforcement, real avg_holding_days, vectorize _datetime_to_int
- Add --cov and --cov-fail-under=50 to CI pytest command
- Replace hardcoded avg_holding_days=5.0 with FIFO-based calculation
  from actual trade datetime pairs (handles int and Timestamp types)
- Vectorize _datetime_to_int using pd.to_datetime().strftime()
  instead of Python for-loop (~100-200x faster on large arrays)
- Add 3 new test cases: weighted holding days, no datetime fallback,
  only-buys edge case
2026-06-11 01:44:39 +08:00
GitHubandClaude Opus 4.8 b4f63c85a6 feat: add OBV trend strategy (v1.9.5)
- New obv_trend.py strategy: OBV energy flow trend following
- Entry: OBV > MAOBV*1.02 buffer + MAOBV 20-bar uptrend confirmed
- Exit: OBV drops below MAOBV
- Tunable params: maobv_period(30), maobv_lookback(20), obv_buffer(0.02)
- Update README changelog and strategy table
- Bump version to 1.9.5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 18:53:06 +08:00
GitHubandClaude Opus 4.8 79c6fcee0e fix: remove unused type: ignore comments in analyser.py
Use 'row_any: Any = row' pattern to avoid arg-type mismatch between
local (pandas-stubs) and CI (bare pandas) environments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:12:38 +08:00
GitHubandClaude Opus 4.8 4dfd18050e fix: resolve all CI mypy (265→0) and ruff (26→0) errors
- pyproject.toml: add mypy overrides for pandas/tabulate/matplotlib stubs,
  disable strict checking for vendored MyTT library
- config.py: use cast() for dict[str, Any] .get() returns
- beichi.py: widen _calc_bi_force param to BI | XD, import XD
- backtest/cli.py: split combo/single strategy into separate typed variables
- backtest/combo.py: add bool_array() helper for numpy return types
- chanlun/analyser.py: type ignore for pandas row access, fix dict type arg
- unified.py: change fields param from object to Any
- ex/mac_client.py: add type args to list literals
- cli/cmd_offline.py: wrap int market as Market enum before API call
- cli/cmd_chanlun.py: fix dict type arg
- offline/write_*.py: explicit int() cast for struct.unpack returns
- MyTT.py: fix line-too-long comments, UP038 isinstance syntax
- tests: fix E712 (==False → ~mask), E741 (noqa), F841, import sorting
- ruff format applied across codebase

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 15:03:41 +08:00
Justin Gu 5aac7d3a39 chore: add risk disclaimer, CI pipeline, pytest coverage config
- Add risk warning after backtest demo output (survivorship bias, overfitting)
- Add disclaimer section at README end
- Add slippage comment to backtest demo command
- Add CI workflow (test + lint + mypy) for PR/push to main
- Add pytest config with coverage threshold (fail_under=50, current=56%)
2026-06-10 12:43:02 +08:00
Justin Gu 21f71d3701 chore: ignore .omc, .claude, .playwright-mcp directories and remove from tracking 2026-06-10 11:54:08 +08:00
Justin Gu c867297d45 fix: remove hardcoded version in 'version' command (v1.9.4) 2026-06-10 03:40:28 +08:00
Justin Gu 531d0c05bc fix: remove hardcoded version in 'version' command, read from importlib.metadata 2026-06-10 03:39:09 +08:00
Justin Gu 862f713483 feat: add 'run-all' CLI command for batch strategy backtesting (v1.9.3) 2026-06-10 03:24:31 +08:00
Justin Gu a3d6d93122 feat: strategy screener - scan all stocks by signal, rank by backtest performance (v1.9.2)
- Add 'screen' CLI command group with 'scan' and 'rank' subcommands
- scan: offline signal scanning from local .day files, zero network IO
- rank: backtest ranking of scanned signals by sharpe/drawdown/etc
- Two-step workflow: scan outputs JSON, rank reads JSON and evaluates
- Support --universe (all/sh/sz/custom file), --sort, --names
- Support pipe mode: scan ... | rank --from - --table
- New module: src/easy_tdx/screen/{scanner,ranker,cli}.py
- 20 unit tests (offline, no network required)
2026-06-10 03:03:03 +08:00
Justin Gu cb0a8ef6c5 chore: bump version to 1.9.1 2026-06-10 02:14:53 +08:00
Justin Gu 5691bb8432 refactor: screen() reuses run_combination(), single runner across combo sizes
- screen() now calls run_combination() internally, eliminating duplicated
  signal extraction/combination logic
- _run_combo_screen creates one CombinationRunner before the size loop,
  so signal cache is reused across 2-factor and 3-factor screens
- Add MAJORITY(2)=AND note to screen() docstring
2026-06-10 02:13:28 +08:00
Justin Gu 1e99feb7c2 feat: multi-factor combo backtest engine (v1.9.0)
- Add backtest/combo.py: CombinationRunner, extract_factor_signals, combine_masks
- Signal merge modes: AND / OR / MAJORITY (majority default)
- CLI: --combo-strategies and --combo-mode for easy-tdx backtest
- run_all_strategies.py: --combo 2 --combo 3 auto-screen best combos
- Fix MyTT MFI/CR divide-by-zero RuntimeWarning
- 14 new unit tests, 328 total passing
2026-06-10 01:37:28 +08:00
GitHubandClaude Opus 4.8 7f1bc645c2 release: v1.8.2 - 5 new strategies, --show chart, 15 total
- New strategies: dmi_trend, cci_breakout, mfi_volume, trix_cross, mtm_momentum
- run_all_strategies.py --show: dual-axis equity curve vs stock price chart
- zhuoyao_momentum strategy (from earlier commit)
- README: updated strategy table (9→15), changelog, demo screenshots
- Bump version to 1.8.2

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:17:04 +08:00
GitHubandClaude Opus 4.8 8fcedeb47a feat(strategies): add zhuoyao_momentum strategy + Maotai demo screenshot
- New ZHUOYAO multi-timeframe momentum strategy (strategies/zhuoyao_momentum.py)
- Entry: SHORT>0 + TREND>0 + SHORT>MID (triple resonance)
- Exit: SHORT<0 or TREND<0 (conservative, any triggers sell)
- Add SH600519 Maotai demo screenshot (strategies/demo/4.png)
- Update README with 4th demo screenshot

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 22:59:19 +08:00
GitHub b57d8f4669 feat(backtest): add --show flag for best strategy equity curve chart
- Add --show/--show-chart flag to run_all_strategies.py
- Display dual-axis chart: normalized stock price vs strategy equity curve
- Mark buy/sell points with green/orange triangles
- Auto-detect Chinese fonts (SimHei/YaHei on Windows, PingFang on macOS)
- Fetch stock name via get_stock_quotes for chart title
- Add 3 demo screenshots to README with disclaimer
- Update README with --show usage and visual examples
2026-06-09 22:41:14 +08:00
GitHubandClaude Opus 4.8 b44636207b release: v1.8.1 - single-source version, batch backtest detail output
- Consolidate version to pyproject.toml as single source of truth
- __init__.py, cli/__init__.py, docs/conf.py all read dynamically
- run_all_strategies.py now shows best strategy full trade details
- Update README changelog for 1.8.1

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 22:12:27 +08:00
GitHubandClaude Opus 4.8 b5b5d0dc5b release: v1.8.0 - backtest engine with batch strategy comparison
- Add backtest section to README with CLI usage and run_all_strategies.py demo
- Update all version numbers to 1.8.0 (pyproject.toml, __init__.py, cli/__init__.py, docs/conf.py)
- Fix turtle_breakout strategy: TAQ returns 3 values (UP, MID, DOWN)
- Add run_all_strategies.py batch comparison script
- Update README intro to highlight backtest feature
- Add backtest to CLI command table and architecture tree

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 20:35:38 +08:00
GitHubandClaude Opus 4.8 70c69c8a66 fix(backtest): cli _print_table used wrong key 'sharpe_ratio' instead of 'sharpe'
Performance dict outputs 'sharpe' but _print_table looked up 'sharpe_ratio',
so perf.get('sharpe_ratio', 0) always returned the default 0 regardless of
actual Sharpe value.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 20:02:20 +08:00
GitHubandClaude Opus 4.8 46298e68d7 fix(backtest): max drawdown now correctly measures peak-to-trough percentage
Previous formula was: max(absolute_drawdown) / initial_capital, which
exceeds 100% when the portfolio grows then drops (e.g. from 600k to 300k
on a 100k initial = 300% drawdown, which is nonsensical).

Fixed to use drawdown_pct (drawdown / peak) which is always in [0, 1].
This correctly measures the maximum percentage drop from the highest
equity peak, matching the standard financial definition.

Also added regression test: test_max_drawdown_never_exceeds_100_pct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 19:00:11 +08:00
GitHubandClaude Opus 4.8 5550702620 fix(strategies): unpack BIAS triple return value in bias_reversal
MyTT.BIAS returns (BIAS6, BIAS12, BIAS24) but the strategy was assigning
all three to a single variable, causing 'array with more than one element'
ValueError when comparing to a scalar threshold.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:56:07 +08:00
GitHubandClaude Opus 4.8 6a6d75f5d5 fix(backtest): strategy position not tracked during signal generation
Root cause: _generate_signals() iterated all bars calling strategy.next()
but never updated _position_size or _cash on the strategy. Strategies
that check self.position['size'] before buy/sell always saw 0, producing
only BUY signals with no SELL — exhausting cash and producing drawdowns
exceeding 100%.

Fix: add _update_strategy_position() that estimates position changes
after each bar's signals using close price. This gives the strategy an
accurate view of its holdings so it can correctly alternate buy/sell.

Regression tests added:
- test_position_aware_buy_sell_alternation: verifies BUY/SELL alternation
- test_position_aware_no_duplicate_buys: no suspicious tiny duplicate buys

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:50:13 +08:00
GitHubandClaude Opus 4.8 f7e1abd873 feat: add 9 demo strategy files for backtest engine
Strategies included:
- ma_cross: MA5/MA20 dual moving average crossover
- expma_cross: EMA12/EMA50 crossover (more responsive)
- macd_cross: MACD golden/death cross
- bollinger_breakout: Bollinger band breakout
- rsi_reversal: RSI overbought/oversold reversal
- kdj_golden: KDJ low golden cross / high death cross
- turtle_breakout: Turtle trading (Donchian channel)
- bias_reversal: BIAS mean reversion
- volume_price: Volume-price confirmation

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:45:01 +08:00
GitHubandClaude Opus 4.8 00b7d75caa docs: add backtest engine usage manual
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:37:58 +08:00
GitHubandClaude Opus 4.8 04c2be1d7f fix(backtest): resolve mypy and ruff lint issues
- dsl.py: use NDArray type annotations, fix None narrowing
- cli.py: add type annotations, fix import sorting
- strategy.py: fix UP038 isinstance, add noqa for I() method name
- tests: fix E712 bool comparison assertions

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:21:25 +08:00
GitHubandClaude Opus 4.8 fc0777533e feat(backtest): add CLI command with auto data fetch and table output
- Created src/easy_tdx/backtest/cli.py with backtest command
- Supports --strategy-file to load Python strategy classes
- Supports --indicators to precompute technical indicators
- Supports --cash, --commission, --execution, --period, --adjust, --count options
- Supports json/table/csv output formats
- Auto-loads K-line data via get_mac_client()
- Registered backtest command in src/easy_tdx/cli/__init__.py
- Added tests/unit/test_backtest_cli.py with basic CLI tests

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:15:37 +08:00
GitHubandClaude Opus 4.8 706f22ba5e feat(backtest): add DSL strategy skeleton and update __init__.py exports
- Add dsl_strategy decorator in dsl.py (P1 skeleton implementation)
- Update __init__.py to export BacktestEngine, Strategy, and related types
- All 106 backtest unit tests pass

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:12:38 +08:00
GitHubandClaude Opus 4.8 371915a5f9 feat(backtest): add BacktestEngine with vectorized execution pipeline
- Implement BacktestEngine orchestrator with 4-step pipeline:
  1. Signal generation (Strategy)
  2. Order simulation (OrderSimulator)
  3. Portfolio tracking (PortfolioTracker)
  4. Performance analysis (PerformanceAnalyzer)
- Support both strategy class and instance initialization
- Add PnL calculation for sell trades
- Add JSON serialization with numpy/timestamp support
- Include comprehensive test coverage (12 tests, all passing)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:11:20 +08:00
GitHubandClaude Opus 4.8 94fabccef8 feat(backtest): add PerformanceAnalyzer with 19 metrics
- Implement PerformanceAnalyzer class with compute() method
- Calculate 19 performance metrics: total_return, annual_return, max_drawdown,
  max_dd_duration, sharpe, sortino, calmar, trade statistics, and volatility
- Handle edge cases: empty data, no negative returns (sortino=999), no drawdown (calmar=999)
- Add 20 comprehensive unit tests covering all metrics
- Type annotations use NDArray pattern for mypy strict compliance
- All tests pass, mypy and ruff checks clean

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 18:05:33 +08:00
GitHubandClaude Opus 4.8 a2aa319803 feat(backtest): add PortfolioTracker with equity curve and drawdown
- Pre-allocate numpy arrays for performance (cash, position, avg_price)
- apply_trades() processes buys/sells with commission and slippage
- equity_curve returns DataFrame with drawdown calculation
- positions returns DataFrame with market value and unrealized PnL
- 12 unit tests covering all scenarios

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 17:55:24 +08:00
GitHubandClaude Opus 4.8 16dc2e7da9 feat(backtest): add OrderSimulator with 5 execution modes and reject policy
- Implement OrderSimulator class for order matching simulation
- Support 5 execution modes: next_open, next_close, this_close, worst, best
- Support 3 position modes: full, fixed, percent
- Support 2 reject policies: reduce (partial fill), skip (reject)
- Implement fee model: commission (min 5 CNY), stamp tax (0.1% sell only), slippage
- Add future_leak_warning flag for this_close mode
- Handle both int and datetime column types in DataFrame
- Add comprehensive test suite with 24 test cases covering all modes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 17:52:31 +08:00
GitHubandClaude Opus 4.8 687851fc67 feat(backtest): add Strategy base class with DataProxy and crossover
- Add _SeriesAccessor for relative indexed data access ([0] current, [-1] previous)
- Add StrategyDataProxy for efficient DataFrame column access via numpy arrays
- Add crossover() function for golden cross detection (fast line crosses above slow line)
- Add Strategy abstract base class with:
  - init() for indicator registration via self.I()
  - next() for signal generation via buy()/sell()
  - Internal engine hooks (_bind_data, _call_init, _set_bar_index, etc.)
- All code is mypy strict compliant with full type annotations
- 25 unit tests covering all components

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:53:57 +08:00
GitHubandClaude Opus 4.8 f37b75ea42 feat(backtest): add core data types (Signal/Trade/Position/BacktestResult)
- Add Signal dataclass for trading signals with optional price/stop_loss/take_profit
- Add Trade dataclass for executed trades with commission/slippage/pnl/rejected
- Add Position dataclass for position snapshots (long/short/flat)
- Add BacktestResult dataclass with to_dict()/to_json()/summary() methods
- Add comprehensive unit tests (13 test cases, 100% pass)
- All code passes mypy strict, ruff lint+format checks

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:43:44 +08:00
GitHubandClaude Opus 4.8 5f14c44791 docs: add backtest engine implementation plan
10 tasks, TDD approach, covering:
- Core data types (Signal/Trade/Position/BacktestResult)
- Strategy base class with DataProxy and crossover
- OrderSimulator (5 execution modes + reject policy)
- PortfolioTracker (equity curve + drawdown)
- PerformanceAnalyzer (18 metrics)
- BacktestEngine (vectorized execution pipeline)
- DSL skeleton (P1)
- CLI integration with auto data fetch

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:25:11 +08:00
GitHubandClaude Opus 4.8 28925063d4 docs: finalize backtest spec with minor clarifications (rev 2.1)
- DSL identifier rules: letters/digits/underscores
- reduce mode: min(requested, max_affordable) formula
- Chanlun time alignment: nearest K-line <= timestamp
- GridResult/run_many return types documented

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:17:02 +08:00
GitHubandClaude Opus 4.8 480e635dcb docs: revise backtest spec per feasibility review (rev 2)
- DSL requires explicit column references (close/open/high/low)
- Add order_reject_policy: reduce | skip
- Add future_leak_warning for this_close execution
- Add benchmark datetime alignment rules
- Support pre-computed indicator columns via StrategyDataProxy
- Add chanlun_result injection (P0 interface, v2 auto-integration)
- Adjust priorities: DSL→P1, run_many/run_grid→P1, chanlun→P1

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:14:05 +08:00
GitHubandClaude Opus 4.8 8d68e9c094 docs: add backtest engine design spec
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 16:05:17 +08:00
Justin Gu 626d0aae44 docs: remove unused badges (readthedocs, black, isort) 2026-06-08 03:44:46 +08:00
Justin Gu 9d906c340c docs: add Sphinx + Read the Docs infrastructure
- Add .readthedocs.yaml build config (Ubuntu 22.04, Python 3.11)
- Add docs/conf.py with myst-parser for Markdown support
- Add docs/index.md toctree including README and existing docs
- Add docs/readme.md to include root README via myst directive
- Add docs/requirements.txt for Sphinx build dependencies
- Add docs/_build/ to .gitignore
2026-06-08 03:36:40 +08:00
Justin Gu 6f7f5bac16 docs: update README 2026-06-08 03:28:58 +08:00
Justin Gu 79670b7777 docs: rewrite README intro to reflect project mission and values 2026-06-08 03:26:47 +08:00
Justin Gu 112ba7849f fix: chanlun bi algorithm stuck in fractal trap during sustained trends
Fix find_bis() greedy algorithm terminating early when dense alternating
fractals cause gap=0 for every opposite-type fractal. The root cause was
blindly replacing start_fx with more extreme same-type fractals, pushing
right_kline_index forward and making subsequent gaps permanently 0.

Solution: add pending_opposite guard — when an opposite-type fractal fails
the gap check, freeze start_fx replacement until a valid bi is formed.

- Affects: sustained up/down trends with dense fractals (e.g. high-price stocks)
- 600519: 114 bi (ending 04-28) -> 142 bi (ending 05-27)
- 601088: 131 bi -> 147 bi (end date unchanged)
- New regression test: test_fractal_trap_regression
- Bump version to 1.7.1
2026-06-08 03:08:12 +08:00
Justin Gu 0175acec43 docs: rewrite README intro to reflect project mission and values 2026-06-07 23:41:21 +08:00
Justin Gu fd4a1233b4 feat: add chanlun (ChanLun) technical analysis module, bump to v1.7.0
- New chanlun/ subpackage: K-line merge, fractal, bi/xianduan/zhongshu/mmd/beichi
- New 'easy-tdx chanlun' CLI command with JSON/table output
- MACD calculation (pure numpy, no extra dependencies)
- Multi-level analysis (MultiLevelAnalyser)
- Pipeline: DataFrame -> merge -> fractal -> bi -> zhongshu -> xd -> mmd -> beichi
- 49 offline unit tests covering all calculation steps
- Detailed README docs with output explanation
- Bump version: pyproject.toml 1.6.1 -> 1.7.0, cli 1.5.0 -> 1.7.0
2026-06-07 23:29:52 +08:00
Justin Gu 4cab9aa325 fix: route index files to get_index_bars() in sync-daily/sync-all
Root cause: _fetch_all_daily_bars used get_security_bars() for all files,
but index server responses have 4 extra bytes per record. Wrong parser
produced garbage dates like '12897-50-77' for sh000001, sh000300, etc.

Fix: add _is_index_code() to detect index codes by prefix (sh: 00/88/99,
sz: 39) and route to get_index_bars() accordingly.

Bumps version to 1.6.1.
2026-06-07 21:55:26 +08:00
Justin Gu d01b11fa74 feat: add offline data write-back and sync commands, bump to v1.6.0
- Add write_daily.py: encode/append daily bars to .day files
- Add write_ex_daily.py: encode/append extended market daily bars
- Add write_min_bar.py: encode/append minute bars (.5/.lc1/.lc5)
- Add sync-daily CLI: sync single stock with pagination support
- Add sync-all CLI: one-command sync for all SH/SZ .day files
- Update README with sync commands and Python write API docs
- 50 new unit tests covering encode round-trip, append dedup, edge cases
- Bump version 1.5.0 -> 1.6.0
2026-06-07 21:13:49 +08:00
Justin Gu b17e98468b feat: add offline CLI commands and bump version to v1.5.0
- New 'offline' command group with 8 subcommands: home, daily, min,
  ex-files, ex-daily, gbbq, financial, blocks
- No network required, reads local TDX data files directly
- Updated CLI examples and README with offline documentation
- Added v1.5.0 changelog entry
2026-06-02 12:30:24 +08:00
Justin Gu 07c1eca5e8 docs: add V4.0 version note for ZHUOYAO/BIAS_SIGNAL in MyTT.py 2026-06-02 12:06:55 +08:00
Justin Gu 903898512a Merge branch 'main' of https://github.com/handsomejustin/easy_tdx 2026-06-02 12:01:00 +08:00
GitHubandClaude Opus 4.7 2eabbb219a chore: bump version to 1.4.3
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 22:31:31 +08:00