Outside the family: competitors, options, and their intersection
A public log of stacking submittable alphas on the quant trading platform WorldQuant BRAIN to raise a score. In episode 4, multiplying a cash-flow regression (pcf) by analyst outlook produced a second alpha and pushed the account into Bronze. But that multiplicative family was already saturated by the first and second alphas. The third had to come from outside the family, a signal built from neither price nor analysts, something genuinely different. This episode is the record of that hunt.
0. One-line summary
After twenty-six simulations, the intersection of "when competitors are strong, the parent company follows" and "the options market is betting on that stock's rise" became the third alpha. Its self-correlation with the first two alphas is 0.14, essentially full independence. Along the way, five hypotheses that looked perfectly healthy died, one came back to life when a sign got flipped, and a syntax trap in the tool made four candidates spin their wheels at once.
1. Starting point: the family is saturated
The first alpha (pcf) asked "has the stock gotten expensive relative to cash flow, compared to its own history?" The second (cau) multiplied that by "are analysts currently raising their outlook?" Both have market cap (cap), i.e. price, in the numerator. That's what I realized at the end of episode 4: pcf, pb (price to book), EV-multiple regressions, and their variants multiplied by analyst signals all share price and form one family, which is why they resemble each other. The first and second alphas already saturated that family, so building a third from the same family would run straight into the self-correlation wall.
The self-correlation rule works like this: a new alpha can't be too similar to alphas I've already submitted (below 0.7). So the third had to be a signal that used neither price nor analyst revisions, one with an entirely different mechanism. The catch is that this kind of "different" signal tends to be weak on its own. The wall for this hunt was obvious from the start.
2. Scattering thirty-six hypotheses across six directions
Instead of brainstorming candidates alone, I ran hypothesis generation in parallel. I set six exploration axes and assigned one agent per axis to design candidates simultaneously: fundamental time-series regression (without price), accounting accruals, options volatility, analysts and news, corporate relationship graphs, and multiplicative combinations. Each agent got the full battlefield record so far: what had died (cross-sectional value ranks, equal-weighted composites), what was alive (time-series regression, multiplication), and what the traps were (small-universe collapse).
The six agents produced six candidates each, thirty-six total. At the end, a synthesis agent deduplicated and narrowed them to a priority list of fifteen. The principle held here too: scatter the hypotheses in parallel, but run the actual simulations in series. This account can only run one simulation at a time, so I built a serial runner that fed each candidate different settings (smoothing, neutralization, weight cap, universe) and measured them one line at a time.
3. Removing price killed the signal too
The axis I had the highest hopes for was "a pure fundamental regression with no price." The logic was simple: pcf was strong because of the time-series regression form, so regressing a company's quality ratios in the same form, minus price, should be both orthogonal and strong. I shorted stocks whose return on equity (ROE) had risen above its own history.
The result was Sharpe minus 0.45. The sign was exactly backwards. Switching to asset turnover (revenue divided by assets) gave minus 0.12. Both dead. The lesson stung: the moment I removed price, the signal died with it. The real reason pcf was strong wasn't the time-series regression form, it was that the price in the numerator jittered every day and kept generating signal. Quality metrics are sticky numbers that only update once a quarter, so even compared against their own history they were nearly flat. Worse, quality behaves more like momentum than mean reversion. Good companies keep being good. So "short quality that's gotten expensive" was a losing bet. Accounting accruals were also weak, at Sharpe 0.3.
4. The two most promising candidates died
The synthesis agent's top two picks were options and earnings surprise.
- Options variance risk premium (
option8): a regression on whether implied volatility (the future volatility the market expects) minus realized volatility (actual past volatility) had widened relative to its own history. There wasn't a single character of price in the input, so orthogonality was guaranteed. - Earnings surprise (
analyst4): the actual reported quarterly EPS minus the analyst consensus estimate, standardized by dispersion across estimates. This is one of the rare anomalies (post-earnings-announcement drift, PEAD) that survives even in large caps, so expectations were high.
Both died. The options regression came in at minus 0.17, earnings surprise at minus 0.1. Orthogonality was perfect, but the signals had no strength. The conclusion from the previous session, "signals that don't resemble anything are weak," held true once more. There were plenty of unlike signals; the unlike signals were weak.
5. The moment one sign flipped
While nearly every orthogonal candidate was collapsing, one stood out with a notably strong negative: the competitor signal from the corporate relationship graph. Sharpe minus 0.99.
A dataset called pv13 contains the average returns of a company's competitors, customers, and partners. I initially set the sign negative based on a market-share-erosion hypothesis: "if competitors are strong, the parent company loses share and weakens." Getting minus 0.99 meant the hypothesis was exactly backwards. Flipping the sign gave plus 0.99.
The interpretation changed. When competitors are strong, the parent company follows along. It wasn't a share-of-market fight, it was information diffusion. When peers in the same industry move first, the good or bad news takes a few days to spread to the parent, and this rides that lag. This was the first strong and genuinely orthogonal signal I'd met in this hunt, a signal built purely from "other companies' returns," with neither price nor fundamentals involved. Still, 0.99 fell short of the submission bar (1.25).
6. Don't add, multiply, again
I pulled out episode 4's lesson again: adding weak signals dilutes them, multiplying creates an intersection. I decided to make the competitor signal (0.99) the anchor and multiply it by another orthogonal signal to sharpen the intersection. I picked options as the partner to multiply against.
Options have separate implied volatilities for calls and puts (option8). If a call's implied volatility is more expensive than the put's, I read that as informed traders buying calls to bet on a rise. The deviation from the equilibrium known as put-call parity is directional information. I converted both the competitor signal and this option signal into stock ranks and multiplied them.
rank(competitor return momentum) × rank(call implied volatility - put implied volatility)
The intersection the multiplication targets is this: go long only on stocks where peers in the same industry are already rising (diffusion) and, at the same time, smart money in the options market is betting on that stock's rise (information). A spot where two streams of information meet on a single stock.
The result was Sharpe 1.70. A 0.99 signal jumped to 1.70 through multiplication. The same pattern from episode 4, where 1.57 became 1.91, worked again. And since neither leg involved price or analyst-outlook changes, the mechanism was completely different from the first two alphas.
One problem remained. Fitness was 0.83, below the submission bar (1.0). The options leg jittered every day, pushing turnover to 28.6%, and high turnover cuts Fitness.
7. Trimming the options leg to clear the gate
The Fitness formula is Sharpe × sqrt(returns / max(turnover, 0.125)). Above 12.5% turnover, cutting turnover raises Fitness. At 28.6%, there was plenty of room to cut. I just needed to slow the signal down without changing its direction.
The fast leg was the options one. I smoothed the call-put implied volatility difference with a 15-trading-day moving average. The competitor leg was already a 21-day average and thus slow, so I only trimmed the fast leg. Turnover dropped from 28.6% to 14.2%, and Sharpe held nearly steady at 1.58. Fitness came to 1.01. It cleared the bar.
All eight gates on the server passed. Every one. Two in particular stood out.
- Sub-universe Sharpe 1.24. In episode 3, an options alpha (cremers) posted a flashy 1.83 only to fall to 0.59 at this gate and get scrapped, because its edge was concentrated in a handful of thinly traded stocks. This time, it held firm at 1.24 even in the sub-universe. The competitor leg spread the signal broadly across large caps, avoiding the trap that using options alone would have fallen into.
- Self-correlation 0.138. Nearly fully independent from the first two alphas. Not only does it clear the 0.7 gate, it's well under the 0.5 required for the higher pool too. Since the score gets a quality bonus for lower self-correlation, the number itself is valuable.
rank(winsorize(ts_mean(ts_backfill(rel_ret_comp, 5), 21), std=4))
× rank(ts_mean(ts_backfill(implied_volatility_call_30 - implied_volatility_put_30, 5), 15))
USA / TOP3000 / delay 1 / decay 6 / industry neutralization / weight cap 2%
→ Sharpe 1.58, Fitness 1.01, turnover 14.2%, sub-universe 1.24, self-correlation 0.138, 8/8 passed
This is the third alpha. Found outside the family, at the spot where other companies' returns meet the options market's bets.
8. A small trap: a name instead of a comma
The most absurd moment of this hunt happened elsewhere. In the first batch, the four most promising candidates all spun their wheels at once with an error saying "input count is 2, should be 1." After staring at it for a while, I found that all four dead candidates shared one thing in common: they all used winsorize (the function that clips extreme values). The three survivors didn't have it.
The cause was syntax. On this platform, winsorize's second argument isn't a positional value, it's a named parameter. It's winsorize(x, std=4), not winsorize(x, 4). Passing it as a plain comma-separated value got read as two inputs and threw an error. Attaching one name brought all four back to life. The competitor signal was among those four, so this one trivial syntax detail nearly buried the third alpha. Without knowing the tool's dialect, even a perfectly healthy hypothesis can look dead.
9. What I learned this episode
- Look at where a signal's engine actually is. pcf was strong not because of its form but because of the price in the numerator. Remove the price and the signal dies even in the same form. What you regress matters before how you regress it.
- Orthogonality isn't free. Almost every unlike signal was weak (options regression, earnings surprise, pure fundamentals). Only one out of thirty-six was both strong and orthogonal: competitor diffusion.
- Doubt the sign. A strong negative isn't necessarily dead, it might just be read backwards. Minus 0.99 flipped its sign and became the seed of the third alpha.
- Multiplication worked twice. Multiplying a weak orthogonal signal by a proven signal to build an intersection raises Sharpe. 0.99 became 1.70.
- Only trim the fast leg. When Fitness is blocked by turnover, don't flatten the whole signal, smooth only the fastest leg to cut turnover while preserving direction.
- Learn the tool's dialect.
winsorize(x, std=4). One name kept four hypotheses alive.
The third alpha is nearly uncorrelated with the first two, which makes it especially valuable on the scoreboard. The next episode will be the grind of stacking these orthogonal alphas toward Silver, and the story of the higher pool, which only alphas below 0.5 self-correlation can enter.
Technical notes (appendix)
- Submission candidate #3 (comp_x_ivspread, id GrwKjdP5):
rank(winsorize(ts_mean(ts_backfill(rel_ret_comp, 5), 21), std=4)) * rank(ts_mean(ts_backfill(implied_volatility_call_30 - implied_volatility_put_30, 5), 15)), USA/TOP3000/delay1/decay6/INDUSTRY/truncation0.02. Sharpe 1.58, Fitness 1.01, turnover 0.1425, returns 0.0584, sub-universe 1.24, self-corr 0.138. Server 8/8 PASS. (The buffered variant ivsm15+decay9, id 2r7XgJRZ, also went 8/8, F1.02/S1.52/sub1.19. Adopted GrwKjdP5 for the higher Sharpe and sub-universe.) Submission is an IP-attribution gate, so it's done directly in the UI. - Data sources:
rel_ret_comp(pv13, average competitor returns, relationship graph),implied_volatility_call_30/implied_volatility_put_30(option8, 30-day call/put implied volatility). Neither is a price ratio nor an analyst estimate, so both are orthogonal to alphas #1 and #2. - What the multiplication means:
rank(a) * rank(b)is an intersection (AND) that only keeps stocks where both signals are high at once. Addition (a + b) is a union (OR) that bets even when only one side is high, letting a weak signal add noise. Competitor 0.99 × options → Sharpe 1.70. - Fitness lift: Fitness = Sharpe × sqrt(|Returns| / max(Turnover, 0.125)). Smoothing only the fast options leg with a 15-day moving average → turnover 0.286 → 0.142, Sharpe held at 1.70 → 1.58, Fitness 0.83 → 1.01. Once turnover hits the free floor (0.125), it can't rise further, so Fitness is then tied to Sharpe alone.
- Dead candidates (in our USA TOP3000 delay1 environment): ROE regression S-0.45, asset turnover regression S-0.12, accruals S0.3, options IV-HV regression S-0.17, earnings surprise (variance-standardized) S-0.1, competitor share-erosion (negative sign) S-0.99. The sign-flipped competitor diffusion S+0.99 became the base for multiplication.
- winsorize syntax: the second argument is a named parameter.
winsorize(x, std=4)is correct;winsorize(x, 4)fails with "Invalid number of inputs: 2, should be exactly 1." Four candidates of the same pattern spun their wheels on this one character. - Workflow: 6 axes (fundamental regression, accruals, options, analyst/news, relationship lead-lag, multiplicative combos) parallel fan-out → 36 candidates generated → synthesis dedup/ranking → shortlist of 15 (8 must-try). Simulation concurrency limit is 1, so runs were serial. About 26 simulations this session.
- Scoring context: self-correlation affects not just the 0.7 gate but continuously feeds the score's Quality Factor (lower is better), and the higher pool (Power Pool) requires 0.5 or below. Candidate #3's 0.138 clears both with room to spare.