SPY-Filtered Entries
תוכן זה אינו זמין עדיין בשפה שלך.
A two-symbol strategy that trades anything subscribers pick — but only when SPY confirms a market-wide uptrend. Demonstrates data feeds, regime filtering, and Compounding sizing.
class SPYFiltered(Strategy): """Only buy when SPY confirms uptrend.""" sma_period = Param(50, min=20, max=200) rsi_period = Param(14, min=5, max=30) trade = Symbol(session=USEquityDaily, trade=True) spy = Symbol(ticker="SPY") sizing = Compounding(percent=Param(95, min=50, max=100))
def execute(self): spy_sma = SMA(self.sma_period, on=self.spy) rsi = RSI(self.rsi_period, on=self.trade)
spy_bullish = self.spy.close > spy_sma.value
if spy_bullish and rsi.crossed_above(30) and self.trade.is_flat: self.buy(self.trade, reason="SPY uptrend + RSI oversold")
if not spy_bullish and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="SPY lost uptrend")Walkthrough
Two Symbols
trade = Symbol(session=USEquityDaily, trade=True) # the trading slotspy = Symbol(ticker="SPY") # data feed (read-only)When more than one Symbol is declared, exactly one must be marked trade=True. The other is a data feed — its OHLCV is readable, but you can’t place orders against it (self.buy(self.spy, ...) is RC2_SIG_DATAFEED).
The trade slot is session-locked but ticker-open — subscribers pick the actual instrument they want filtered by SPY.
Indicators on different symbols
spy_sma = SMA(self.sma_period, on=self.spy) # SMA of SPY's closersi = RSI(self.rsi_period, on=self.trade) # RSI of the trading instrumentEach indicator picks its own source. The strategy sees SPY’s trend as a regime filter while reading RSI of whatever the subscriber bound to trade.
Two-condition entry
if spy_bullish and rsi.crossed_above(30) and self.trade.is_flat:Both must be true: SPY trading above its 50-day SMA and the trading instrument’s RSI just exited oversold. Either condition alone wouldn’t be enough.
Regime-driven exit
if not spy_bullish and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="SPY lost uptrend")When SPY drops below its trend line, exit immediately — even if the instrument’s own RSI says nothing’s wrong. The thesis is “I only want exposure during market uptrends,” and this enforces it.
Compounding sizing
sizing = Compounding(percent=Param(95, min=50, max=100))Compounding means “use 95% of current equity” — winners grow the position over time. Combined with the Param, the optimizer can find the right risk fraction.
In walk-forward, Compounding chains equity across windows (unlike FixedCapital, which resets to initial per window). For long-horizon backtests where compound growth matters, this is the right model.
What to tune
| Param | Typical range | Effect |
|---|---|---|
sma_period |
20..200 | Lower = more responsive regime; higher = smoother. |
rsi_period |
5..30 | Lower = more entries; higher = fewer but cleaner. |
sizing percent |
50..100 | Risk control — lower = less drawdown, less upside. |
Common modifications
VIX as a second filter
Add VIX as a third symbol and skip entries when fear is high:
class SpyAndVixFiltered(Strategy): """Trade only when SPY is trending and VIX is calm.""" trade = Symbol(session=USEquityDaily, trade=True) spy = Symbol(ticker="SPY") vix = Symbol(ticker="VIX") sizing = Compounding()
def execute(self): spy_sma = SMA(50, on=self.spy)
spy_bullish = self.spy.close > spy_sma.value low_fear = self.vix.close < 20
if spy_bullish and low_fear and self.trade.is_flat: self.buy(self.trade)
if not spy_bullish and self.trade.is_long: self.sell(self.trade, self.trade.position, reason="SPY turned")Sector rotation pattern
Pair a sector ETF as the trading symbol with SPY as the filter — buy the sector when both it and SPY are trending:
trade = Symbol(session=USEquityDaily, trade=True) # subscriber binds XLK, XLF, etc.spy = Symbol(ticker="SPY")Each subscriber gets a different sector — but the SPY filter applies identically.