Intraday FOMO
The explosive intraday rallies in the S&P so far this year have been fascinating to watch. Typically the S&P exhibits fairly substantial mean reversion tendencies across multiple timeframes, so to see these rallies gather strength as they go is unusual to say the least. This behavior seems indicative of extreme “FOMO”, or fear of missing out, a term that has entered the lexicon this market cycle. To quantify FOMO, I created a simple intraday trading system that mimics this behavior.
Using one min bars, I calculate five period momentum and then standardize it using a 1380 period z-score. Why 1380? S&P futures trade 23 hours per day so this lookback represents one trading day when using one minute bars. When the z-score crosses above 2.5 standard deviations, the system buys and holds for an additional five minutes. Essentially we are betting that extreme momentum over very short timeframes will carry through. If you run this simple strategy on intraday data back to 2007, you’ll find it shreds money at a steady clip, even without transaction costs.

Despite this abysmal history, over the last 84 trading days, or roughly four months since the October lows, this strategy has delivered a Sharpe Ratio of around 3! The chart below shows the rolling 84 day Sharpe Ratio of strategy returns since 2007. Sure enough, this recent period is indeed extraordinary, having eclipsed all prior highs and rightfully earning the FOMO moniker.

If we look at initial crosses of the rolling 84 day Sharpe Ratio above 3, ignoring repeat signals within the past month, these have not been particularly auspicous times to get long the S&P. But once again, the current market has ignored any previous historical tendencies and powered higher. I also looked at market reactions to extremely low readings in the rolling FOMO Sharpe Ratio but didn’t find anything of note.


And in case you were wondering, no, you can’t flip the rules and profit. Short-term edges like this are very difficult to monetize in the presence of transaction costs. The equity curve below reverses the rules to short on extreme positive momentum and covers the position five minutes later. The red line shows the same system with half tick transaction costs included. Had I also included commissions and disallowed fractional position sizes the equity curve would have gone to zero in short order. Although you can’t trade the intraday FOMO system directly, its performance history suggests that patiently working bids in the S&P is a better strategy than chasing the market higher. I also find it interesting that intraday behavior can potentially be aggregated to provide insights into higher timeframes.

Code
class FOMO_ES : Strategy{ public static void Run() { CsiBarServer server = new CsiBarServer(@"c:\data\csi\sti"); var data = server.LoadSymbol("ES", new DateTime(2007, 1, 1));
var strat = new FOMO_ES(); strat.PrimarySeries = data; strat.RunSimulation(); strat.EventStudyReport(); strat.Chart(); }
protected override void OnStrategyStart() { base.OnStrategyStart();
Col1 = TimeSeries.Load(@"c:\temp\fomo_sharpe.csv").Sync(PrimarySeries); Col1.Name = "Rolling FOMO Sharpe"; Condition1 = CrossAbove(Col1, 3); Col2 = Indicators.BarsSinceTrue(Condition1);
Condition2 = CrossBelow(Col1, -5.75); Col3 = Indicators.BarsSinceTrue(Condition2);
Plot(Col1, 1, Color.Black, paneSize: 35f); Plot(3, 1, Color.Red, "Dot"); }
protected override void OnBarClose() { base.OnBarClose();
if (Col2.Last == 0 && Col2.Previous >= 21) { SnapEvent(); } }}
class FOMO : Strategy{ public static void Run() { AsciiBarServer server = new AsciiBarServer(@"c:\temp"); server.Instruments = InstrumentCollection.Load("instruments.xml"); var data = server.LoadSymbol("ES");
var mm = new FixedFractional(); mm.Options.IncludeWeekends = false;
var strat = new FOMO(); strat.PrimarySeries = data; strat.MoneyManager = mm; strat.RunSimulation();
var dr = mm.Results.GetDailyReturns(); var fs = Indicators.SharpeRatio(dr, 84); fs.Chart(); fs.Save(@"c:\temp\FOMO_Sharpe.csv");
var ntcosts = mm.Results.GetEquityCurve(); mm.TransactionCosts = new Balsam.TransactionCostModels.TickTransactionCosts { Ticks = 0.5 }; mm.RunSimulation(); var wcosts = mm.Results.GetEquityCurve(); wcosts.Name = "Equity Curve w/ tcosts"; new ISeries[] { ntcosts, wcosts }.Chart(); }
public int Length { get; set; } = 1;
public double Threshold { get; set; } = 2.5;
public int Horizon { get; set; } = 5;
protected override void OnStrategyStart() { base.OnStrategyStart();
Col1 = Momentum(Close, 5); Col2 = ZScore(Col1, Length * 60 * 23);
var hourly = PrimarySeries.ToIntraday(60); Col3 = Atr(hourly, 24); }
protected override void OnBarClose() { base.OnBarClose();
if (MarketPosition == PositionSide.Flat && CrossAbove(Col2, Threshold).Last) { BuyClose(); }
ExitOnHorizon(Horizon); }
protected override void OnOrderSubmitting(Order order) { base.OnOrderSubmitting(order);
order.ATR = Col3.Last * Math.Sqrt(24); }}