Skip to content

Correct the holding rule, execution lag and asymmetry index; regenerate all downstream results - #2

Open
Tofik (plut777) wants to merge 17 commits into
dissensus-ai:masterfrom
plut777:fix/strategy-specification
Open

Correct the holding rule, execution lag and asymmetry index; regenerate all downstream results#2
Tofik (plut777) wants to merge 17 commits into
dissensus-ai:masterfrom
plut777:fix/strategy-specification

Conversation

@plut777

Copy link
Copy Markdown

Proposed pull request

Title

Correct the holding rule, execution lag and asymmetry index; regenerate all downstream results


Summary

The published code did not implement the strategy the published paper describes.
Three defects are corrected here. The largest of them meant that

the published strategy held a position in 25 of 504 weeks.

It closed any open position the moment its entry signal stopped firing, which is
not the exit rule §2.4 states. Corrected, it holds a position in 55 weeks.

Every headline figure in the paper — the +3.60% return, the 0.149 Sharpe ratio,
the factor regression with its 25-observation effective sample, the "immaterial"
transaction costs, the 19.2-pip break-even — described that five percent of the
sample. That is why the published null result had so little content: a strategy
that is almost never invested cannot demonstrate much in either direction.

The corrected strategy loses 6.64% gross over the decade, Sharpe −0.153,
across 15 holding episodes. The paper's conclusion is unchanged in direction and
considerably stronger in substance. Two findings are new, and both sharpen the
null rather than softening it:

  • There is no break-even transaction cost. Not a larger one — none. A
    break-even presumes a gross profit to be consumed, and there is none.
  • The strategy carries a substantial short-momentum exposure. It loads
    negatively and significantly on time-series momentum (β = −0.82, 95% CI
    [−1.26, −0.39], p = 0.00019 on in-position weeks), a loading that was
    insignificant before correction. Momentum rose over the exposed weeks and the
    strategy was short it, which accounts for roughly a quarter of the realized
    loss — so part of that loss reflects a known factor exposure rather than a
    failure specific to asymmetry. Momentum alone explains 12% of the variation in
    in-position returns, so this is a material exposure rather than the whole
    story.

The sample is unchanged: n = 504, 8 January 2016 to 29 August 2025.


How to read this PR

Changes are grouped so that each group can be accepted or rejected on its own:

  • (a) Implementation defects — the paper said X, the code did Y, the code now
    does X. Three items. Nothing is in this group unless the published paper and
    the published code genuinely disagreed.
  • (b) Methodological changes proposed — the paper and the code agreed, and
    this PR proposes something different. Three items, each reversible without
    disturbing (a).
  • (c) Manuscript corrections — the code is defensible and the paper describes
    it wrongly.
  • (d) Prose corrected without a wrong figure.
  • (e) Provenance footnotes restored.

docs/CORRECTION_CHANGELOG.md has the full detail.
analysis/before_after_results.csv gives every changed figure with its published
value, its corrected value, and which category produced the change.


(a) Implementation defects corrected

Paper (4d21c69) Published code Now
a1 "Exit: signal reversal OR 4-week maximum" The hold branch was unreachable — its guard was always true — so positions closed as soon as the entry signal stopped firing Holds through quiet weeks; reverses on the opposing signal; expires after four returns
a2 Entry one period after the Friday signal Signals lagged inside the loop, then the position shifted again: two lags, while benchmarks used one One lag everywhere — headline, benchmarks, walk-forward, factors, costs, snooping
a3 Equation 5: mean squared deviations about the overall mean pos.var()/neg.var() — re-centres each subgroup, uses n−1, and returns a neutral-looking 1.0 where the statistic is undefined Equation 5 as printed; undefined cases return missing

a1 is the one that matters: 25 → 55 exposed weeks, and the dominant driver of
nearly every changed figure.


(b) Methodological changes proposed

These are judgement calls. The published paper and the published code agreed in
each case; this PR proposes departing from them.

b1. How to size a week in which a direction is held but no signal fires

Fixing a1 creates a state the published specification never had to describe,
because the published implementation could never reach it. The published rule was
"Rebalancing: Weekly (end of Friday close)" with Equation 10 evaluated at the
contemporaneous AI_t, and the published code did resize on every bar it held a
position — so paper and code agreed. Neither ever faced a held but unsignalled
week.

Proposed: evaluate the sizing equation weekly while a direction is held, as
the smaller of the two available extensions — it keeps the published rebalancing
frequency and the contemporaneous subscript, and changes no published sentence.
Freezing the notional at entry is the alternative; it is computed in the same run
and reported.

weekly (proposed) frozen (alternative)
Gross return −6.64% −7.57%
Sharpe −0.153 −0.173
Max drawdown −12.56% −14.29%
In-position weeks 55 55
Holding episodes 15 15
Turnover 52.00 49.15

Entries, exits, direction and exposure are identical. No conclusion depends on
the choice.
The argument against the proposal is in the changelog rather than
omitted.

b2. "Trades" reported as holding episodes and execution legs

The published paper defined trades as "position-change events divided by two" and
the code implemented exactly that — but the label said "completed round trips",
which that formula does not compute. For the momentum benchmark it reported 27
round trips for 54 directional holdings and 107 executions.

Proposed: report holding episodes and execution legs separately, with
resizing and turnover, from dated ledgers. Headline: 17 published "trades" → 15
episodes and 61 legs. The underlying returns are untouched by this item.

b3. Statistics from a single episode are not reported as performance

