By the end of the competition, the repository contained more strategy code than the account had ever used. That was not an inconsistency. It was the distinction I needed the research process to preserve.

A strategy could exist in four different senses:

  • the idea had been described;
  • the implementation existed;
  • the configuration enabled it;
  • a running process had loaded that configuration.

Those are separate claims. The first version of the workflow made them too easy to blur together. A class registered under quantsilico/signals/ could look finished even when it had never passed a validation gate or entered the live configuration.

Part 1 covered the common engine beneath the strategies. This article is about the decisions above it: why momentum and Kalman launched, why OU entered later, why the network family failed the longer test, and why several other candidates remained research-only or were disabled for operational reasons.

The validation metrics below come from named project outputs. They are backtest and decision records, not live competition returns. Their panel identity stays attached to them throughout the article.

Promotion required evidence, not code

The promotion path I was trying to enforce looked like this:

Model to Market promotion pathImplemented code is not permission to trade. Configuration promotion is not runtime confirmation.
  • implemented ≠ enabled
  • enabled ≠ runtime-confirmed
  • rejected_by_validation ≠ researched_not_promoted
  • operationally_disabled ≠ universally invalid
Candidate definition
Hypothesis
Required instruments
Expected mechanism
Alpha implementationRegistered · no broker access
Validation identity
Named panel
Source / input hash
Locked baseline
Costs & assumptions
Validation result
Return
Reported Sharpe
Drawdown
Gate result
Validation branches
⊘ rejected_by_validation
◇ gated_pending_evidence
○ researched_not_promoted
→ configuration candidate
Operational review
Monitorable inputs
Exposure & concentration
Symbol availability
Execution requirements
Observable failure mode
Promotion record
Enabled flag
Strategy weight
Symbol routing
Dated commit
Runtime distinction
Configuration promoted
Runtime config hashRecovered or not recovered
Live activationConfirmed or remains limited
Promotion path (text fallback)
Promotion path stages
StageStep
DefinitionHypothesis → instruments → mechanism → Alpha implementation
IdentityNamed panel · input hash · locked baseline · costs
ResultReturn · Sharpe · drawdown · gate
Branchrejected_by_validation · gated · researched_not_promoted · configuration candidate
Ops reviewInputs · exposure · symbols · execution · failure modes
PromotionEnabled flag · weight · routing · dated commit
RuntimeConfig promoted ≠ runtime hash recovered ≠ live activation confirmed
Research / implementationValidation comparisonPromotion handoffRejected or gated (⊘ / ◇)Runtime distinction

A hypothesis becomes a registered Alpha implementation without broker access. It is validated against a named panel, locked baseline, costs and assumptions. Results include return, reported Sharpe, drawdown and a gate. Branches are rejected_by_validation, gated_pending_evidence, researched_not_promoted, or configuration candidate. Only configuration candidates pass operational review of inputs, exposure, symbols, execution and failure modes. Promotion records an enabled flag, weight, symbol routing and dated commit. Runtime evidence then distinguishes configuration promotion from a recovered config hash and confirmed live activation. Implemented is not enabled; enabled is not runtime-confirmed; rejected_by_validation is not researched_not_promoted; operationally_disabled is not a universal invalidation.

A candidate moves from hypothesis to live eligibility only after implementation, named-panel validation, cost and sensitivity checks, operational review and a dated configuration decision.
  1. 01
    Hypothesis
    State the idea, expected behaviour and instruments required.
  2. 02
    Implementation
    Register the candidate behind the shared Alpha interface without giving it access to execution.
  3. 03
    Named-panel comparison
    Compare it with the locked baseline on an identified dataset rather than whichever panel is easiest to load.
  4. 04
    Costs and sensitivity
    Check whether the result survives the recorded cost model and reasonable parameter changes.
  5. 05
    Operational review
    Decide whether the inputs, positions and failure modes can be monitored and constrained during the competition.
  6. 06
    Configuration decision
    Record the enable flag, weight, symbols and commit that represent the promotion decision.
  7. 07
    Runtime evidence
    Use the loaded configuration hash or equivalent state record to distinguish promotion in Git from confirmed activation in a running cycle.

