Cross-Asset Strategies
Strategies that trade one asset class while reading features from another
One of Horizon’s most powerful patterns: a strategy that trades one asset class while reading features from another. Used for hedging, regime filtering, and cross-asset arbitrage.
The technique
Features accept a market argument that pins them to a specific market id, regardless of the strategy’s current universe. That lets you read the VIX from an equity strategy, or a prediction market probability from a crypto strategy, or a bond yield curve from an options strategy.
Example: Equity hedged by prediction market
from horizon import Strategy, Signal
from horizon.asset_classes import AssetClass, Equity
from horizon.features import Price, Zscore
class RecessionHedge(Strategy):
asset_classes = [Equity] # only trades equities
features = {
"recession_prob": Price(market="polymarket:recession-2025"), # pinned
"z": Zscore(window=20), # per-market
}
def evaluate(self, f, universe):
# Same value for every market. it's the recession market's price
recession_prob = next(iter(f.recession_prob.values()))
if recession_prob > 0.60:
return [] # pause on high recession risk
return [
Signal.from_score(m, score=-f.z[m.id], edge_per_stdev=15)
for m in universe
if abs(f.z[m.id]) > 2
]
Example: VIX-regime equity strategy
class VIXAware(Strategy):
asset_classes = [Equity]
features = {
"vix": Price(market="CBOE:VIX"),
"z": Zscore(window=20),
}
def evaluate(self, f, universe):
vix = next(iter(f.vix.values()))
# Different entry rules per regime
if vix < 15:
# Low vol. quiet regime, lower z-threshold
threshold = 1.5
elif vix < 25:
# Normal
threshold = 2.0
else:
# High vol. wait for deeper pullbacks
threshold = 3.0
return [
Signal.from_score(m, score=-f.z[m.id], edge_per_stdev=15)
for m in universe
if abs(f.z[m.id]) > threshold
]
Example: Crypto-correlated equity strategy
class CryptoCorrelated(Strategy):
asset_classes = [Equity]
features = {
"btc_return": Return(window=5, market="BINANCE:BTC-USD"),
"my_return": Return(window=5),
}
def evaluate(self, f, universe):
btc = next(iter(f.btc_return.values()))
# Only go long if BTC is up (correlation play)
if btc < 0:
return []
return [
Signal.increase(m, edge_bps=30, reason=f"btc={btc:+.2%}")
for m in universe
if f.my_return[m.id] > 0.01
]
How pinning works
When Feature(market="X") is specified:
- The feature always uses market “X“‘s history, regardless of the current market
- All strategies’ universe markets see the same value from that feature
- The value propagates through
ctx.feedsto downstream consumers
In the feature store’s compute method:
for mid in universe_market_ids:
target_mid = feature.market or mid # use pinned market if set
hist = self.history(target_mid)
value = feature.compute(target_mid, hist, feeds)
When feature.market == "CBOE:VIX", every iteration uses VIX’s history, so every market sees the same VIX value.
Combining pinned and per-market features
features = {
"vix": Price(market="CBOE:VIX"), # pinned
"z": Zscore(window=20), # per-market
"spy_return_5d": Return(window=5, market="SPY"), # pinned
}
Mix freely: the pinned features don’t interfere with the per-market ones.