Strategies
pinkfish Follow-Trend (Farrell Aultman), faithful port: buy when the daily close is above its 200-day SMA while the S&P 500 (^GSPC) closes above its own 200-day SMA; sell on a close below the SMA or when the S&P regime turns negative. Applied separately to 8 US index/asset ETFs, equal 1/8 sleeves, next-open fills (USEQ 1-DAY, long-only)
Outcome: Paper Trading
PinkfishFollowTrendEtfSleeves
Outcome Summary
PinkfishFollowTrendEtfSleeves ported the open-source pinkfish Follow-Trend rule to 8 ETF sleeves on USEQ, keeping the source's parameters and changing only execution, sizing and venue. Across 6 iterations it made +184.4% with Sharpe 0.63 and a 12.6% max drawdown, stayed out of the market in 2008, and earned 1.17% per trade, far above the cost floor. Its out-of-sample evidence was positive but just short of significance (pooled t 1.94, holdout t 1.60), its Calmar ratio was below the floor and it had a drawdown that lasted about 969 days. The analyst recommended promotion and it is now in paper trading with a low-confidence label.
Hypothesis
Implementation
Port of pinkfish follow-trend (Farrell Aultman, MIT) to 8 independent USEQ daily ETF sleeves: SPY, QQQ, DIA, IWM, EFA, EEM, TLT, GLD. Each sleeve checks the sell rule first: if long, sell when the ^GSPC close is below its 200-day SMA, or when the close is below SMA(200)*(1-band). If flat, buy when the ^GSPC regime is positive and the close is above SMA(200)*(1+band). Each entry buys whole shares worth 1/8 of account equity. A decision made on a daily close fills at the next session's open.
Verification Results
Acceptable as is. If ^GSPC gaps show up in the audit, exclude ^GSPC from the sync legs and carry the regime forward explicitly.
Hypothesis adaptation (3) says: if the ^GSPC bar for a session is missing, keep the previous regime. Instead, the base alignment barrier skips the whole session for every sleeve until all 9 legs, ^GSPC included, have a bar at that close. The _prev_regime fallback in _run_sleeves is effectively dead code. With complete Yahoo ^GSPC daily data the two behave the same. The developer disclosed this in the rationale.
Optionally size from min(equity*sleeve_fraction, free margin), or use a small buffer (e.g. 0.995). Not a correctness blocker.
Each entry is sized at 12.5% of equity at the decision close and fills at the next open. When all 8 sleeves are long, a gap-up open, or positions marked up since entry, can push gross slightly above 100% at leverage 1.0. A late sleeve's entry order could then be shrunk or refused by the margin check.
None required.
Without point_in_time_universe, the backtest window starts at the latest-listed leg (GLD, Nov 2004), plus the 200-bar warmup. This is fine for these survivorship-free ETFs. It just shortens history compared with SPY/^GSPC alone.
Long-only SMA200 trend filter with an S&P regime gate on 8 ETFs is a well-known, low-turnover rule. Expect modest Sharpe, and expect TLT/GLD returns to be understated because dividends are not in PnL. Whipsaw around the SMA when percent_band=0 can produce many small round trips; on USEQ costs are tiny, so this is mainly a Sharpe/drawdown question for the analyst.
Backtest Review
- Sharpe
- 0.63
- Total return
- 184.42%
- Max drawdown
- 12.64%
- Trades
- 742
- Win rate
- 33.2%
- Profit factor
- 2.08
The trades match the hypothesis. All 608 closed trades are long. All 8 sleeves trade, between 56 and 95 round trips each. Entries fill at the next session open (14:30 UTC) at about 12% of equity (leverage 0.12). The 2008 to mid-2009 bear market has zero exposure because the S&P 200-day regime filter worked as written.
Economics are well above the USEQ cost floor. Average trade return is 1.09% of notional against the 0.05% floor (0.10% preferred). Profit factor is 1.91. The payoff ratio is about 3.8:1 (average win $1,137 vs average loss $300), so the 34% win rate is what a trend follower should show.
Every sleeve is net positive: QQQ +$32.0k, SPY +$19.5k, DIA +$17.4k, IWM +$14.2k, GLD +$13.6k, TLT +$5.9k, EEM +$4.8k, EFA +$3.2k. The profit is not concentrated in one sleeve.
Development period (2006-02 to 2022-05): +110.7%, CAGR 4.7%, Sharpe 0.59 (probabilistic Sharpe 0.98), max drawdown 12.6%. That drawdown is well inside the pre-registered 25% limit and far below the 2008 drawdown of an equal-weight basket of these ETFs. The low return is not just beta (lesson 170).
608 trades give the walk-forward enough samples. Only two parameters are tunable, both economically meaningful: sma_period (100-300) and percent_band (0-3%, which controls whipsaw).
The risk-adjusted edge is modest: Sharpe 0.59, Calmar 0.37, longest drawdown 871 days, tail ratio 0.93. A low-turnover SMA trend filter is expected to look like this.
Several flat or negative years (2011, 2012, 2015, 2018, 2022 to the cut). The whipsaw QA expected at percent_band=0 shows up as 17 consecutive losses. On USEQ costs these losses come from price moves, not fees.
Dividends are not in PnL, which understates TLT, SPY and the other income-paying sleeves. This is a known adaptation, not a defect.
QA's note is partly confirmed. Sharpe is modest and whipsaw losses are visible. However, per-trade economics (1.09% vs a 0.05% floor, PF 1.91) and drawdown are healthy, so the concern does not justify skipping optimization.
Analysis
Faithful port of pinkfish Follow-Trend, all parameters fixed at the source's values; one evaluation, no parameter search (effective trials = 1).
Average trade return 1.17% of notional against the 0.05% USEQ floor; profit factor 2.08 over 742 trades; winners 4.2 times the size of losers; impact 2.0% of gross, zero commissions.
Out-of-sample results positive and close to significant: 31 windows pooled give Sharpe 0.54 (t 1.94, hurdle 2.0); holdout (134 trades, 1,600 days) Sharpe 0.78 (t 1.60), drawdown 8.1%, consistent with the walk-forward result (z +0.31).
Sensitivity is a broad plateau with no cliffs: every tested point is within one standard error of the base.
Max drawdown 12.6% against the pre-registered 25%; out of the market for all of 2008; 15 of 20 years positive; largest year carries 14.9% of profit; beta 0.33.
Risk-control check passes on the ledger: worst loss 1.33% of equity, largest position 12.8% of equity.
All 6 predictions recorded before coding were met.
Not statistically significant (t 1.94 pooled out-of-sample, 1.60 holdout): low confidence, paper only.
Calmar 0.41 is below the 0.5 floor; the longest drawdown took about 969 days to recover (2011-2016).
Information ratio is -0.27 against the equal-weight ETF basket: the value is a smaller drawdown, not extra return.
Walk-forward windows vary widely (-3.4 to +3.0) because the rule whipsaws in sideways markets.
65 capped-margin entry shrinks (single-leg long entries, not broken hedges).
The 1-bar IC of -0.009 does not measure a multi-month trend rule.
Backtest gate: - backtest produced ZERO trades — the entry condition never fired on real data
LOW CONFIDENCE, NOT STATISTICALLY PROVEN: paper observation only, never live money on this evidence. Trade the fixed source parameters at reduced paper allocation. Kill criteria: after at least 12 months and at least 60 paper trades, retire if average trade return is below 0.30%, profit factor is below 1.2, drawdown exceeds 15%, or Sharpe is negative over a full 12 months while the ETF basket is positive.
Risk Review
Defined risk control holds on route (b), signal exit: positions close on a close below the 200-day SMA or when the S&P 500 regime turns negative. Sizing is capped at 1/8 of equity per sleeve, long-only, 1x. Measured on the ledger: the largest position was 12.82% of equity (EEM, 2020-07). The worst realised loss was 1.33% of equity (EEM entered 2009-06, -10.7% on notional). The worst adverse excursion was 1.32% of equity. All are far inside the conservative profile: realized vol 8.4% (band 5-60%), peak position vol 8.1% (cap 120%), trade loss under the 15% cap. The lack of a stop is faithful to the source and is not an issue.
Gross exposure runs at the 1x margin ceiling. Peak margin utilization is about 1.0015 against the 1.02 limit, and the backtest had 65 capped-margin entry shrinks. The cause is that each sleeve is sized at 12.5% of equity at the decision close but fills at the next open, while the other sleeves have marked up. The effect is under-sized late sleeves, not leverage, but paper fills will show the same refusals and shrinks.
Drawdown profile is gradual but slow to recover. Max drawdown is 12.6% at 1x, against the 25% the hypothesis pre-registered. The longest drawdown lasted 871 days. Annual returns were negative in 2011, 2012, 2015, 2018 and 2022, with whipsaw around the SMA in sideways markets. The bootstrap drawdown CI reaches 35%, above the pre-registered 25%, so a deeper drawdown than history is plausible. The COVID stress window lost 5.9% (drawdown 6.1%), which the regime exit kept contained. The strategy was flat for all of 2008.
Correlation with the deployed book: the book already holds US-equity trend and calendar exposure: PinkfishCalendarMonthlySmaSpy (an SPY SMA timer, very likely highly correlated with the SPY/DIA/QQQ/IWM sleeves), SpyTurnOfMonthFlowWindow, HalloweenInternationalEtfBasket80, EmCountryEtfTurnOfMonthBasket and NfciImpulseSectorSpreadUseq. The S&P 500 regime filter puts all eight sleeves (including TLT and GLD) to cash at the same moment, so the diversification is smaller than eight sleeves suggest. No book member is in breach and there are no open execution items. However, PinkfishCalendarMonthlySmaSpy, a USEQ daily SMA strategy, shows a 'diverged' decision replay, which is a fidelity warning for this venue path.
Market impact is negligible: liquid index ETFs, 12.5% sleeves, daily bars, about 600 trades over 16 years. Modeled impact is about 2% of gross PnL, with zero commission. Trade frequency is low and fully fee-viable: avg trade return 1.09% of notional in the development period, against the 0.05% USEQ floor.
The evidence is low confidence. The final parameters are the source's fixed values (no search, one trial). The pooled out-of-sample t-stat is 1.94, just under the 2.0 hurdle. The holdout result is not enough to call it significant. This supports labelled paper observation only, not real-money evidence. Deflated Sharpe was not evaluated as a floor (missing from the backtest metrics), so treat it as unknown, not passed.
Paper only, under the persistent low-confidence label. This run is not real-money evidence.
Allocate as part of the US-equity trend/calendar cluster (with PinkfishCalendarMonthlySmaSpy and the turn-of-month ETF strategies), not as an independent allocation.
USEQ paper must run on the supervised SF_PAPER_USEQ path. Before any live review, resolve the 'diverged' replay verdict seen on the sibling USEQ SMA strategy and confirm this strategy's replay matches.
Iteration History
Verification failed (Layer 1.5 — hypothesis/config consistency) [class=hypothesis_mismatch]: - optimization_plan fixes leverage at 1.0
Maintain the security indicator independently for every primary bar, or rebuild the sum from self._bars[-self.sma_period:] whenever the number of newly buffered primary bars exceeds one. Compute the security SMA before excluding sessions with missing regime values.
The incremental security SMA becomes incorrect after a missing index session. The base alignment barrier buffers MSFT bars but skips calculate_signal when the matching index bar is absent. On the next aligned session, this code adds only the current close and subtracts only one historical close, although the 200-session window advanced by multiple bars. The resulting sum no longer represents the current 200 MSFT closes and can generate incorrect entries and exits. The hypothesis explicitly requires handling missing aligned rows.
Retrieve the pinned helpers and compare regime values, equality behavior, sizing and trading boundaries against a small source-generated reference ledger before claiming exact parity.
The pinned CROSSOVER, TradeLog and select_tradeperiod semantics remain unverified. Fetch attempts did not retrieve the pinned implementations, so the sign-based regime, full-capital sizing and trade-period boundaries cannot be certified as an exact source translation.
Allow the initial calculation at 200 bars; require the additional historical element only for subsequent incremental updates.
Requiring 201 primary bars delays the first eligible decision even though the current-inclusive security SMA and index SMA require only 200 observations.
Verify the pinned helpers and compare a reference decision and fill ledger before describing this as a faithful line-for-line port.
The pinned CROSSOVER, TradeLog and select_tradeperiod semantics remain unverified. The disclosed interpretation is coherent, but exact source parity, including equality behavior, sizing and trade-period boundaries, is not established.
Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]: - [critical] The hypothesis explicitly requires entries and exits filled at the daily close that produced the decision. This bar-driven FactoryStrategy on USEQ instead fills at the next session's open. The supplied execution capabilities mark same-close execution unsupported, and src/backtesting/next_open.py confirms the next-open behavior. Disclosing this incompatibility does not satisfy the required execution rule; overnight gaps change fills, sizing exposure, and daily balances. (line 17) — fix: Return the execution incompatibility to the owner or Research Lead. Proceed only with an explicitly revised hypothesis accepting next-session-open execution, or platform support for the required same-close fills. Do not present current backtest results as a faithful reproduction.
Return the execution incompatibility to the owner or Research Lead. Proceed only with an explicitly revised hypothesis accepting next-session-open execution, or platform support for the required same-close fills. Do not present current backtest results as a faithful reproduction.
The hypothesis explicitly requires entries and exits filled at the daily close that produced the decision. This bar-driven FactoryStrategy on USEQ instead fills at the next session's open. The supplied execution capabilities mark same-close execution unsupported, and src/backtesting/next_open.py confirms the next-open behavior. Disclosing this incompatibility does not satisfy the required execution rule; overnight gaps change fills, sizing exposure, and daily balances.
Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]: - [critical] The hypothesis explicitly requires entries and exits at the current daily close and daily balances after those executions. This bar-driven USEQ configuration instead executes at the next session's open, as confirmed by src/backtesting/next_open.py and its integration in runner.py. The code acknowledges this incompatibility. Next-open fills change overnight exposure, execution prices and post-decision balances; passing smoke tests cannot establish faithful reproduction. (line 6) — fix: Return the execution incompatibility to the owner or Research Lead. Resume only with an explicitly revised hypothesis accepting next-session-open execution, or platform support for the required same-close execution. No strategy-level threshold adjustment repairs this mismatch.
Return the execution incompatibility to the owner or Research Lead. Resume only with an explicitly revised hypothesis accepting next-session-open execution, or platform support for the required same-close execution. No strategy-level threshold adjustment repairs this mismatch.
The hypothesis explicitly requires entries and exits at the current daily close and daily balances after those executions. This bar-driven USEQ configuration instead executes at the next session's open, as confirmed by src/backtesting/next_open.py and its integration in runner.py. The code acknowledges this incompatibility. Next-open fills change overnight exposure, execution prices and post-decision balances; passing smoke tests cannot establish faithful reproduction.
Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]: - [critical] The hypothesis explicitly requires entries and exits at the current daily close, with balances recorded after those executions. This bar-driven USEQ strategy inherits market-order execution at the next session's open, as confirmed by src/backtesting/next_open.py. Both should_enter() and should_exit() therefore produce different execution prices, overnight exposure and post-decision balances. The archival header acknowledges the incompatibility but does not resolve it. (line 118) — fix: Keep faithful reproduction blocked. Resume only after the hypothesis explicitly accepts next-session-open execution or the platform supports the required same-close execution. Numeric retuning cannot repair this mismatch.
Keep faithful reproduction blocked. Resume only after the hypothesis explicitly accepts next-session-open execution or the platform supports the required same-close execution. Numeric retuning cannot repair this mismatch.
The hypothesis explicitly requires entries and exits at the current daily close, with balances recorded after those executions. This bar-driven USEQ strategy inherits market-order execution at the next session's open, as confirmed by src/backtesting/next_open.py. Both should_enter() and should_exit() therefore produce different execution prices, overnight exposure and post-decision balances. The archival header acknowledges the incompatibility but does not resolve it.
Outcome Summary
A simple 200-day trend and regime filter mostly reduces drawdown rather than adding return (information ratio -0.27 vs the equal-weight basket, Calmar 0.41 below the 0.5 floor), so judge such ports on risk reduction and expect long whipsaw drawdowns in sideways markets.
The backtest review sent it to optimization and the analyst recommended promotion: the trade economics were strong, sensitivity showed a broad plateau, and every pre-registered prediction was met. Because the out-of-sample results were not statistically significant, it went to paper trading with a low-confidence label.
A faithful port of Farrell Aultman's pinkfish Follow-Trend rule: hold each of 8 US ETFs (SPY, QQQ, DIA, IWM, EFA, EEM, TLT, GLD) in equal 1/8 sleeves, long-only, while its daily close is above its 200-day SMA and the S&P 500 is above its own 200-day SMA, with fills at the next session open.
From 2006-02 to 2026-10 the backtest returned +184.4% (CAGR 5.19%, Sharpe 0.63, max drawdown 12.6%) over 742 trades, with an average trade return of 1.17% of notional and a profit factor of 2.08. Out of sample, 31 pooled walk-forward windows gave Sharpe 0.54 (t 1.94, just under the 2.0 hurdle) and the holdout gave Sharpe 0.78 (t 1.60, drawdown 8.1%).
Analysis
follow-trend
Analysis
strategy.py
Backtest and paper results are hypothetical. Trading involves risk of loss.