Systemicalpha

Collapse

kaya-collapseCorecollapse1 cr

Variants

One module per value type. They behave identically; pick the one matching the type you are wiring.

NameType
Collapse Booleanboolean
Collapse Doubledouble
Collapse Floatfloat
Collapse Integerinteger
Collapse Stringstring

A sweep asks a question many times at once. Collapse answers it once.

Fan a strategy out over a basket of assets, or a range of indicator settings, and you get many parallel candidates. But an order can only be sent once. Collapse applies your rules and forwards exactly one, rejoining the branch into one the rest of the strategy can act on.

If Filter says "give me the best few", Collapse says "give me the one". It is the normal way to end a sweep.

The fan-out is described on the For Each page; ranking several survivors onto numbered outputs on the Filter page. This page is about choosing the winner, and it is mostly worked examples.


Where Collapse Sits

flowchart LR
    S["Sweep<br/>5 combinations"]
    subgraph FAN["Fanned-out branch"]
        direction TB
        C0["Candidate 0"]
        C1["Candidate 1"]
        C2["Candidate 2"]
        C3["Candidate 3"]
        C4["Candidate 4"]
    end
    CO["Collapse<br/>Take = 1"]
    SEL["Selection"]
    IDX["Selection index"]
    D["Rest of the strategy<br/>one branch"]
    S --> C0 & C1 & C2 & C3 & C4
    C0 & C1 & C2 & C3 & C4 --> CO
    CO --> SEL --> D
    CO --> IDX
    style CO fill:#1565c0,color:#fff
    style SEL fill:#c8e6c9,stroke:#2e7d32
    style IDX fill:#c8e6c9,stroke:#2e7d32
    style D fill:#fff3e0,stroke:#ef6c00

Everything downstream of Selection sees one ordinary series, as if the sweep had never happened.


The Selection Rule

Let C={c0,…,cn−1}C = \{c_0, \ldots, c_{n-1}\} be the candidates in sweep order. Three stages follow.

Stage 1 — Eligibility. A candidate survives if it carries data and its Condition ends true:

S={ ci∈C  :  ci≠∅  ∧  last(condi)=true }S = \{\, c_i \in C \;:\; c_i \neq \varnothing \;\wedge\; \text{last}(\text{cond}_i) = \text{true} \,\}

Without Condition wired the second clause drops, and every non-empty candidate survives.

Stage 2 — Ranking. Survivors are ordered by the last value of their Sort series, si=last(sorti)s_i = \text{last}(\text{sort}_i):