The corrected walk-forward opens one out-of-sample episode in eight test
years. A Sharpe ratio, a hit rate and an annualized return computed from one
episode are not estimates of anything — the published 60% hit rate meant three
weeks, and one episode would mean one up week and one down week.

Proposed: decline to print them. The rule is enforced in code, applied
wherever such statistics arise rather than table by table, and logged when it
fires. Suppressed values stay in full_pipeline_results.json so the decision is
checkable. Withheld figures are removed with the reason given, never replaced by
an unexplained placeholder.

The walk-forward table now reports what the procedure did — training window,
selected threshold, episodes opened. The activity count is the finding, and
it is a stronger one than any return from a single episode because it does not
depend on how that episode happened to turn out.


(c) Manuscript corrections

The paper mis-stated its own code in four places: the fast-alpha equation showed
a price difference where the code uses a percentage return (the printed version
divides yen by a percentage volatility and is dimensionally incoherent); the
position-size formula carried an unreachable 0.5 floor and a "no leverage" claim
when the real range is [1, 2] gross-notional units; hedge alpha was printed with
a time-varying rate differential that exists nowhere in the repository, the code
using a fixed −2% constant; and the tail-alpha window was described in weeks
where the code uses trading days.

Two disagreements were resolved in favour of the code, against the working
rule that the code gets fixed to match the paper. Both are flagged so they can be
overruled:

  • Monday-open execution. The paper specifies it; the code uses the Friday
    close. The Friday close is kept — but a false justification is removed. An
    earlier draft of this correction claimed Monday opening prices are absent from
    the dataset. They are present; the daily bars carry an Open column. The
    Friday-close proxy is a choice, and the paper now says so. Implementing
    Monday-open execution is on the backlog.
  • The EVT section is presented in the paper as characterising tail-alpha
    exceedances; the code fits absolute weekly returns. The section is relabelled
    as a weekly-return diagnostic. The rule says the code should have been changed
    instead; refitting is on the backlog, and the relabelling should not be
    mistaken for the fix.

(d) Prose corrected without a wrong figure

The manuscript sweep changed 38 figures across 21 locations. But a
figure-by-figure sweep does not catch a sentence whose argument depends on a
result that no longer holds, and those matter more.

The published paper argued that a modest gross edge survived measurement and was
not eliminated by costs. Every sentence resting on that architecture is wrong
under the corrected numbers regardless of its digits. Examples, none containing
an incorrect figure: the robustness section described a backtest "statistically
indistinguishable from zero" (defensible on the interval, but it presents a
losing strategy as a null one) and asked whether costs "erode strategy returns"
(presupposing returns to erode); the drawdown paragraph called the figure "only
modestly smaller" than buy-and-hold's, presenting as mitigating what is damning —
four-fifths of a permanently invested position's drawdown, incurred while exposed
in 55 of 504 weeks.

The break-even statement is the clearest case. The published paper reported a
break-even round-trip cost of 19.2 pips and read it as reassurance. That figure
was meaningful only while the gross return was positive. It is not that the
corrected break-even is larger, or harder to estimate: it does not exist, and
the paper now says so in the cost section, the abstract and the conclusion. It is
a cleaner statement of the null than the cost table it replaces.

One qualification is added rather than removed: costs remain small in magnitude
(0.38pp at two pips), but the model charges spread strictly in proportion to
notional with no per-order component, while the corrected specification
generates 31 resizings averaging 0.19 units — exactly the population a per-ticket
charge would fall hardest on. The reported drag is a lower bound.


(e) Provenance footnotes restored

4d21c69 carried nine table notes recording the paper's own earlier corrections.
Seven had been deleted during the drafting of this branch; all seven are
restored. Two survived — and keeping two of nine is not an editorial decision,
it is what happens when notes get dropped while the prose around them is
rewritten.

One deserves singling out. The data-snooping note recorded that the published
RC = 2.14 (p = 0.042) was not reproducible from any specification of the stated
candidate universe. That is the paper's most quotable statistic — the one number
in it that reported a significant result. Deleting the note deleted the record
that it was already known to be unreliable.

Each note is restored verbatim, with current values appended as a following
sentence rather than woven into the original. These notes are a dated record of
what was wrong and when; editing them to match today's numbers would destroy what
makes them worth keeping.

That preservation was enforced mechanically, not by eye: every one of the nine
original sentences is checked to appear in the current source as an exact
substring. The check earned its place — a first attempt inserted the words "then
reported" inside the factor-attribution sentence, a two-word rewrite of the
historical record that reading would not have caught. The claim of verbatim
restoration is worth something only because a machine enforced it.


Result changes

Full table with per-item attribution in analysis/before_after_results.csv.

Metric Published (4d21c69) Corrected
In-position weeks 25 of 504 55 of 504
Cumulative gross return +3.60% −6.64%
Sharpe 0.149 −0.153
Maximum drawdown −7.96% −12.56%
Holding episodes / execution legs 17 "trades" 15 / 61
Break-even round-trip cost 19.2 pips does not exist
Retail-wide net return +3.22% −7.02%
Momentum loading (in-position) −0.247, t = −0.51 −0.823, t = −3.73
Factor intercept (full sample) +0.00008 −0.00012
In-position factor sample 25 weeks 55 weeks
Walk-forward pooled return / episodes +2.46%, 3 trades +2.74%, 1 episode
Walk-forward Sharpe / hit rate 0.419 / 60.0% withheld — one episode
Low-VIX / high-VIX return +2.38% / +2.67% −5.08% / −1.65%
GBP/USD cross-market +17.18% −13.32%
SPY cross-market +11.66% +14.20%
GLD cross-market −30.26% −15.44%
Tail-alpha AI 0.17 0.03
Coverage-alpha AI 3.45 2.22

