2026-10-13 — new conditions, taiwan long-history data layer, local long backtest engine
Backups before the change (verified file count + per-file sha256): ~/apify/backups/{stock-screener,tw-kd-screener,breakout,actors-lib}_2026-10-13_pre-upgrade (each has BACKUP_NOTE.md). This directory was never tracked by git, so there is no tag.
ind prior_range (HH/LL/HC/LC/AMP over the previous n bars, excluding today), ma_cluster (CL, lag), turnover (TO/TOP, traded value), high_n (H52/L52/DTH52/FRL52), kd_runs (K/D run lengths, KD blunting 鈍化), macd_live (weekly/monthly MACD including the unfinished week, no look-ahead).
Condition keys: gte / lte comparators, lag (read the previous bar), px (raw / adjusted price), params.min_bars (warm-up).
All new indicators are strict: not enough history -> value missing -> condition false.
btengine.build_series accepts amount / raw prices / min_bars. data.py and main.py now pass traded value (7th tuple element) so turnover works in the Actor TW path.
Fixed: rulespec.validate_rule used to reject synthetic b-dict conditions yielded by iter_conds (latent bug); twdata.limit_up/limit_dn float rounding.
Added — local data layer (src/lib/twfetch.py, twdata.py) — NOT used by the Apify Actor run
twfetch.py: resumable, rate-limited, official-source-only fetcher (TWSE MI_INDEX, TPEx afterTrading/otc, TWT49U / exDailyQ ex-dividend tables, TAIEX price + total-return index). Failure is never recorded as holiday; 8 consecutive failures stop the run.
twdata.py: raw files -> store.sqlite (incremental), back-adjusted (還原) price loader using the official ex-rights table (reference price after dividend / pre-ex close), limit-up/down from official columns, unmarked-break detection (capital reduction, ETF split, par change), delisting visible (no survivorship filter), quality_report().
Added — local long-history backtest engine (src/lib/longbt.py)
Entry at the next market day's open; skip limit-up open; limit-down locked exits are deferred; halts deferred; delisted names closed at last close with a configurable haircut; cost 0.585% round trip (fee 0.1425% x2 + tax 0.3%); back-adjusted or raw basis.
Equal-weight portfolio, max drawdown, 0050 / TAIEX total-return / whole-market benchmarks, by-year and bull/bear slices, n<30 warning, signal-day clustering (Kish effective sample size), 95% CI (naive and signal-day cluster bootstrap).
Rules and exits are evaluated by the same rulespec/btengine as the Actor.
Added — examples and tests
examples/rules_breakout_A.json (10 conditions), rules_breakout_B.json (A + MA cluster + liquidity).
test_longbt.py (48 tests), tests/regress_compare.py (old lib vs new lib, same rules and data: entries, trades, matches_now).
Shared lib files synced to ~/apify/actors/lib via tools/sync_lib.sh (FILES list extended: indicators rulespec btengine twfetch twdata longbt).
Compatibility
Existing rules give bit-identical entries and trades (regress_compare.py: 9 rules x 1976 stocks on the old 117-day cache; 9 rules x 120 random stocks on the long data, 2020-01..2026-10, raw prices: zero differences).
Behaviour differences of longbt vs the previous half-year script (documented, not bugs): positions still open at the end of data are marked open and excluded from closed stats (the old script closed them at max_hold on the last bar); signals whose own bar is a data break are excluded.
Known limits
See README "Data layer" and "Local long-history backtest".
2026-10-14 — weekly/monthly conditions evaluated as of each day; README cleanup
Changed (backtest semantics):tf:"W"/"M" conditions now see the period in progress up to the evaluated day
(btengine.build_series computes per-day values from completed periods + the partial period; rulespec reads them
for the current period). Before, mid-period days used the last completed period, so backtest and screen disagreed.
Checked: 200 random rules, full-history vs cut-at-day evaluation identical (79 differed before); daily-only rules
(61, entry and exit) bit-identical to the published engine; on the last day of each period weekly/monthly
decisions identical to before (49,751 mid-period days changed). Tools: tools/engine_equivalence.py,
tools/engine_lookahead.py. Cost: 1,260 daily bars with 3 weekly/monthly conditions ≈ 0.14 s per symbol.
Fixed (published 0.1.9 bug, by the 2026-10-13 upgrade): rules whose b is another series spec
(MA5 crossing MA60) were refused as malformed.
README: local-only data layer and long-backtest sections moved to NOTES.md (they described tools the Actor
does not run and paths on the developer's machine); macd_live row and No look-ahead row updated.
Fixed (published 0.1.9 bug): a rule the data cannot support (px:"raw", turnover on US data, an unknown
indicator, volume on data without volume) returned {"error": ...} with zero rows, i.e. a SUCCEEDED run with an
empty dataset. It is now refused (RuleCannotEvaluate, input level: no rows, no per-row charge, status message).