winner={arg⁡max⁡ci∈SsiOrder=descarg⁡min⁡ci∈SsiOrder=asc\text{winner} = \begin{cases} \arg\max_{c_i \in S} s_i & \text{Order} = \texttt{desc} \\[4pt] \arg\min_{c_i \in S} s_i & \text{Order} = \texttt{asc} \end{cases}

Without Sort wired no reordering happens at all: survivors keep input order, and Order is irrelevant.

Stage 3 — Take. Take is fixed at 11: the first ranked candidate becomes the Selection, the rest are discarded. That fixed 11 is the whole difference from Filter.

Both resolve stably: survivors sharing a Sort value keep their input order, so the earlier wins.

flowchart TB
    A["All candidates<br/>in input order"] --> B{"Condition wired?"}
    B -- "no" --> C["Every non-empty<br/>candidate survives"]
    B -- "yes" --> D["Keep candidates whose<br/>Condition ends true"]
    C --> E{"Any survivor?"}
    D --> E
    E -- "no" --> F["Selection empty<br/>branch dies"]
    E -- "yes" --> G{"Sort wired?"}
    G -- "no" --> H["Keep input order"]
    G -- "yes" --> I["Rank by last Sort value<br/>Order = asc or desc<br/>ties keep input order"]
    H --> J["Take the first<br/>Take = 1"]
    I --> J
    J --> K["Selection + Selection index"]
    style F fill:#ffcdd2,stroke:#c62828
    style K fill:#c8e6c9,stroke:#2e7d32
    style D fill:#e3f2fd,stroke:#1565c0
    style I fill:#e3f2fd,stroke:#1565c0

Inputs and Outputs at a Glance

WiringRole
ItemsThe candidates, one per sweep combination, in sweep order.
Condition (optional)One boolean series per candidate. Only its last value is read.
Sort (optional)One numeric series per candidate. Only its last value is read.
Orderdesc — highest Sort value wins. asc — lowest wins. Absent, it behaves as desc.
TakeFixed at 1.
SelectionThe winning candidate's series.
Selection indexWhich candidate won, counting from 0 in input order.

Two habits to form now. Only the last value of Condition and Sort matters — they are verdicts on a candidate, not bar-by-bar signals. And Items are in sweep order, which Selection index reports.


Worked Example 1: One Sector Out of Five

The idea. Rather than spread capital across every sector, hold the one with the strongest risk-adjusted momentum, and hold nothing that is falling.

The setup. Daily closes for five SPDR sector ETFs from Alpha Vantage over the 63 trading days from 2026-06-05 to 2026-09-04 (64 closes, 63 daily returns, roughly one quarter). One combination per ticker, in this order: XLE (energy), XLK (technology), XLF (financials), XLV (health care), XLU (utilities).

The Sort statistic. Quarterly return over annualised volatility — a simple risk-adjusted momentum score:

score=Pend/Pstart−1σdaily×252\text{score} = \frac{P_{\text{end}} / P_{\text{start}} - 1}{\sigma_{\text{daily}} \times \sqrt{252}}

where σdaily\sigma_{\text{daily}} is the sample standard deviation of the 63 daily log returns and 252\sqrt{252} annualises it. Order is desc; Condition requires a positive quarterly return — a gate, not a ranking. It sets who reaches the podium, not who tops it.

The Candidate Table

IndexSectorStart (06-05)End (09-04)ReturnAnn. volatilitySort valueCondition
0XLE57.6764.06+11.08%22.18%0.500✅ true
1XLK180.30187.28+3.87%31.92%0.121✅ true
2XLF52.3058.10+11.09%12.75%0.870✅ true
3XLV153.01171.45+12.05%18.62%0.647✅ true
4XLU44.3543.08−2.86%14.63%−0.196❌ false

Check the arithmetic: 58.10/52.30−1=0.110958.10 / 52.30 - 1 = 0.1109, and 0.1109/0.1275=0.8700.1109 / 0.1275 = 0.870.

What happens. XLU falls at Stage 1: it declined, so its Condition ends false. The four survivors rank descending — XLF (0.870), XLV (0.647), XLE (0.500), XLK (0.121). Take is 1, so:

  • Selection = the XLF price series
  • Selection index = 2

Why This Example Is Worth Studying

The sector with the highest return did not win. XLV rose 12.05% against XLF's 11.09%, but was 46% more volatile, and the score reversed the ordering. XLE is starker: XLF's return earned at 22.18% volatility instead of 12.75% lands two places lower.

The lesson: the module does not know what "best" means — your Sort input defines it. Wire raw return and XLV wins; wire risk-adjusted return and XLF does. That choice is the real work.

Notice how the optional inputs divide the labour. Condition is a hard veto — is this acceptable at all? Sort is a soft preference — among those, which is better? A veto through Sort alone fails when every candidate is bad: something still wins.


Worked Example 2: When Nothing Deserves Your Money

The idea. Sweep an indicator setting instead of an asset. Hold XLK only while its close sits above its NN-day simple moving average, letting the sweep choose NN — but refuse to trade unless the winner made money.

The setup. XLK daily closes from Alpha Vantage. Five lookbacks, N∈{10,20,30,40,50}N \in \{10, 20, 30, 40, 50\}, in that order. Averages warm up over earlier bars; the rule runs across the 49 trading days from 2026-06-26 to 2026-09-04. Sort is the rule's realised return, Order desc, Condition demands it be positive.

The Candidate Table

IndexLookback NNDays investedWinning daysRule returnCondition
01019 / 4942.1%−2.80%❌ false
12021 / 4938.1%−9.99%❌ false
23024 / 4945.8%−0.62%❌ false
34028 / 4942.9%−6.07%❌ false
45031 / 4945.2%−3.53%❌ false

Simply holding XLK over the same window returned +3.41%. Every swept rule lost money, and every one lost to doing nothing clever.

What happens. All five fail the Condition. The survivor set is empty, Selection is empty, everything downstream is dead, no position is taken. That is the module working, not failing: the honest answer to "which lookback?" was none of them.

The Same Sweep Without a Condition

Unwire Condition and re-run the same numbers. Nothing is eliminated, all five rank by Sort descending, and N=30N = 30 wins at −0.62% — Selection index 2. The strategy dutifully buys the least-bad loser.

Sort ranks; it never rejects. If your strategy must be able to stand aside, only Condition lets it.


Ties, and Values That Are Not Numbers

A Real Tie

Ties are rare with a statistic whose decimals never coincide, and the norm with counting statistics. Split the same five sectors over the same window into thirteen blocks of five trading days (the last runs three) and count the blocks that closed up:

IndexSectorPositive blocks (of 13)
0XLE7
1XLK6
2XLF10
3XLV8
4XLU6

Wire this count to Sort with Order = asc — a contrarian sweep hunting the weakest sector — and XLK and XLU tie at 6. Ranking is stable, so input order decides: XLK wins, Selection index 1, being combination 1 against XLU's 4.

With coarse statistics, the order of your sweep is a silent tie-breaker. Use a finer statistic if that bothers you.

Unreadable Sort Values

A Sort value that cannot be read as a number — an empty series, or something non-numeric — is not an error and does not stop the strategy. The candidate is ranked last in both directions: under desc and asc alike, every readable candidate is placed ahead of it.

Two unreadable candidates keep their relative order. The rule is conservative on purpose: an unreadable score never sneaks to the front by posing as negative infinity under asc. It wins only when nothing readable is left to beat.


The Trap: Choosing the Winner Is Not Free

Selecting the best-performing candidate from a sweep and trading it is the classic overfitting mistake. The more combinations you sweep, the likelier the winner won by luck — and luck does not repeat.

The mechanism is multiple comparisons: search enough variants against one history and something looks excellent by chance. Bailey, Borwein, López de Prado and Zhu show in Pseudo-Mathematics and Financial Charlatanism that a few trials manufacture an impressive backtest from noise.

Defences that fit naturally around a Collapse:

  • Sweep across assets, not parameters. Cross-sectional momentum is widely replicated; the parameter that best fit last quarter is not.
  • Keep the sweep small. Five candidates is a choice; twenty is a search.
  • Make the Sort statistic robust, measured over a longer window than you intend to trade.
  • Insist the winner clears an absolute bar through Condition. Best of five bad options is no reason to invest.

Collapse answers "which one?" precisely. Whether the question was worth asking of this data is a judgement it cannot make for you.


Missing Data

Candidates arrive from a fanned-out branch, and branches die upstream: a source with no history, a filter that rejected everything, a computation that produced nothing.

  • A candidate whose branch died arrives empty. It holds its place, so Selection index stays meaningful, but can never win.
  • A missing or empty Condition series eliminates that candidate. No verdict is not a pass.
  • A missing or empty Sort series eliminates nothing — it makes that candidate unrankable, so it goes to the back.
  • Candidates at different frequencies align as everything in Systemic does: lower-frequency series up-sampled by holding the last known value, shorter ones padded by repeating their final point. Since only the last Condition and Sort value is read, a padded tail carries that verdict forward unchanged.
  • If every candidate is eliminated, Selection is empty and the branch is dead — the intended way to express "no acceptable choice, so no position".

Edge Cases

SituationWhat Collapse does
One candidate, Condition falseSelection empty; branch dies.
Condition not wiredNothing is eliminated on that basis; every non-empty candidate competes.
Sort not wiredNo reordering; the first surviving candidate wins, and Order is ignored.
Neither Condition nor Sort wiredThe first non-empty candidate wins — a plain "take the first branch that has data".
All Sort values equalStable ranking; the earliest surviving candidate wins.
Sort value unreadableRanked last under both asc and desc.
All candidates emptySelection empty; branch dies.
One candidate, Condition trueIt wins. Selection index 0.
Sweep at its capA sweep produces at most 20 combinations, so there are never more candidates.

Further Reading

Rendering diagrams...
© 2026 WanoLabs. All rights reservedAlpha build v0.1.5