The GBP/USD sign flip is a finding, not a rounding. The published paper reads
+17.18% on GBP/USD as evidence that FX offers more favourable conditions for the
strategy than equities or gold. Corrected, it is −13.32%, and the flip is
attributable to the implementation fixes, not to the sizing proposal
: with the
implementation fixes and the frozen-notional alternative the figure is −14.11%.
Rejecting (b1) does not restore the published reading. The cross-market section's
claim about FX conditions no longer has a basis.

For the same reason, the widened cross-market spread is traceable: the
cross_market code block is byte-identical to the version this branch started
from, and rerunning it under the frozen alternative reproduces the intermediate
figures exactly. The movement is the sizing default alone; the input skewness
statistics are unchanged in all four markets.


Verification

  • 14 deterministic unit tests, all passing, covering the AI edge cases, the dated
    timing convention, entry, hold, expiry, reversal, simultaneous signals,
    no-signal periods, both sizing modes, resize cost accounting and reversal
    accounting.
  • The complete pipeline runs online and reruns identically with --offline.
  • Seven of the eight input files reproduce byte-for-byte on an independent
    download (see Data below).
  • Every figure in the manuscript was machine-checked against
    analysis/full_pipeline_results.json.
  • The nine original provenance sentences are machine-checked for verbatim
    presence.
  • After every rerun: n = 504 spanning 2016-01-08 to 2025-08-29; the factor
    intercept matches the strategy's own mean weekly return
    (−0.00011964 against −0.00012029); the low- and high-VIX returns compound to
    the full-sample return (−6.640684% against −6.640684%).

The PDF was rebuilt and inspected

paper/alpha-asymmetry.pdf is rebuilt from the corrected source. It compiles
clean: 27 pages, zero overfull boxes, zero underfull boxes, no undefined
references or citations, bibliography resolved against references.bib.

Both figures were regenerated from the current pipeline and compared
byte-for-byte against the committed versions. backtest_results.png is drawn
from the corrected return series. alpha_asymmetry_analysis.png is unchanged
since before the sizing change, which is correct: it shows the alpha
distributions, which do not depend on the strategy.

The PDF's own text was extracted and checked. The corrected figures are present;
none of the superseded ones appear anywhere.

A note on this section, because it is the third time this document has made a
claim about its own provenance.
The draft this branch started from asserted
that the PDF had been compiled and visually inspected when it had not. That was
removed and replaced with a statement that no LaTeX toolchain was available and
the PDF was stale. That statement was true when written and later stopped being
true
, when a self-contained engine was installed and the paper compiled. A
document asserting something about itself that has since become false is the
failure this pull request exists to correct, so the sequence is recorded rather
than tidied away, and the build was deliberately left until last so that the
claim and the artefact became true at the same moment.

Version, DOI and supersession — decisions for you

The manuscript now carries \paperver 3.1.0, incremented from the 3.0.0 you set
in f04ae08 for the July manuscript. Three things need your decision:

The version identifiers in this repository disagree with each other, and did
before this branch.
paper/alpha-asymmetry.tex carried 3.0.0 for the July
manuscript; CITATION.cff called the same work 2.1.0-dev; the last deposited
version is v2.0.1 (10.5281/zenodo.20635291). Neither 3.0.0 nor 2.1.0-dev
was ever deposited. CITATION.cff is set to 3.1.0-dev to follow the number
printed on the paper, with the disagreement documented in the file. Reconciling
them properly is yours.

Depositing mints a new Zenodo version DOI, which cannot be known in advance.
\paperdoi is therefore left as the concept DOI 10.5281/zenodo.18638784, which
resolves to the newest version. After deposit, record the new version DOI in
CITATION.cff and CLAUDE.md. The SSRN record (SSRN:6147567) is separate and
needs its own revision; Zenodo does not propagate to it.

We recommend posting a correction notice against the superseded record, not
merely depositing a new version. The headline result changes sign: +3.60% to
−6.64%. A reader who lands on v2.0.1 through a citation or a search result has no
way to know it has been superseded, and the specific claim they would take away —
that the strategy earns a small positive gross return — is wrong rather than
imprecise. Depositing a new version alone leaves that reader uninformed. This is
your call as author and it carries reputational weight either way, but asked
directly: we would post the notice.


Data, and a decision for you

The raw CSVs are not committed, and analysis/cache/ remains gitignored.
analysis/data_access.py gives the reason: Yahoo Finance data may be subject to
redistribution terms. Publishing eight files of vendor data in a public
repository is a licensing decision for the repository owner, not one an outside
contributor should make inside a correctness PR — so it is raised here for you
to decide rather than taken.

What is provided instead: analysis/fetch_data.py downloads the inputs and
verifies each file's SHA-256 against the committed manifest, so a fresh clone can
obtain the data and confirm it is the same data.

Seven of eight hashes reproduce byte-for-byte. Six of the eight series are FX
spot rates or index levels, which carry no corporate-action adjustment and
structurally cannot drift; GLD paid no distribution over the window. SPY, a
distributing ETF fetched with auto_adjust=True, has its entire history rescaled
by every new distribution — the only file in the set that could differ, and it
did. That is the expected outcome rather than a near miss. The difference moves
five values in the SPY cross-market row in their fifth or sixth significant
figure, each rounding to the same printed number, and nothing else in the
pipeline.


