11 Commits
Author SHA1 Message Date
shadowinlife ee03474268 feat(factors): industry-neutralized CN value alphas + PIT SW2021 sector tag (csi300) (#1802)
* feat(bench): add horizon parameter for monthly-frequency IC revalidation

h-bar forward returns (close[t+h]/close[t]-1, no ffill — a NaN close never
bridges) sampled non-overlapping end-anchored, so the naive-iid t-stat and
the alive/dead thresholds stay honest without new statistics. Threaded
through run_bench / run_bench_strict / the alpha_bench tool + MCP wrapper /
CLI --horizon (argparse type + handler guard, exit 2 on bad input).
horizon=1 is byte-identical to the previous next-bar formula.

Follow-up #1 of issue #1789 (monthly-horizon revalidation of the 13
fundamental factors — the conventional reading for fundamental factors).

* fix(bench): exclude SEC-fundamental alphas off sp500 instead of failing the bench

--zoo fundamental --universe csi300 (the repro command printed in issue
#1789) hard-failed with 'Choose the S&P 500 universe' because the
validator rejects fund:* inputs on any non-sp500 universe, while the
mirror case (CN val:* alphas on sp500) skips cleanly per alpha. The
runners now split the selection up front, disclose the excluded ids in
the result envelope, and error only when nothing is left to bench; the
validator stays as the backstop for direct callers.

The CLI/tool JSON envelopes also state their own horizon,
forward_return_sampling and universe_excluded — a saved summary must
show the configuration its numbers came from.

* fix(bench): close review findings on horizon/exclusion disclosure

- multi-zoo CLI aggregation threads horizon / forward_return_sampling /
  universe_excluded from the sub-runs into the aggregate envelope (a saved
  all-zoos JSON now states its own horizon and reconciles its exclusions)
- alpha compare surfaces universe-excluded shortlist members as skipped
  entries with the exclusion reason instead of silently dropping them
- horizon upper bound (1.._MAX_HORIZON) enforced consistently at the CLI
  parser, the handler, and both runner entries BEFORE any panel load —
  previously only the tool/MCP capped it and an unbounded CLI run fetched
  the full panel before failing late
- non-int horizon can no longer TypeError in the entry schema construction
- categorise docstring notes the daily calibration of the |IC|>0.02 floor
  at h>1; the sampling comment no longer overclaims (residual
  autocorrelation from persistent factor exposures is not modeled)
- MCP wrapper uses _MAX_HORIZON instead of a hardcoded duplicate

Pinned by new tests: parser/handler/runner cap rejection (fetch not
called), multi-zoo disclosure aggregation, compare exclusion disclosure.

* feat(factors): industry-neutralized CN value alphas + PIT SW2021 sector tag

Three within-industry z-scored variants of the CN value factors
(fund_pb/fund_ps_ttm/fund_dividend_yield -> *_ind_neutral, zoo 475 -> 478):
raw A-share PB/PS/dy are heavily industry-confounded, so each name is
z-scored against its Shenwan L1 peers instead of the whole cross-section.

- industry_loader.py: tushare index_member_all (SW2021) membership with
  in_date/out_date -> point-in-time labels per trading day; deterministic
  overlap resolution; 2000-row truncation guard refetches per L1; degrades
  to an unmapped frame on provider failure, never invents a label.
- csi300 bench attaches panel['sector'] outside the pickled price cache,
  masked to the PIT membership cross-section, behind the same 0.9 coverage
  gate the sp500 GICS tag uses; meta discloses sector_source/coverage.
- base.zscore_by_group: the z-scoring sibling of alpha101's _ind_neutralize
  demean - no global fallback (an unlabelled cell stays NaN rather than
  wearing an industry-neutral label it did not earn).
- README x7 + SKILL.md counts (incl. the stale 462 prose sites), i18n zh/en.

Follow-up #2 of issue #1789.

* fix(loaders): index_member_all takes no src param; fetch PIT history via is_new Y+N

Live-verified endpoint contract (pinned by test): index_member_all accepts
only the documented l1/l2/l3_code, ts_code and is_new filters — an
undocumented src param silently returns an empty frame (HTTP 200, 0 rows),
which degraded the first csi300 sector attach to coverage 0.0. src now goes
to index_classify (truncation fallback) only.

The server also defaults is_new to 'Y' (current members, no out_date rows),
so the historical half ('N', ~2000 rows with out_date back to the 1990s) is
fetched separately and concatenated — without it every label would be
today's classification and the loader's PIT claim false.

Live smoke: 350/350 csi300 PIT constituents covered (100%), 0.9 gate passes.

* docs(factors): surface the SW2021 taxonomy-retroactivity caveat; pin cache immutability

Review follow-ups: the loader docstring disclosed that SW2021
classifications are applied retroactively, but no user-facing surface
did — one sentence added to each ind-neutral alpha's notes, the i18n
en/zh entries and SKILL.md (membership is point-in-time; the taxonomy
version itself is not). The sector-attach test now pins that the
original pickled panel dict is never mutated by the attach, and the
sector_coverage meta semantics (labelled-somewhere fraction, not
per-date) are documented at the attach site.

* fix(factors): align ind-neutral PB/PS with the upstream nonpositive-multiple guard

The maintenance batch (d3c76819) added a guard to fund_pb/fund_ps_ttm:
nonpositive multiples are excluded rather than ranked as cheap. The
industry-neutral variants follow suit — the within-group z-score sees
the same values.where(values > 0) pre-filter, the guard sentence leads
the notes/i18n entries, and the upstream parametrized test is mirrored
for the neutral stems.
2026-10-12 19:22:41 +08:00
Haozhe Wu d3c76819c3 fix: complete October 11-12 maintenance and research updates
Fix live option guards, grounding operand evidence, source-bound benchmarks, trade artifacts, attribution, drawdowns, valuation tolerances, and SEC point-in-time factor inputs.

Add Brazil research data and market support, 13 fundamental and valuation factors, channel progress forwarding, and opt-in ZimaOS templates. Update seven README News sections with three visible dates.

Validation: 17,635 backend tests passed; 1,084 final targeted tests and 103 frontend tests passed. Frontend build, package build, documentation checks, and CI gates passed.
2026-10-12 02:29:46 +08:00
Haozhe Wu 6365a9eba9 factors: pin Carhart's skip month and correct the text that described the old formula
#1578 fixed academic_carhart_mom to the return from t-252 to t-21, and
regenerated its golden with it, so the golden alone cannot tell the two
formulas apart. A new test scales one stock's closes inside the skipped
month and requires its last-bar value unchanged, and moves when the close
21 or 252 bars back changes; the old r_252 - r_21 fails it.

The alpha's notes, the zoo LICENSE and the wiki's Chinese note still said
'12-month return minus 1-month return'. The strategy-dev-manager example
taught the same subtraction, and named the alpha academic_carhart_momentum,
an id that does not exist.
2026-09-30 16:33:13 +08:00
Haozhe Wu 36f1698e1f wiki: Chinese alpha notes, and the GTJA 191 post re-measured
Alpha Library: the 191 notes, 117 nicknames and 10 theme tags now switch
to Chinese with the rest of the site. wiki/alpha-library/i18n.zh.json keeps
each translation next to the English it came from; the generator puts the
Chinese in data-text-zh only while that English still matches the
registry, and tests/factors/test_alpha_library_translations.py fails on a
missing, stale or orphaned translation or an unnamed theme.

GTJA 191 post: the 2026-05-17 numbers came from Tushare prices that were
not split-adjusted and from today's CSI 300 roster applied to the whole
window, both fixed on 2026-08-04 (d52199ff). Re-ran the bench on the
current code (point-in-time membership, qfq prices): 8 alive, 14 reversed,
168 dead, gtja191_138 untested (99% NaN), where the first version said
10 / 15 / 165 and never accounted for the 191st. Both languages now carry
a revision note and the new counts, theme table, top 5 by IR and three
most-negative alphas, and fix what the translation surfaced or the code
contradicts: the top 5 are ranked by IR, not IC; the theme table counts
every tag an alpha carries; the reflection matches the table; the Method
universe matches the Caveats; Kakushadze's preprint came a year after the
report; forward returns are simple, not log; no GTJA formula needs sector
tags; the bench measures no turnover and draws no IC plot; five alphas use
a proxy input (named); the reproduce command covers 2018-2025. A new
caveat reports the bench's multiple-testing deflation: 3 of the 8 alive
alphas clear the IR expected from 190 null formulas.
2026-09-29 18:36:52 +08:00
Haozhe Wu a7f645f368 wiki: one header on every page, English by default, one switch for 中文
Each section carried its own copy of the header: Home's nav had no Home,
the docs' nav had no Docs, the research post had no star or theme button,
and the generated Alpha Library pages had no header at all. Only the docs
could switch language, and they guessed Chinese from the browser while
every other page stayed English.

- partials/ holds one head block, header and footer; sync_site_chrome.py
  writes them into every page between markers and, with --check in CI,
  also requires <body data-section> (the current section is highlighted by
  CSS), the language attributes and a single ?v= token site-wide. The
  Alpha Library generator inserts the same partials.
- site.js is the shared shell (theme, stars, language, footer traffic on
  home only). English is the HTML; Chinese comes from locales/zh.json via
  data-i18n hooks, and check_i18n.py fails on a key with no Chinese or a
  Chinese string nothing uses. A URL that names a language (/docs/zh/,
  the two articles) shows it and remembers it; everything else follows the
  stored choice, English by default. theme-init.js keeps a page hidden until
  its Chinese text is in, so it never flashes English.
- English tutorial and Chinese research post added. The Chinese tutorial
  gets the corrections its translation surfaced: 462 alphas in five zoos,
  AAPL.US, the mandate caps trades per day (it has no daily loss limit),
  Shadow Account extracts up to five rules, the web UI needs Docker or a
  checkout.
- Alpha Library: the zoo pages were never deployed — content/ is
  gitignored and nothing built it, so every zoo link served the root
  redirect. The deploy now exports the manifest and renders the pages (and
  redeploys when agent/src/factors changes); the fifth zoo, fundamental, is
  listed; generated pages allow same-origin scripts only.
- Phones keep the nav as a scrollable row instead of hiding it.
2026-09-29 17:53:13 +08:00
Kada Liao 63f825ca77 fix(wiki): add alpha library social card
Signed-off-by: Kada Liao <kadaliao@gmail.com>
2026-07-05 02:09:09 +08:00
Matt Van Horn d8f88d45da fix(wiki): harden alpha-library CSP by externalizing inline scripts 2026-07-01 15:40:27 +08:00
Haozhe Wu 399b5b4e23 fix(wiki): harden alpha library hydration 2026-06-29 15:51:47 +08:00
Haozhe Wu cb82c6262c fix(wiki): align live-trading copy with shipped Robinhood Agentic support; hide footer counter until data loads
The wiki still claimed 'no live trading / does not execute live trades', which contradicts the shipped opt-in bounded Robinhood Agentic channel (and the README). Aligns home, docs, and alpha-library copy with the README's framing. Also fixes a CSS specificity bug where .footer-traffic{display:flex} overrode the [hidden] attribute, showing placeholder dashes before /api/stats data arrived.
2026-06-05 14:39:55 +08:00
Haozhe Wu 137689c5c1 feat(wiki): Alpha Library auto-render + "Alpha 191 in 2026" blog
wiki/scripts/build_alpha_library.py (475L)
- Reads manifest.json produced by `vibe-trading alpha export-manifest`
- Emits per-alpha pages (content/<zoo>/<id>.html) and per-zoo
  overview tables (content/<zoo>/index.html) — 452 + 4 = 456 pages
- Jinja2 with select_autoescape(["html","xml"]) mandatory; no |safe
  on user-derived content; strict CSP on every generated page
  (default-src 'self'; style-src 'self' 'unsafe-inline';
   script-src 'none'; img-src 'self' data:)
- Emits content/index.json for the landing page to hydrate per-zoo
  counts via fetch()
- Friendly exit when manifest.json missing
- End-of-main self-test compares manifest formula content against
  rendered HTML to verify the escape pipeline; prints
  "html escape ok" or "html escape FAIL"

wiki/alpha-library/index.html (rewritten from 78-line shell to 192L)
- Hero + 4 zoo cards (Kakushadze 101 Formulaic Alphas, qlib158,
  gtja191, academic) + stats grid + 3-line CLI quickstart + GitHub
  footer
- Per-zoo counts hydrated at runtime from content/index.json
- Loose CSP (script-src 'self' 'unsafe-inline') only on this landing
  page to keep the site theme-init inline script and the inline
  fetch() — generated /content/** pages stay on script-src 'none'

wiki/research-lab/index.html
- Post grid replacing the empty hero stub; links to the new blog
- One-line lede explaining the section

wiki/research-lab/posts/alpha-191-in-2026.html (~675L, ~2400 words)
- 6-section deep-dive: TL;DR / Background / Method / Findings /
  Caveats / Reproduce
- Numbers from a real bench run on CSI 300 / 2018-2025: 10 alive,
  15 reversed, 165 dead (of 190 tested, 1 skipped). Top 5 by IR:
  gtja191_171 / _111 / _163 / _002 / _054. Theme survival rates:
  microstructure 22%, reversal 11%, volatility 8%, volume 5%,
  momentum 2%, liquidity 0%
- Survivorship-bias caveat for cross-zoo readers (the current
  constituent list of any index is a forward-biased proxy for the
  point-in-time constituents during the window)
- Strict CSP (script-src 'none'); no MathJax/KaTeX — formula_latex
  rendered as <code class="formula">; aggregate stats only (no
  per-stock per-date values, Tushare ToS)

wiki/research-lab/posts/alpha-191-in-2026.tweet.md
- HKUDS-style draft tweet (Chao Huang voice): emoji-decorated
  project name, real numbers (10 / 15 / 165), 8-year OOS qualifier,
  GitHub + pip links, ≤2 hashtags. READY to post once the release
  goes live.

.gitignore adds wiki/alpha-library/manifest.json and
wiki/alpha-library/content/ as generated, never-checked-in artifacts.
2026-05-17 20:04:24 +08:00
Haozhe Wu eba0f1f8a1 docs: add wiki section shells 2026-05-14 22:02:32 +08:00