- 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)
- 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
- 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>
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>
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>
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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
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
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.
- 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
Add BIAS_SIGNAL indicator derived from TongDaXin's 30-day bias formula.
Outputs BS_X (raw bias), BS_SMA (short signal line), BS_LMA (long signal
line) for trend direction and reversal detection via asymmetric bull/bear
logic.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The 1.4.1 commit added the indicator registry entry in indicator.py but
forgot to include the actual ZHUOYAO() function definition in MyTT.py.
Also includes lint cleanups (trailing semicolons, import formatting).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add 捉妖大师 (ZHUOYAO) indicator to the indicator registry. Outputs
ZY_LONG/ZY_MID/ZY_SHORT/ZY_TREND four lines based on 20/60/120-day
ROC with EMA smoothing for trend resonance detection.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Integrate MyTT library to provide 30 technical indicators (MACD, KDJ, RSI,
BOLL, DMI, ATR, etc.) accessible via API and CLI with automatic EMA warm-up.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New MacClient/AsyncMacClient method that ranks all boards of a given
type (industry/concept) by change_pct, amount, main_net_amount, or vol.
Aggregates member quotes via get_board_summary() for each board.
Also bumps version to 1.3.0 and updates README + CLI version.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
New MacClient/AsyncMacClient method that aggregates board member quotes
into total amount, main force net inflow (1d/3d/5d), and up/down counts.
Includes example demo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add MacClient/AsyncMacClient with full MAC protocol support (quotes, kline
with adjustment, tick charts, transactions, boards, capital flow, auction,
unusual, symbol info, server info)
- Add MacExClient/AsyncMacExClient for extended markets (HK, US, futures)
- Add UnifiedTdxClient auto-routing between A-share and extended markets
- Add `easy-tdx` CLI tool with JSON default output, Agent-friendly
- Add field bitmap protocol for custom quote field selection
- Fix quote-list missing fields (default to BASIC+VOLUME preset)
- Add config.py with centralized host management and auto-discovery
- Add 50+ examples covering all APIs (01-20)
- Rewrite README with CLI-first, Agent-friendly documentation
- Bump version to 1.1.0
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- K-line: daily+ periods output 'date' only, minute periods output 'datetime'
- Transactions (tick-by-tick): combine date param + hour/minute into 'datetime'
- XdxrRecord, HistoricalFundFlow: year/month/day merged to 'date'
- MinuteBar: rename unknown_1 to _unknown_1 (hidden from DataFrame)
- MinuteBar: add datetime column computed from bar index (A-share 240-bar pattern)
- get_minute_time_data: use history endpoint only (current-day endpoint broken in pytdx too)
- Update all examples to reflect new DataFrame column names
- Add ExTdxClient/AsyncExTdxClient for futures, HK stocks, etc (port 7727)
- Add offline module: read daily bars, minute bars, blocks, gbbq, financials
from local TDX installation directory (inspired by pytdx)
- Add examples 09 (file download) and 10 (offline data reading)
- Rewrite README with comprehensive API docs and code examples
- Add TdxFileNotFoundError and TdxOfflineError exceptions
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add CALC_HOSTS, financial file list/record parsing (codec/financial.py),
new client methods (get_financial_file_list, get_financial_file,
get_financial_records) with async counterparts, and example 09 demo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Add example scripts for all API categories (connection, market info,
kline, minute, transaction, finance, block, fund flow)
- Fix GetIndexBarsCmd: index bar records have 4 extra bytes (advance/
decline counts) that were not consumed, causing pos drift and
corrupted dates/volumes for all records after the first
- Fix price_limits.py example (SecurityQuote has no name attr)
- Fix finance_info.py display (scientific notation -> formatted numbers)
- Add PostToolUse ruff hook (scripts/ruff_hook.py)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>