Reproduction

python3.12 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/python -m pytest -q
.venv/bin/python analysis/fetch_data.py
.venv/bin/python analysis/full_pipeline.py --offline

Questions for you

  1. The data licensing question above — commit the raw CSVs, or keep the
    fetch-and-verify approach?
  2. (b1), the sizing proposal. Weekly evaluation or frozen at entry? Both are
    computed; no conclusion depends on it.
  3. (b2) and (b3) — the trade-accounting redefinition and the refusal to
    report single-episode statistics. Both are reversible.
  4. (c2), the two exceptions — keeping the Friday close despite the paper
    specifying Monday open, and relabelling the EVT section rather than refitting
    it. Either could be done properly instead.
  5. The stale PDF. You have the toolchain.
  6. Whether an approved EUR–JPY rate or forward series can be supplied, which
    would let hedge alpha stop being a constant multiplied by a correlation.

Provenance

This branch began from an unreviewed draft produced by an AI agent, committed
unmodified as 5135bac and labelled as such. That commit was audited rather
than adopted
: its claims were checked against 4d21c69, its manuscript edits
were reviewed line by line, several were reversed, and every change that survives
is justified in this PR and in the changelog against the published paper. It
carries no authority and is kept in the history only so that the work done after
it is separately reviewable.

docs/REVIEW_NOTES.md is the working audit record from that process. It is not
part of the argument here.

No branch has been pushed, no pull request opened, and nothing merged.

🤖 Generated with Claude Code

Tofik (plut777) and others added 17 commits September 2, 2026 19:28
Unmodified import of the correction branch produced by another AI agent
(Codex) and delivered as alpha-asymmetry-corrected-branch.zip. It is
committed here as a single, clearly labelled starting point so that every
subsequent change on this branch is separately reviewable.

This commit is a submission under audit. Its contents are not endorsed and
have not been verified. In particular the manuscript changes in
paper/alpha-asymmetry.tex are known to include at least one edit that
rewrites the specification to match the code rather than correcting the
code to match the specification.

Base: upstream/master 4d21c69
Records, for every change on this branch, what changed, who originated it,
and whether it is a bug fix or a specification decision.

Findings from this first pass, all verified against the upstream history:

- All four originally reported implementation bugs are confirmed in
  4d21c69:analysis/full_pipeline.py. Three of them were genuine code-vs-paper
  defects fixed in the correct direction.
- The rebalancing rule in the manuscript was rewritten from "Weekly (end of
  Friday close)" to "none within an episode" with no disclosure anywhere. The
  original code also resized weekly in effect, so paper and code had agreed.
- Freezing position size at entry and redefining "trades" as holding episodes
  are specification decisions, not implementation bugs, and are currently
  listed as bugs.
- The claim that Monday opening prices are absent from the dataset looks
  false; daily bars carry an Open column. Verification pending.
- Table provenance footnotes recording the paper's own earlier corrections
  were deleted.
- The pre-correction "before" figures Codex hard-codes are accurate.

No analysis code or manuscript text changed in this commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seven of the eight recorded SHA-256 hashes reproduce byte-for-byte on an
independent download. SPY differs because it is fetched with auto_adjust=True
and its history is back-adjusted by dividend factors between fetches.

Rerunning the full pipeline on the fresh data changes exactly five values,
all in the SPY cross-market row, all in the fifth or sixth significant
figure. No number printed in the manuscript changes. The EUR/JPY analysis is
identical throughout.

Constraint checks on the rerun: n = 504 spanning 2016-01-08 to 2025-08-29;
factor intercept -0.00013783 against a mean weekly return of -0.00013928;
low- and high-VIX returns compound to -7.573378% against a full-sample
-7.573378%. Both identities hold.

Also resolves EX-3: the downloaded frames carry an Open column, so the
manuscript's claim that Monday opening prices are absent from the dataset is
false.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The raw CSVs stay out of the repository: Yahoo Finance data may be subject to
redistribution terms, which is why analysis/cache/ was gitignored to begin
with. Publishing them is the repository owner's decision to make knowingly,
so it is raised in the PR text rather than taken here. What a fresh clone
needs instead is a way to fetch the inputs and confirm they are the right
ones.

- add analysis/fetch_data.py, which downloads the eight series and checks
  each file's SHA-256 against the committed manifest, separating expected
  differences from unexpected ones and failing only on the latter;
- record per-series hash stability in analysis/data_access.py and annotate
  the committed manifest with it. Six series are FX spot rates or index
  levels with no corporate-action adjustment and cannot drift; GLD made no
  distribution over the window; SPY is a distributing ETF fetched with
  auto_adjust=True and is the only file in the set that can change. The
  annotation adds fields only: no recorded hash or timestamp was altered, so
  data_manifest.json remains the record of the run that produced the
  committed results;
- stop full_pipeline.py overwriting data_manifest.json with its own observed
  hashes. That destroyed the reference fetch_data.py compares against, so
  after a single pipeline run a reader would have been checking their data
  against itself. The observed manifest now goes to a separate, gitignored
  data_manifest.observed.json;
- rewrite the README Data section with the correct expectation.