The repository did not complete every stage equally for every candidate. I do not want to hide that by pretending the process was more formal than it was.

Momentum and Kalman have clear launch evidence. The network family has a clear long-panel rejection. BTC has a clear research-only boundary. Other candidates have implementation or configuration records without a complete research packet.

That unevenness is exactly why the status names matter.

Three records for one candidate

I eventually needed three different records for each candidate:

  1. Implementation state — whether the strategy existed and could be constructed.
  2. Configuration state — whether it was enabled, at what weight and for which symbols.
  3. Runtime state — whether a running process loaded that configuration and exposed it through telemetry.

A candidate could therefore be implemented without being enabled, enabled without a recovered first-cycle runtime hash, or operationally disabled after previously being permitted.

Candidate status in the surviving competition record
Momentum
live_at_launch
Kalman gold/silver
live_at_launch
OU EUR-cross mean reversion
promoted_later — configuration changed 23 Jun 2026
Network / launch / armoured network
rejected_by_validation — long panel
Aggression profiles
rejected_by_validation — sweep stage
Churn modifications
rejected_by_validation — base bundle retained
BTC scalper
researched_not_promoted
Forecasting / TSFM / graph overlays
gated_pending_evidence or planned_only
Alternative crypto
operationally_disabled during the finals window

rejected_by_validation does not mean the underlying idea can never work. It means the tested implementation did not earn promotion under the data, assumptions and deadline available at the time.

researched_not_promoted is different. It means the candidate did not reach a decision point where live eligibility was justified.

operationally_disabled is different again. That status reflects a control decision about live exposure rather than a general conclusion about the research hypothesis.

Launch configuration and later promotion

The launch snapshot records two enabled sleeves:

  • momentum at weight 0.60;
  • Kalman gold/silver mean reversion at weight 0.40.

That is the strongest evidence for the launch decision. Both strategies appear in the locked baseline and the launch snapshot with the same weights.

Their inclusion does not give me a complete historical research packet for each sleeve. I cannot recover every intermediate test, rejected parameter or contemporaneous note that led to 0.60 and 0.40.

What I can say is narrower and more useful: those were the two strategies represented in the documented launch configuration, and the rest of the engine was built to treat their output through the same portfolio, risk and execution path.

Momentum was the larger contribution. Kalman provided a relative-value sleeve around the gold/silver relationship. Neither strategy could submit an order directly; each supplied target weights to the common portfolio layer described in Part 1.

The launch decision was therefore not “these are the two best strategies in general.” It was “these are the two candidates I was willing to put into the first competition configuration.”

Adding OU after launch

OU mean reversion was not part of the documented launch blend.

Git history shows ou_score changing to enabled: true on 23 June 2026 in commit b813b9d. The same change reweighted momentum and Kalman from 0.60/0.40 to 0.55/0.25 and assigned OU a weight of 0.20. The relevant EUR-cross routing was also enabled in that configuration state.

Documented configuration change on 23 June 2026
Momentum
0.60 → 0.55
Kalman gold/silver
0.40 → 0.25
OU mean reversion
disabled / absent from launch blend → enabled at 0.20
Configuration evidence
Commit b813b9d
Runtime evidence
Exact first OU-enabled live-cycle hash not recovered

A stale YAML comment still describes OU as gated off. The executable value and Git diff outrank that comment.

This is also where configuration evidence and runtime evidence separate.

I can date the configuration change. I cannot recover the first heartbeat that proves the exact cycle which loaded it. I therefore describe OU as promoted in configuration on 23 June, not as definitely placing its first live trade at a specific timestamp.

That distinction may sound narrow, but it prevents Git history from being used as a substitute for runtime state.

