Trying to ship one alpha, I built an alpha factory
The first installment in a series that publicly records each session of the WorldQuant BRAIN project.
There is a place where, if you write down a stock trading idea as a single line of formula, a computer scores whether it would actually have made money in past markets. It is WorldQuant BRAIN, run by the hedge fund WorldQuant. Even a formula made by an ordinary person can be licensed into real fund strategies if it is good enough, and the contributor becomes a paid consultant. This post is a record of the day when I had just created an account and tried to submit my first alpha, only to end up building a pipeline that automatically produces alphas first.
What is WorldQuant BRAIN, anyway?
The formulas made here are called alphas, and in short, an alpha is a formula that turns data into stock-level bets. For example, if you translate the idea “buy stocks that have fallen a lot over the last 5 days and sell stocks that have risen a lot,” it becomes one line: -ts_delta(close, 5). The platform then automatically takes long (buy) positions in stocks with high values and short (sell) positions in stocks with low values, simulates daily profit and loss using historical data, and scores the result.
The goal is consultant status, and the honest reality
BRAIN has a scoring system. The more good alphas you produce, the more points you accumulate, and once you pass 10,000 points (Gold), you become eligible for a consultant invitation. If you become a consultant, you receive quarterly compensation based on the performance of your alphas. To be honest, the amount is usually not large. Some people have reportedly earned 100 dollars over four months. So I think the real value is less in the money itself and more in the learning, the career signal, and the path it can open toward a WorldQuant internship or full-time role. There is also one deal term to know before starting: the moment you sign the consultant agreement, ownership of the alphas you created transfers to WorldQuant.
My reason for stepping in was simple. I had just made an account, and a friend was going to hand over a few strategies. If I uploaded those well, I thought I might at least reach the consultant threshold. Still, before uploading something someone else gave me as-is, I felt I needed to take at least one alpha all the way through with my own hands to see how this arena works. So I decided to submit one alpha on my own first.
For an alpha to be accepted as submittable, it has to clear several criteria. The core ones are three. The Sharpe ratio, which measures return relative to risk, needs to be roughly 1.25 or higher. Fitness, the overall quality score, needs to be 1.0 or higher. Daily trading turnover has to fall between 1% and 70%. On top of that, there is also a condition that it must not be too similar to alphas I have already submitted or to the overall alpha pool (correlation above 0.7).
I decided to build tools first, not work by hand
First of all, I had to decide on the method. I could have clicked through the platform screen one by one by hand, but I was obviously going to test many alphas quickly, and a click-by-hand workflow would hit its limits soon. So I decided to build a small automation tool locally from the beginning. It was a pipeline that logs in to BRAIN with Python, sends formulas to run simulations, receives the results, and stores them one by one in a database.
Login as the first gate
The first wall in automation is usually login. Depending on the account, BRAIN asks for browser-based biometric authentication on the first login, and this was effectively the only real obstacle to a fully automated login. So I arranged the order to verify this riskiest part first. If login failed after I had built everything else, all of it would have been wasted effort. Fortunately, login passed in one shot without biometric authentication, and the number-one automation risk disappeared almost anticlimactically.
What the first alpha taught me
As soon as login worked, I ran the simplest reversal alpha, -ts_delta(close, 5), and the result was oddly instructive. Sharpe was 1.29, above the 1.25 baseline, but Fitness got stuck at 0.70. Because the Fitness formula effectively multiplies Sharpe by the square root of “returns divided by turnover,” the score was cut down by a high turnover of 40%. It looked like the score would rise if I could simply reduce trading.
Five rounds of tuning, from 0.70 to 0.95
This was where the real work started. I held on to the same idea and ran five rounds of simulations while changing only the settings. Fitness slowly climbed from 0.70 to 0.85, 0.89, 0.94, and 0.95.
Each round taught me something. At first, I tried to reduce turnover by smoothing the signal with decay, but that actually broke everything. A 5-day reversal is such a fast signal that if you smooth it too much, turnover falls, but returns and Sharpe collapse much more severely. Only after seeing Sharpe fall to 0.9, 0.7, and 0.6 as I raised decay did I realize the direction was wrong.
So I turned the other way. Instead of cutting turnover, I went toward making the signal itself stronger. When I lowered decay, Sharpe jumped to 1.39. When I used -ts_sum(returns, 5), the “sum of returns,” instead of price change, it got even better. Neutralizing by industry produced the highest returns.
There is an operator called hump as the standard tool for reducing turnover. It reduces trading by ignoring small position changes, but for this alpha it was complete poison. When I set the value to 0.01, trading almost stopped, turnover fell to 0.009, and Sharpe collapsed to 0.44. Even when I set it very small at 0.0005, Sharpe was cut down without fail. The lesson was clear. This alpha’s returns were embedded in those position changes themselves, so suppressing the changes killed the signal along with them.
I also tried truncation to prevent weight from concentrating in a single stock, but whether I set it to 0.05 or 0.10, the result was exactly the same. The weights of this alpha were not reaching the cap in the first place, so pulling on that lever did nothing. It was a dead lever.
Asking the server directly
At 0.95, it would not climb any further. So I changed the approach. Instead of guessing more, I decided to ask the server directly. BRAIN has a check feature that evaluates whether an alpha is submittable item by item, and I attached it to my tool to inspect the best candidate. The answer was clear. Sharpe, turnover, weight concentration, and robustness in a small universe all passed, and exactly one thing, Fitness, failed. That confirmed two facts at once: Fitness 1.0 is a real hard gate, and everything except that one item was passing.
I decided to accept the conclusion honestly. As long as a very common short-term reversal signal is used on its own, Fitness 0.95 is its ceiling. I could twist settings to squeeze out the last 0.05, but an alpha forced into shape that way would collapse in front of future real data anyway. The real answer was to find a better idea or mix in another uncorrelated signal.
Building an alpha factory
Now that I had confirmed the infrastructure worked, the remaining task was to scale it up. Instead of entering formulas one by one by hand, I built a batch miner that combines operators and data fields to mass-produce candidates, automatically simulates them, filters them by criteria, and runs the server check only on those that pass. I split the roles into a candidate generator, a concurrent runner, a data-field explorer, and an orchestrator that ties the three together. When I first ran it on a small batch, the flow worked fairly smoothly: it generated dozens of candidates, simulated them all at once, ranked the results, and automatically sent the ones that passed the criteria to the check step.
The first real mining run and two walls
For the first real mining run, I chose the fundamental side rather than price. Since I had already learned that price reversal was saturated, I wanted to find signals in places where fewer people were crowded. So I applied valuation templates to 24 company fundamental scores and ran them, and that was where I met two walls.
The first wall was that signals made by simply ranking fundamental scores were too weak. The best one had a Sharpe of 0.47, nowhere near the 1.25 threshold. The second wall was a more practical problem. This account could effectively run only one simulation at a time, so when I tried to run three at once, about half were bounced with concurrent execution limit errors. The documentation said the free tier allowed around three, but it seems a freshly created account was tighter than that. On top of that, there was also a cap on the total number of simulations per day, so after running nearly 70 that day, I hit the limit right there.
Lessons the day left behind
As I wrapped up the day, a few lessons remained.
First, infrastructure and alpha are completely different problems. Building a working automation pipeline was not that difficult for an engineer, but the truly hard part was finding alphas that clear the gate. In one example, someone tested 1,103 alphas and got only 28, so in a manner of speaking, this is a 40-to-1 world.
Second, do not guess, measure. I learned the most when I stopped endlessly twisting the threshold and asked the server directly. Once you know exactly what is holding you back, the time spent spinning in circles disappears.
Third, the sooner you meet constraints, the better. Walls like biometric login, concurrent execution limits, and daily quotas make the design that much more accurate when you hit them early instead of later.
Fourth, common signals have a ceiling. That is why something everyone knows, like a 5-day reversal, does not work on its own. Value is hidden in places where fewer people are crowded, or in combinations of multiple signals.
The next story
The infrastructure is done for now. From here, if I put in an expression, the simulation and submission check run right away. The next step will probably be one of three things. I might take the strategy my friend used and validate it immediately, mix in a signal uncorrelated with reversal to raise Sharpe, or keep mining steadily over several days within the daily quota. Whichever path I choose, it will run on top of the tools built on the first day. I set out to submit one alpha and ended up building an alpha factory first, but in the long run, I suspect this is actually the faster path.
Technical notes (appendix)
I am leaving the details that did not fit into the prose. This part is for technical readers.
Best candidate so far (misses only Fitness among the submission gates):
trade_when(ts_rank(ts_std_dev(returns, 22), 252) > 0.55, rank(-ts_sum(returns, 5)), -1)
- Settings: USA / TOP3000 / delay 1 / decay 2 / neutralization INDUSTRY / truncation 0.01
- Results: Sharpe 1.37, Fitness 0.95, turnover 0.255, returns 0.123, sub-universe Sharpe 1.14
- Server submission check: LOW_SHARPE PASS, LOW_FITNESS FAIL, turnover PASS, CONCENTRATED_WEIGHT PASS, LOW_SUB_UNIVERSE_SHARPE PASS, SELF_CORRELATION PENDING
Fitness climb record: 0.70 (basic reversal) → 0.85 (gating) → 0.89 (lowered decay) → 0.94 (returns-sum core) → 0.95 (industry neutralization), then stagnation.
Tool structure (local Python):
- Harness: login (including biometric authentication branch), simulation submission, polling, result lookup, submission check, sqlite logging
- Batch miner: candidate generation (templates x data fields x parameters), concurrent runner, data-field exploration, filter, automatic survivor check
- 14 datasets: price-volume, fundamentals, analyst, options, news, social, and so on
Measured constraints:
- Concurrent simulation limit around 1 (anything above that is CONCURRENT_SIMULATION_LIMIT_EXCEEDED)
- Daily simulation quota exists (hit the limit at around 70 that day)
- data-fields lookup requires specifying a dataset, and the data-sets list returns at most 50 at a time