No analysis result changes. Verified by running the pipeline before and
after and diffing every value in full_pipeline_results.json: zero
differences. pytest: 11 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Records three instructions to apply in later steps so they survive: the
sizing write-up must not present weekly resizing as a pure restoration or
-7.57% as a baseline being departed from; the provenance footnotes deleted
from 4d21c69 must be restored; and the changelog and PR text must be split
into implementation defects, proposed methodological changes, and manuscript
corrections, with the 25-of-504 exposure figure stated plainly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The file mixes CRLF and LF line endings. Editing it through Python's text
mode rewrote every CRLF line as LF, which turned a one-line addition into a
71-line diff and would have looked, in review, like an unexplained rewrite of
the ignore rules.

Restores the file byte-for-byte from its previous state and re-inserts only
the single new entry, matching its neighbours' terminator. No rule other than
analysis/data_manifest.observed.json is added, removed, or reordered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…rule

Resolves the sizing conflict the Codex branch introduced and left
self-contradictory: Equation 10 reads "contemporaneous AI_t" while the same
paragraph asserts the size is fixed at entry.

Verified against 4d21c69, the July 2026 version: the manuscript said
"Rebalancing: Weekly (end of Friday close)", Equation 10 subscripted AI by t,
and the original code recomputed the size from the current ai_20w on every bar
where the entry signal fired -- which, given the dead exit branch, was every
bar on which a position was held. Paper and code agreed on weekly resizing.
Codex departed from both and rewrote the rebalancing line to match its code,
disclosing the change nowhere.

This is a SPECIFICATION DECISION, not a bug fix, and neither option is a pure
restoration. Repairing the dead exit branch creates held-but-unsignalled weeks
that the published rule never had to size, because the original implementation
could not reach that state. Weekly resizing is adopted as the smaller
extension: it keeps the manuscript's stated rebalancing frequency and Equation
10's contemporaneous index, and changes no published sentence. Freezing at
entry is reported as the alternative, and the argument against weekly resizing
-- that it promotes AI toward a second timing signal -- is recorded in
docs/REVIEW_NOTES.md rather than omitted.

- run_asymmetry_strategy takes sizing="weekly" (default) or sizing="entry";
  resizing changes the notional only, never opening or closing an episode,
  flipping direction, or resetting the four-return holding clock;
- both variants run in the pipeline and are reported under sizing_variants;
- trade_ledger column position_size renamed entry_position_size, since the
  notional now varies within an episode;
- corrects the strategy.py claim that Monday-open prices are unavailable: the
  daily bars carry an Open column, so the Friday-close proxy is a choice.

Results, weekly vs entry: return -6.64% vs -7.57%, Sharpe -0.153 vs -0.173,
drawdown -12.56% vs -14.29%, turnover 52.00 vs 49.15 units, execution legs 61
vs 30. Entries, exits, direction, hit rate, 55 in-position weeks and 15
holding episodes are identical under both. No conclusion in the paper turns on
the choice.

Constraints: n = 504, 2016-01-08 to 2025-08-29. Factor intercept -0.00011964
against a mean weekly return of -0.00012029. Low- and high-VIX returns
compound to -6.640684% against a full-sample -6.640684%. Both identities hold.

pytest: 14 passed, from 11. One test encoded the frozen-size specification and
was rewritten to the weekly one; three were added. Changing a test to match
changed behaviour is disclosed deliberately -- the rewritten assertions are
part of the specification decision, not incidental maintenance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ssification

Answers two questions on the record rather than by inference.

Cost scaling: analysis/strategy.py:329 defines unit_cost as a cost per unit of
notional, and every event multiplies it by the notional actually traded. There
is no fixed per-leg term in the model. Verified empirically -- total units
charged equal total turnover exactly under both sizing modes. So doubling the
legs is irrelevant and the 5.8% turnover rise is what matters; cost rose 5.6%.
Resizes are 51% of legs but 11% of turnover, because ai_20w is a 20-week
rolling statistic and moves slowly.

Carries the caveat that this is partly a property of the cost model: it has no
per-ticket component, so it cannot penalise 31 extra small orders. Added to
the backlog.

Records the full cost sensitivity table and the White RC and Hansen SPA
figures under weekly sizing alongside the frozen ones, and notes that
break-even is undefined under both because the gross return is already
negative.

Reclassifies the rewritten test assertions out of the constraint-check block
and into the specification decision, so they are reviewed as category (b)
methodological changes rather than as maintenance.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The walk-forward reported a 2.74% pooled return, a 0.3186 Sharpe ratio, a 50%
hit rate and a 0.35% annualized return from new_episodes = 1. A Sharpe ratio,
a hit rate and an annualized return are sample statistics; from one holding
episode they describe the path that happened and carry no information about
the path that would be expected. The 50% hit rate is one profitable week and
one unprofitable week.

- full_pipeline.py gains MIN_EPISODES_FOR_INFERENCE = 2 and emits
  inference_supported per year and pooled, with a reporting_rule string. Two
  is not a threshold at which inference becomes sound; it is the point below
  which these quantities stop being statistics at all;
- the pipeline logs the suppression when it fires, so the decision is visible
  in the run output rather than being an absence;
- every suppressed quantity is still computed and still written to
  full_pipeline_results.json. That file is the audit record, and deleting
  numbers from it would make the reporting decision unverifiable;
- tab:oos is rebuilt around what the procedure did -- test year, training
  window, selected threshold, new holding episodes. The performance columns
  are removed rather than filled with dashes, and the note states that their
  absence is deliberate;
- the prose now argues the point positively: the finding is the activity count
  itself, which is a stronger result than any return from one episode because
  it does not depend on how that episode turned out.