Candidates that failed or remained gated

The network family received a much clearer rejection record.

The archived long-panel comparison records the locked baseline against three related configurations:

config panel return reported Sharpe max DD gate
locked_baseline long +14.50% 0.377 24.38% BASELINE
network long −16.46% −0.112 45.41% FAIL
launch long −16.46% −0.112 45.41% FAIL
network_armored long −16.46% −0.112 45.41% FAIL

Loading long-panel return chart…

Long-panel return comparison. The locked baseline remained positive while the three archived network-family rows recorded the same negative return and failed the gate.

The decision report records FALLBACK_BASELINE.

I preserve the identical rows for network, launch and network_armored because that is what the archived output contains. I do not infer three independent performance paths merely because the configurations have different names.

The decision was straightforward: none of those tested variants earned promotion over the locked baseline on the long panel.

That does not establish that graph or network methods are useless. It establishes that these implementations, under this comparison, failed badly enough that I kept them out of the competition configuration.

Why panel identity mattered

The short and long comparisons were not interchangeable.

They represented different datasets and potentially different periods, coverage and assumptions. A return or Sharpe number without its panel identity is incomplete evidence.

This became one of the easiest ways to fool myself. A candidate could look acceptable on the smaller or friendlier dataset and then collapse when I moved to the longer panel. If I copied only the headline metric into a summary table, the difference in evidence quality disappeared.

The long-panel records therefore stay labelled panel = long. I do not average them with the shorter comparison, choose the better-looking value or present them as repeated estimates of the same experiment.

The validator also needed to fail loudly when a named dataset was unavailable. A missing long-history input should be an error, not permission to substitute whichever panel happens to exist locally.

The point was not to make every panel identical. It was to make the identity of the experiment impossible to lose.

Testing more aggressive sizing

The aggression sweep tested whether increasing the risk profile improved the competition setup enough to justify promotion.

The recorded conclusion retained baseline_hold. The tested aggressive profiles did not pass the chosen gate at that stage.

That decision can look inconsistent beside the later configuration, which allowed much more exposure. It is not the same question.

The sweep asked whether those specific candidate profiles passed the validation rules when tested. The later configuration history records operational decisions made as the competition developed and the remaining window shortened.

A later increase in permitted exposure does not retroactively turn the failed sweep into a pass. Equally, the failed sweep does not prove that every possible increase in exposure was wrong.

The evidence supports a timeline:

  1. I launched with a conservative baseline.
  2. The tested aggression profiles failed their recorded gate.
  3. I retained baseline_hold at that stage.
  4. Later configuration changes nevertheless raised risk limits during the competition.

Part 3 deals with the consequences of that timeline. Here, the important point is that the validation verdict belongs to the tested profiles and date that produced it.

Churn changes that stayed out

The churn work reached a similar decision by a different route.

The recorded conclusion retained base_bundle. The documented modifications did not pass the relevant gates, and some mechanisms required paper-live or live observations that were not available in the research output.

This was not a dramatic failure like the long-panel network result. It was a failure to produce enough evidence for promotion.

That difference matters.

A candidate can remain out because it loses money in the test. It can also remain out because its claimed benefit cannot be separated from turnover, execution timing or a short sample.

For the churn changes, the honest status is that the base bundle remained the supported choice. I do not turn the absence of a passing result into a stronger claim that every churn reduction mechanism was bad.

The BTC scalper stayed offline

The BTC scalp work never entered the live alpha blend.

The repository contains M1- and tick-oriented research, mocks and a history-fetch path. The fetch script keeps real MT5 retrieval behind an explicit --live-fetch flag. Without that flag, the script reports that no real MT5 fetch was executed.

The operational boundary was therefore visible in the tool itself:

# scripts/fetch_mt5_history.py — guarded behaviour

--live-fetch
    Explicitly permit a real MT5 history fetch.

Default behaviour:
    No real MT5 fetch is executed when --live-fetch is absent.