Raises but does not resolve a consistency question: the threshold-sensitivity
table reports a Sharpe ratio and hit rate for the 1.25 threshold from the same
single episode. The identical objection applies. Left untouched because item 3
named the walk-forward, and recorded in REVIEW_NOTES for a decision.

No analysis result changes. n = 504, 2016-01-08 to 2025-08-29; both identities
hold. pytest: 14 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Step 4, with the approved extension of step 3 folded in.

Sensitivity table (approved scope extension): the 1.25 threshold reported a
Sharpe ratio and hit rate from one holding episode -- the same 2020 episode
the walk-forward selects. MIN_EPISODES_FOR_INFERENCE, supports_inference()
and INFERENCE_REPORTING_RULE are now module-level and applied wherever such
statistics are produced, so a quantity withheld in one table cannot reappear
in another. Both suppressions are logged when they fire.

The defect there is row-level rather than column-level -- thresholds 0.50,
0.75 and 1.00 rest on 25, 15 and 4 episodes -- so removing the columns would
have deleted valid information for three rows to suppress one. The table now
leads with Episodes and Exposed Weeks, which vary monotonically where the
return does not, and the 1.25 row's performance cells are replaced by a single
spanned "single episode: not reported" with the reason given. Nothing is
dashed and nothing is unexplained.

Momentum finding: previously visible only in the factor table and in two
passing adjectives. Now argued in the text from the current weekly-sizing
column -- b = -0.046, t = -2.08, p = 0.038 full sample; b = -0.823,
t = -3.73, p = 0.00019 in position, against -0.007 (p = 0.71) and -0.247
(p = 0.61) in the published version.

Written into the factor-table note with the pre-correction values, into two
new paragraphs of the Factor Attribution discussion, into the Discussion as a
fifth failure mode ("It Is Not Trading Asymmetry"), and into the abstract.
The substantive claim: read on the exposure sample the strategy behaves close
to a one-for-one short position in twelve-week momentum on the pair it trades,
a mechanical consequence of a short leg that fires after the price has run
above its sixty-day average. An asymmetry strategy that is a disguised
short-momentum bet is not harvesting asymmetry, which sharpens the null rather
than softening it.

Caveats written in: the full-sample p = 0.038 is marginal and would not
survive this paper's own Bonferroni discipline at family size six; the
full-sample coefficient is mechanically attenuated by 449 structurally zero
weeks; Mom is a single-pair rule, not the cross-sectional factor; 55 weeks
from 15 episodes is small and serially dependent.

No analysis result changes. n = 504, 2016-01-08 to 2025-08-29; both identities
hold. pytest: 14 passed.

Cannot compile the PDF -- no LaTeX toolchain here -- so paper/alpha-asymmetry.pdf
is stale. Structural checks were run on the source instead. docs/PROPOSED_PR.md
still claims the PDF was compiled and inspected; that is Codex's claim and must
be removed or re-earned.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Step 5, plus the PDF disclosure.

Cost accounting, comments only, no executable line changed:

- pip_size = 0.01 is yen-pair specific and is now documented at the point of
  use. A pip is 0.01 only for pairs quoted to two decimals; for GBP/USD it is
  0.0001 and for SPY or GLD the notion does not apply. Passing JPY pips to a
  non-JPY market overstates cost by a factor of 100 and raises nothing.
  Nothing is currently mispriced -- the cost table runs only on EUR/JPY and the
  cross-market section runs at zero cost -- but that is a property of the
  callers, not of the default, and the comment says so;
- the absence of any fixed per-leg or per-ticket term is stated where the cost
  is computed, so total charged units equalling total turnover is documented
  rather than left to be derived;
- the resize branch's reachability is documented, including that it is
  unreachable under sizing="entry", which is why sizing is a parameter and not
  a fork.

The other half of item 5 -- removing the dead resize branch -- is withdrawn by
consequence rather than skipped. It was dead only under Codex's frozen sizing.
Under weekly sizing it prices all 31 within-episode notional changes, 11% of
turnover.

docs/PROPOSED_PR.md: deletes the claim that "LaTeX was compiled and the
resulting PDF was rendered and visually inspected." That claim originated with
Codex, is false for this branch, and must not reach the repository owner inside
a pull request about unverified assertions. Replaced with an explicit statement
that the committed PDF is Codex's stale September build, that no LaTeX
toolchain was available, and exactly which structural checks were run on the
source instead -- brace balance, matched table/tabular/equation environments,
and per-table column counts including multicolumn spans -- together with what
those checks do not establish. The owner has the toolchain and should rebuild
and inspect.

Also records in REVIEW_NOTES.md that the sensitivity-table deviation from the
instruction to remove columns was proposed and approved explicitly, with the
reason, and that leading the table with Episodes and Exposed Weeks is a
substantive improvement rather than formatting: activity falls monotonically
where the return ordering is noise.

pytest: 14 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…output

Sweeps the manuscript to the weekly-sizing results and lands the three
statements agreed after step 2. Every table figure was machine-checked against
analysis/full_pipeline_results.json; all match.

Specification text, carried over from step 2 and not previously in the paper:

- Position Sizing now states that the sizing equation is evaluated at each
  Friday close while a direction is held, and the execution list restores
  "Rebalancing: weekly at the Friday close" in place of Codex's "none within
  an episode";
- a paragraph discloses the sizing choice as a choice: repairing the exit rule
  creates held-but-unsignalled weeks the published specification never had to
  size, both readings are extensions to that state, weekly is adopted as the
  smaller one, and no conclusion depends on it;
- the false claim that Monday opening prices are absent from the dataset is
  replaced with a statement that the Friday-close proxy is a choice, since the
  daily bars carry an opening price.

Break-even, stated rather than dropped: the published 19.2-pip figure was
meaningful only while gross return was positive. There is now no break-even
cost at all -- the strategy does not break even at any cost because it does
not break even at zero cost. Written into the cost section, the cost-table
note, the abstract and the conclusion, as a stronger statement of the null
than the cost table it replaces.

Cost-model limitation: spread is charged strictly in proportion to notional
with no fixed or minimum per-order component, while the corrected
specification generates 31 resizings averaging 0.19 units -- exactly the
population a per-ticket charge falls hardest on. The reported drag is a lower
bound, and "costs are immaterial" is a claim this model cannot make.

Data-snooping footnote explaining why the RC and SPA statistics are identical
across sizing specifications: both take a maximum over the candidate universe,
that maximum belongs to the seeded random candidate, and only the bootstrap
covariance sees a change confined to the asymmetry rule.

Prose contradicted by the corrected numbers without containing a figure:

- the robustness intro described an in-sample backtest "statistically
  indistinguishable from zero" and asked whether costs "erode strategy
  returns"; both presuppose an edge that no longer exists;
- the drawdown paragraph called -12.56% "only modestly smaller" than
  buy-and-hold's -15.78%; it now notes that losing four-fifths of a
  permanently invested position's drawdown while exposed in 55 of 504 weeks
  is not evidence of risk control;
- the trading-frequency paragraph said "no resizing", which is false under
  weekly sizing.

The momentum finding is added to the conclusions, which previously carried it
nowhere despite it being a fifth failure mode in the discussion.

No analysis result changes. pytest: 14 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…gory

Three record items plus the footnote restoration.

Footnotes: 4d21c69 carried nine "an earlier version of this table..." notes
recording the paper's own prior corrections. Codex deleted seven and kept two,
which is not a systematic editorial decision. All seven are restored. Four were
named in review; the other three -- the cross-market MR/TF/HAT categories, the
cost table's net return rising with costs, and RC = 2.14 -- were found by
enumerating the notes at the base commit rather than by working from the list.

Each is restored verbatim, with any current value appended as a following
sentence rather than woven into the original. These notes are a dated record of
what was wrong and when; editing them to agree with today's numbers would
destroy what makes them worth having. Verified mechanically that all nine
original sentences appear as exact substrings.

That check caught a violation: the first attempt inserted "then reported"
inside the factor-attribution sentence, a two-word rewrite of the historical
record. Fixed, and the check is kept.

The 2020 subsample return of 2.74% is identical under both sizing
specifications and identical to the lone walk-forward episode, because 2020
contains a single holding episode whose notional was never revised. It looks
exactly like a figure someone forgot to update, so the subsample table note now
says why it is not.

Cross-market traceability, recorded before anyone asks: the spread widened
toward a more striking result, so the provenance is established. The
cross_market code block is byte-identical between the Codex import and HEAD,
and rerunning it with sizing="entry" and no other change reproduces Codex's
committed figures exactly. The movement is the sizing default alone; the input
skewness statistics are unchanged.

REVIEW_NOTES records the prose fixes as their own category, separate from the
count of figures changed: "statistically indistinguishable from zero", "do
trading costs erode strategy returns", "no resizing", and the drawdown framing
were all wrong without containing a wrong digit. The general form is that the
published paper argued a modest edge survived measurement and was not
eliminated by costs; every sentence resting on that architecture is wrong under
the corrected numbers regardless of its digits, and such sentences must be
hunted by reading for the argument rather than by checking figures.

pytest: 14 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Rebuilds the correction narrative on 4d21c69, the published July 2026 version,
rather than on the unreviewed intermediate draft this branch started from. That
draft was never adopted upstream and its figures have no standing with the
repository owner, so every before/after comparison now runs published versus
corrected.

before_after_results.csv is rebuilt on the published baseline with a per-row
"stage" column attributing each change to the correction that produced it --
implementation defect, accounting redefinition, sizing choice, or reporting
decision -- and a note explaining it. Rows added for volatility, Sortino,
turnover, execution legs, the four subsample returns, all four momentum
coefficients, break-even, and the withheld walk-forward statistics.

The GBP/USD sign flip is called out explicitly: +17.18% published to -13.32%
corrected, attributable to the implementation fixes rather than to the sizing
proposal, since the frozen-notional alternative gives -14.11%. The published
paper's claim that FX offers more favourable conditions rested on that 17%, and
rejecting the sizing proposal does not restore it.

Changelog and PR restructured into the agreed categories: (a) implementation
defects, where the paper and code genuinely disagreed and the code was wrong;
(b) methodological changes proposed, where they agreed and we are departing --
the sizing rule for held-but-unsignalled weeks, the trade-accounting
redefinition, and the refusal to report single-episode statistics; (c)
manuscript corrections, split into the paper mis-stating its own code and the
two disagreements resolved in the code's favour against the working rule; (d)
prose wrong without a wrong figure; (e) restored footnotes; (f)
reproducibility. The sizing freeze is out of "confirmed implementation bugs"
entirely -- it was never a defect.

The 25-of-504 exposure figure leads both documents as the single most important
number, with the explanation that it is why the published null result had
little content.