This was useful because a research script capable of contacting the same platform as the live system can otherwise blur another boundary: analysing historical data versus interacting with the execution environment.

The candidate status is researched_not_promoted.

There is no evidence that the BTC scalper entered the configured ensemble, received a live weight or affected the competition account. It should not be described as a live strategy that later failed.

Forecasting and graph work remained gated

The forecasting and graph-oriented work also remained outside the active competition blend.

The launch and archived configurations keep tsfm_forecast and xasset_graph_momentum disabled at weight 0.0.

Those modules show where the research was heading. They do not show competition-time live capability.

This is another reason I separate implementation from promotion. A repository can contain a model, tests and configuration fields while the actual decision remains “not enabled.”

The correct status is either gated_pending_evidence or planned_only, depending on the specific candidate record. Neither status should be rewritten as a live deployment merely because the code exists.

Operational controls during the live window

Alternative-crypto exposure produced a different kind of decision.

The record is not primarily a backtest rejection. It is a sequence of operational control changes during the live window.

Git history shows quarantine-related work around 22 June, progressive symbol disables and a 25 June change that disabled ETH, SOL, XRP and BAR while changing the handling of existing disabled-symbol positions. A later change adjusted that behavior after manual flattening.

That sequence supports the status operationally_disabled.

It does not provide enough public evidence to reconstruct a precise incident P&L, broker rejection or position timeline. I am therefore not filling the gap with a dramatic story.

What the repository proves is more limited:

  • those symbols became subject to explicit controls;
  • the handling of existing exposure changed;
  • the decision occurred during the competition window;
  • the change was operational rather than a general declaration that alternative-crypto strategies could never work.

The runtime event details belong in Part 3 if a suitably redacted record can support them.

What the promotion process still lacked

The promotion process became clearer during the competition, but it was not equally complete from the beginning.

The first weakness was provenance. I had outputs, configs and Git commits, but not every candidate had one compact record tying together its dataset, parameters, costs, result, decision and effective runtime state.

The second weakness was the gap between configuration and activation. A commit can prove when I enabled OU. Without the first matching runtime hash, it cannot prove the exact cycle that loaded the new configuration.

The third was panel discipline. A short-panel result and a long-panel result could look like two measurements of the same thing when they were actually different experiments. The panel name, source hash and generation command needed to travel with the metric.

The fourth was stale documentation. A comment that says “gated” while the executable field says enabled: true is worse than no comment because it gives the reader two incompatible answers.

The fifth was timing. I was researching, promoting candidates and operating the account inside the same fixed window. A technically valid candidate could still be a poor promotion decision if there was not enough time left to observe it safely.

None of those problems is solved by generating more strategies. They require better decision records and stricter boundaries between research, configuration and runtime evidence.

Rules I would retain

If I rebuilt the same workflow, I would keep the following rules:

Rules retained from the competition workflow
Name the panel
Every metric carries the dataset or panel that produced it.
Pin the baseline
A candidate is compared with a fixed reference rather than the latest convenient configuration.
Fail on missing data
A missing named panel is an error, not permission to fall back silently.
Separate status types
Research-only, gated, rejected, promoted and operationally disabled mean different things.
Trust executable state over comments
Enable flags and committed values outrank stale prose.
Record the effective commit
Promotion requires a dated configuration state.
Capture runtime identity
A loaded configuration hash distinguishes a Git decision from confirmed activation.
Review operations before promotion
A candidate needs monitorable inputs, positions and failure modes, not only a return series.

The candidate generator was never the scarce part of the process. The scarce part was evidence strong enough to justify changing the account.

That is the aspect of the competition work that carried into QuantSilico: not the claim that every rejected candidate should stay rejected forever, but the requirement that a candidate earn a traceable path from research output to deployment state.

Part 3 starts where those decisions reached the running account: execution modes, live telemetry, risk changes and the difference between the last recorded telemetry point and the final project settlement.

Reproducibility