The footnote entry names the data-snooping note specifically: RC = 2.14
(p = 0.042) was the paper's most quotable statistic and the only significant
one in it, and the deleted note recorded that it was already known to be
irreproducible. Records that keeping two of nine is what happens when notes are
dropped during a prose rewrite rather than an editorial choice, and records the
verbatim-restoration violation the substring check caught -- as method, since
the claim of verbatim restoration is only worth something because it was
machine-enforced.

The Codex-labelled-it-a-bug framing is dropped throughout both documents.
Codex remains as a short provenance note in each, saying what 5135bac is and
that it was audited rather than adopted, and in the sizing evidence trail where
it is load-bearing. REVIEW_NOTES.md gains a header stating that it is the
working audit record and not part of the PR's argument.

pytest: 14 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Author block: Tofik Israfilov added as second author, affiliated to Dissensus,
tofiq@dissensus.ai. No ORCID supplied, so none is printed. The
"Sole author" contribution line is replaced with an itemised statement --
original research, design, analysis code and manuscript to M. Farzulla; audit
of the published version against its code, identification of the specification
mismatches, the decisions resolving them, and the correction record to
T. Israfilov, with an explicit statement that he did not author the analysis
code.

Confidence pass on the two new claims. The break-even subsection survived
unchanged. The momentum section did not, and one problem was not a matter of
emphasis:

The text argued that the strategy's loss was partly explained by a momentum
exposure "one that lost money over this sample", citing -18.92%. That figure
belongs to the twenty-week benchmark, not to the twelve-week regressor, and
the argument inverts: the strategy is SHORT momentum, so a momentum factor
that lost money would have made it money. The twelve-week factor in fact
gained 2.63% over the full sample and 1.93% over the 55 exposed weeks. The
conclusion survives -- momentum rose and the strategy was short it, costing
roughly -1.6 percentage points, about a quarter of the -6.64% loss -- but the
reason given was backwards.

Also corrected: R^2 of 0.203 was attributed to momentum alone when it is the
three-factor figure (momentum alone is 0.122); "close to a one-for-one short
position" now carries the 95% CI [-1.26, -0.39] and is bounded accordingly;
"largely explained" replaced, since twelve percent leaves seven-eighths
unexplained; the mechanism is offered rather than asserted; the section
heading changes from "It Is Not Trading Asymmetry" to "It Is Partly Trading
Momentum"; and the R^2 comparison no longer reads as a like-for-like rise
between two different samples.

Applied in all four places the claim appears -- abstract, discussion, factor
section, conclusion -- and every figure re-verified against
full_pipeline_results.json and by independent recomputation of the confidence
interval, the partial R-squared and the exposure contribution.

REVIEW_NOTES records the inverted argument as its own finding: it sat in the
section presented as the paper's new contribution, it passed the numbers sweep
because its figure was correctly computed and attached to the wrong claim, and
it was caught by asking what the regressor itself returned rather than by
proofreading. Also records the contrast with the break-even section, and the
PR text's own now-false claim about the LaTeX toolchain.

pytest: 14 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
AI disclosure rewritten with symmetric attribution: both authors are named as
directing AI assistance in their own phase, M. Farzulla for the original
version and T. Israfilov for the corrections. The earlier draft named only the
latter, which would have implied the former was not answerable for the
assistance used in his own work. Retains the statement that the verification
apparatus caught errors that review by reading did not, and adds that the
apparatus is part of the contribution rather than housekeeping -- it is what
lets these corrections be checked instead of trusted, and it is reusable.

Version bumped 3.0.0 -> 3.1.0. The reasoning offered for this was checked and
was partly wrong: 3.0.0 was not set by the intermediate draft but by Studio
Farzulla in f04ae08 on 2026-07-21. It was however never deposited, which
CITATION.cff confirms. The repository carried three disagreeing version
identifiers before this branch -- 3.0.0 in the .tex, 2.1.0-dev in CITATION.cff,
and v2.0.1 as the last Zenodo deposit -- and reconciling them is on the backlog
as the author's call. Note that \paperver and \paperdoi are defined but never
rendered, so neither appears in the compiled PDF.

CITATION.cff: second author added to both the authors list and the preferred
citation, version aligned to 3.1.0-dev with the prior disagreement documented
in the file, repository-code corrected from studiofarzulla to dissensus-ai.

PR text: the summary bullet still carried the pre-confidence-pass phrasing
("substantially a short momentum bet"); brought in line with the manuscript.
The PDF sections of both documents are rewritten -- they had asserted that no
LaTeX toolchain was available and the PDF could not be rebuilt, which was true
when written and is no longer. The sequence is recorded rather than tidied
away, since a document asserting something about itself that stopped being
true is the failure this PR exists to correct. Adds the recommendation to post
a correction notice against the superseded record, given a signed headline
moving from +3.60% to -6.64%.

PDF rebuilt with Tectonic: 27 pages, zero overfull and underfull boxes, no
undefined references, no LaTeX warnings, bibliography resolved. Both figures
confirmed byte-identical to a fresh pipeline run before compiling.

pytest: 14 passed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The title page reads "Tofik Israfilov"; the GitHub account, the git commit
authorship on this branch and the personal email all read "tofigisrafilov".

Left as-is for the pull request on instruction, and recorded as a decision owed
before deposit. Once a version carries a DOI the author string propagates into
Scholar, ORCID and Crossref, and disambiguation services key on exact strings,
so the two forms would be indexed as two people. Free to fix now, expensive
after the first citation. Notes that registering an ORCID is what makes the
question survivable if both forms are going to exist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant