WORLDQUANT BRAIN
Python Alphas Competition 2026
Complete Beginner to Competitor Study Notes
Competition Period: June 1 – July 12, 2026 | Prize Pool: $30,000
Region: USA D1 | Language: Python | Universe: TOP3000
SECTION 1 — The Big Picture
1.1 What is a Quantitative Alpha?
An alpha is simply a daily signal that tells a trading system which stocks to buy, which to sell, and how strongly.
You are NOT building a trading bot or managing real money. You are writing a Python function that looks at
market data and outputs a score for each of 3000 stocks — every single trading day.
Positive score → buy that stock
Negative score → sell that stock
Larger number → stronger conviction
The competition rewards you when your alpha signal correctly predicts which stocks will go up and which will go
down. Brain handles all the trading mechanics — your entire job is producing the best signal.
1.2 What Brain Does With Your Signal
Step What It Does Setting
Your alpha function Outputs a score for each of
3000 stocks daily
Neutralization Removes overall market bias MARKET or SECTOR
from your scores
Pasteurization Masks stocks not in the ON or OFF
active universe
Scaling Normalizes positions to a Automatic
unit book size
Truncation Caps max position size per 0.8 typical
stock
Simulation Trades and measures PnL BrainLabs / Actual
over historical data
1.3 How You Are Scored
After simulation, Brain reports these metrics. Your goal is to maximise Sharpe Ratio above everything else:
Metric Meaning Target
Sharpe Ratio Return divided by risk. The > 1.5 is solid. > 2.0 is
primary metric. excellent.
Ann. Return Annualised profit Higher is better.
percentage.
Max Drawdown Worst peak-to-trough loss Less negative is better.
period.
Turnover (Daily) How often your positions Moderate — too high hurts.
change.
Fitness Brain’s composite score. Must pass threshold.
⚠️ SHARPE IS KING
Every technique you learn in these notes has one purpose — improve your Sharpe Ratio. A Sharpe above 1.5
is competitive. Above 2.0 is excellent. Focus all iteration on moving this number up.
1.4 The Competition Workflow
You will repeat this loop dozens of times between now and July 12:
1. Write or modify your alpha function
2. Test it locally with BrainLabs — fast, takes seconds
3. Check the Sharpe. If good, validate with Actual Simulation
4. Submit your best alphas to Brain before the deadline
💡 KEY RULE FROM DOCS
Develop and iterate using BrainLabs simulation (fast feedback), then validate your best Alpha with actual
simulation before submission. Never waste remote simulation quota on unfinished ideas.
SECTION 2 — Python Fundamentals for Brain
You do not need to know everything about Python. You only need the building blocks that appear in every alpha.
This section covers exactly those.
2.1 Variables — Storing Information
A variable is a named box that holds a value. The = sign means store this value — it is NOT the equals sign from
maths.
name = "mean_reversion" # text (called a string)
delay = 1 # whole number (int)
sharpe = 1.84 # decimal number (float)
active = True # True or False (bool)
2.2 Data Types — Critical for Brain
Brain is extremely strict about data types. Understanding these four types now will save you hours of debugging
later:
Type Description
int Whole numbers: 1, 0, -5, 3000
float Decimal numbers: 1.84, -0.05, 0.0004
float32 A compact 32-bit decimal. Brain requires this for
your output.
float64 A 64-bit decimal. NumPy defaults to this — must
convert to float32.
bool True or False only.
str Text in quotes: "returns", "USA", "MARKET".
🔴 CRITICAL: The float32 Rule
Your alpha function MUST return a float32 array. NumPy operations like [Link] silently upgrade float32
to float64. If you forget to convert back, Brain raises: "Alpha vector is not float32". Always end
with .astype(np.float32).
2.3 Lists — Multiple Values in One Box
A list holds multiple values. Indexing starts at 0. Negative indices count from the end. You will use this constantly
with Brain data.
returns = [0.01, -0.02, 0.005, 0.03, -0.01]
returns[0] # 0.01 ← first item (index 0, NOT 1)
returns[-1] # -0.01 ← last item (most recent day)
returns[-2] # 0.03 ← second to last (yesterday)
returns[1:3] # [-0.02, 0.005] ← slice: from index 1 up to (not including) 3
💡 Why This Matters for Brain
In your alpha, [Link][-1] gives you TODAY's returns for all 3000 stocks. [Link][-2] gives you
yesterday. [Link] gives you the full window. This exact pattern is in every alpha you will write.
2.4 Functions — Reusable Instructions
A function takes inputs, does something, and returns an output. Your entire alpha is one function. Helper
functions like pasteurize and neutralize are also functions you define.
def greet(name): # def = define. name = input parameter
message = "Hello " + name
return message # return = send back the result
result = greet("Brain") # result = "Hello Brain"
Key parts: def (define), function name, parameters in brackets, return at the end.
2.5 Importing Libraries
Libraries are pre-built toolkits. You bring them in with import. Brain uses two main libraries:
import numpy as np # The maths toolkit — used for EVERYTHING
import [Link] as npt # Type hints for NumPy arrays
# The "as np" gives it a short nickname
# So you write [Link]() instead of [Link]()
💡 Import Rule for Brain Submission
When submitting to Brain, only "from [Link] import alpha" is allowed in the same cell as your @alpha
function. Do NOT include Brain, BrainCache, or SimulationSettings imports in that cell — your submission will
fail.
SECTION 3 — NumPy — The Engine Behind Every Alpha
NumPy is a Python library for fast maths on large arrays of numbers. Every piece of data in Brain is a NumPy
array. Every operation you perform on stock data uses NumPy functions. This section is not optional — it is the
core skill.
3.1 What is a NumPy Array?
A NumPy array is like a Python list, but faster, more powerful, and built for maths. Unlike a list, every item in an
array must be the same data type.
import numpy as np
# 1D array — one row of numbers
prices = [Link]([100.0, 105.0, 98.0, 110.0, 107.0])
print([Link]) # (5,) ← 5 items, 1 dimension
print([Link]) # float64
3.2 2D Arrays — The Shape of Brain Data
Brain data arrives as a 2D array: rows are dates (time), columns are stocks (instruments). This is the most
important concept in these notes.
# Imagine this is [Link] with lookback=2
# Shape: [3 rows, 4 stocks]
[Link] = [Link]([
[ 0.01, -0.02, 0.005, 0.03 ], # row 0: 3 days ago
[-0.01, 0.03, -0.010, 0.01 ], # row 1: 2 days ago
[ 0.02, -0.01, 0.015, -0.02 ], # row 2: TODAY (most recent)
])
[Link] # (3, 4) ← 3 dates, 4 instruments
[Link][-1] # [ 0.02, -0.01, 0.015, -0.02] ← today only
[Link][:, 0] # [ 0.01, -0.01, 0.02] ← stock 0, all dates
3.3 axis=0 vs axis=1 — The Most Important Concept
The axis parameter tells NumPy WHICH direction to perform an operation. This single concept explains most of
the maths in every alpha.
axis Direction Result
axis=0 Collapse rows → operate Result shape:
DOWN the columns (across [n_instruments]
time)
axis=1 Collapse columns → operate Result shape: [n_dates]
ACROSS the rows (across
stocks)
# [Link] shape: [3 dates, 4 stocks]
[Link]([Link], axis=0)
# → ONE number per STOCK (average return of each stock over time)
# Result shape: (4,) ← this is what we want for an alpha signal!
[Link]([Link], axis=1)
# → ONE number per DATE (average return across all stocks that day)
# Result shape: (3,) ← NOT what we want for alpha output
💡 Why axis=0 in Every Alpha
Your alpha must return ONE signal per STOCK. axis=0 collapses the time dimension and gives you one value
per stock. That is why the docs always use axis=0. [Link]([Link], axis=0) means "for each stock,
calculate the average return across all historical days in the window."
3.4 Essential NumPy Functions for Brain
Function Purpose
[Link](a, axis=0) Mean ignoring NaN values. Use instead of [Link] —
stock data has gaps.
[Link](a, axis=0) Standard deviation ignoring NaN. Used for
volatility calculations.
[Link](a, axis=0) Sum ignoring NaN values.
np.nan_to_num(a, nan=0) Replace NaN with 0 (or any value). Used before
division.
[Link](a) Returns True where values are NaN. Used for
masking.
[Link](a) Returns indices that would sort the array. Used
for ranking.
[Link](a, L1 norm — sum of absolute values. Used for scaling
ord=1) positions.
[Link](np.float32) Convert array dtype. ALWAYS do this before
returning from alpha.
[Link]() Create a separate copy. ALWAYS do this before
modifying Brain data.
[Link](n) Array of n zeros. Used to initialize store
variables.
[Link]([a, b]) Stack arrays vertically (add rows). Used for rank
caching.
[Link](cond, x, y) Choose x where condition is True, else y. Used for
rank calculations.
3.5 NaN — Missing Data in Brain
NaN stands for "Not a Number". It represents missing data. In Brain, stocks that have no data for a given day
have NaN in their field. This is extremely common — every alpha must handle NaN correctly.
import numpy as np
# NaN contamination — normal mean FAILS with missing data
returns = [Link]([0.01, [Link], 0.03, -0.02])
[Link](returns) # nan ← one NaN poisons the whole result!
[Link](returns) # 0.006667 ← ignores NaN, correct result
# Always use nan-safe functions in Brain alphas
🔴 Never Use [Link] in a Brain Alpha
Always use [Link], [Link], [Link] instead of [Link], [Link], [Link]. Stock data always has
NaN gaps. One NaN in the input turns the entire output into NaN, which breaks your alpha signal.
SECTION 4 — The Brain Setup
Every Brain session starts with the same three lines. Understanding exactly what each line does will prevent
confusion later.
4.1 The Three Setup Lines
from brain import Brain, BrainCache
from [Link] import SimulationSettings
from [Link] import alpha
import numpy as np
import [Link] as npt
brain = Brain(raw=True)
cache = BrainCache(brain)
4.2 Brain(raw=True) — The Connection
Brain() is the main connection to WorldQuant's data platform. It defaults to:
Parameter Default
instrument_type 'EQUITY' — we are trading stocks
region 'USA' — US stock market
delay 1 — one day execution delay (realistic)
universe 'TOP3000' — the 3000 largest US stocks
raw=True — tells Brain to give you data in its native format (float32, int32). Without raw=True, everything
converts to float64, which does not match the real simulation environment. Always use raw=True.
4.3 BrainCache — Speed Optimisation
BrainCache wraps Brain and stores downloaded data in memory so you do not re-download the same data
repeatedly during simulation. Pass it to every simulation call.
⚠️ When to Create a New BrainCache
Create a new BrainCache whenever you change region, delay, or date range. Cached data is tied to those
parameters. If you change settings but keep the old cache, you will get wrong results silently.
# If you change region — create a new cache
brain_eur = Brain(raw=True) # same Brain instance is fine
cache_eur = BrainCache(brain_eur) # new cache for EUR region
# BrainCache is for simulation only
# For exploring data, use brain.get_data_frame() directly:
df = brain.get_data_frame("returns")
print([Link]) # (n_dates, n_instruments)
print([Link]) # check the data types
SECTION 5 — The @alpha Decorator
The @alpha decorator is the heart of everything. Every alpha you submit must use it. It declares what data your
alpha needs and what state it remembers between time steps.
5.1 The Full Structure
from [Link] import alpha
import numpy as np
import [Link] as npt
@alpha(
data=["returns"], # which data fields to load
store=[], # what state to remember (empty for simple alphas)
)
def mean_reversion(data, store) -> [Link][np.float32]:
# [Link] — shape [lookback+1, n_instruments], float32
# [Link] — always available, int: 1=in universe, 0=out
signal = -[Link]([Link], axis=0)
return [Link](np.float32)
5.2 The data Parameter — Declaring Your Inputs
data is a list of field names. Each field becomes available inside your function as [Link], shaped
[lookback+1, n_instruments].
Declaration Effect
data=["returns"] Load only daily returns (most common)
data=["returns", Load returns AND closing prices
"close"]
data=["returns", Load returns AND trading volume
"volume"]
data=[] Load no fields (unusual, rarely used)
🔴 RULE: Never include "universe" in data
"universe" is automatically available as [Link] in every alpha. If you add it to data=[], Brain raises an
error. The docs are explicit: "Do not include universe in your data list."
5.3 Accessing Data Inside Your Alpha
# [Link] shape: [lookback+1, n_instruments]
# With lookback=5: shape is [6, 3000]
[Link][-1] # TODAY's returns — shape: [3000]
[Link][-2] # Yesterday's returns — shape: [3000]
[Link] # Full window — shape: [6, 3000]
# universe: 1 = stock is in universe today, 0 = it is not
[Link][-1] # Today's universe mask — shape: [3000]
# Values: 1 or 0 for each stock
⚠️ Data Arrays Are Read-Only
The engine marks data arrays as non-writable. Trying to modify them in-place raises a ValueError. Always
use .copy() before modifying. This is one of the 6 rules Brain checks.
# WRONG — raises ValueError: assignment destination is read-only
a = [Link][-1]
a[a < 0] = 0 # trying to modify in-place
# CORRECT — copy first, then modify
a = [Link][-1].copy()
a[a < 0] = 0 # safe to modify the copy
5.4 The Sliding Window
Brain calls your alpha function once per trading day. Each day, the window slides forward: one new row of data
enters at the bottom, the oldest row drops off the top. With lookback=5, you always have 6 rows (5 history +
today).
Setting Window Size Meaning
lookback=0 1 row Today only. No history.
lookback=5 6 rows Today + 5 previous days.
Good for short-term.
lookback=21 22 rows Today + 21 previous days
(~1 month).
lookback=63 64 rows Today + 63 previous days
(~1 quarter).
💡 Early Time Steps Warning
During the first few trading days, the window is still growing and has fewer rows than lookback+1. Your code
must handle this gracefully. The store variable (store.x is None) is the standard way to detect the first call.
SECTION 6 — The Three Essential Helper Functions
Every competitive alpha uses three helper functions: pasteurize, neutralize, and scale. These are not optional
extras — they are required for your alpha to be valid and competitive. The docs show them in every example.
6.1 pasteurize — Remove Out-of-Universe Stocks
Pasteurization sets the signal to NaN for any stock that is not in the active universe today. If you skip this, your
alpha bets on stocks that cannot actually be traded.
def pasteurize(a, u):
"""
a = your alpha signal array, shape [n_instruments]
u = [Link][-1], shape [n_instruments], values: 1 or 0
"""
a = [Link]() # MUST copy — never modify in-place
a[~[Link](bool)] = [Link] # set out-of-universe to NaN
return a
# How it works:
# [Link](bool) → [True, False, True, True, False, ...]
# ~ → flips: [False, True, False, False, True, ...]
# a[~[Link](bool)] = [Link] → NaN for all out-of-universe stocks
6.2 neutralize — Remove Market Bias
Neutralization removes the overall market direction from your signal. Without it, your alpha might just be
betting "the whole market goes up" every day, which is not an edge.
def neutralize(a):
"""Remove market mean from the signal."""
a0 = np.nan_to_num(a, nan=0, posinf=0, neginf=0)
return a - [Link](a0)
# Why nan_to_num first?
# [Link] on an array with NaN returns NaN
# nan_to_num replaces NaN with 0 so the mean calculation works
# The mean is subtracted from every stock's signal
# Result: signal is now centred around zero (market-neutral)
6.3 scale — Normalise Position Sizes
Scaling ensures your signal sums to a unit book size. Without it, position sizes can be arbitrary and the
simulation cannot compare alphas fairly.
def scale(a):
"""L1-normalise the signal to unit book size."""
a0 = np.nan_to_num(a, nan=0, posinf=0, neginf=0)
norm = [Link](a0, ord=1) # L1 norm = sum of absolute values
return a / norm if norm > 0 else a # guard against division by zero
# ord=1 means: norm = |a[0]| + |a[1]| + |a[2]| + ...
# Dividing by this makes the absolute values sum to 1.0
# "Unit book" means: total long positions = total short positions = 0.5
6.4 Putting It All Together — The Standard Alpha Pattern
Every alpha you write will follow this exact pattern from the docs:
from [Link] import alpha
import numpy as np
import [Link] as npt
def pasteurize(a, u):
a = [Link]()
a[~[Link](bool)] = [Link]
return a
def neutralize(a):
a0 = np.nan_to_num(a, nan=0, posinf=0, neginf=0)
return a - [Link](a0)
def scale(a):
a0 = np.nan_to_num(a, nan=0, posinf=0, neginf=0)
norm = [Link](a0, ord=1)
return a / norm if norm > 0 else a
@alpha(
data=["returns"],
store=[],
)
def mean_reversion(data, store) -> [Link][np.float32]:
# Step 1: compute raw signal
a = -[Link]([Link], axis=0).astype(np.float32)
# Step 2: remove out-of-universe stocks
a = pasteurize(a, [Link][-1])
# Step 3: remove market bias, normalise positions
a = scale(neutralize(a))
# Step 4: ALWAYS return float32
return [Link](np.float32)
SECTION 7 — Store — Memory Across Time Steps
The store parameter lets your alpha remember things between time steps. By default, your alpha function has
no memory — each day it only sees the current window of data. The store fixes that.
7.1 Why You Need Store
Suppose you want to track a running average that goes further back than your lookback window allows. Or you
want to compare today's signal to what it was 30 days ago. You need store for that.
7.2 Simple Usage — Untyped String Entry
@alpha(
data=["returns"],
store=["my_state"], # declare a store variable called my_state
)
def my_alpha(data, store) -> [Link][np.float32]:
# On the FIRST call, store.my_state is None
if store.my_state is None:
store.my_state = [Link]([Link][1], dtype=np.float32)
# Update the stored value
today = [Link][-1].copy().astype(np.float32)
store.my_state = 0.9 * store.my_state + 0.1 * today # EMA
return store.my_state.astype(np.float32)
7.3 Typed Store Entry — Recommended for Instrument Arrays
If your store variable is instrument-sized (one value per stock), use a typed dict. The simulator then
automatically extends it when the universe grows.
@alpha(
data=["returns"],
store=[{"name": "ema", "dims": "i", "extend": np.float32(0)}],
)
def ema_alpha(data, store) -> [Link][np.float32]:
if [Link] is None:
[Link] = [Link]([Link][1], dtype=np.float32)
today = np.nan_to_num([Link][-1].copy(), nan=0.0).astype(np.float32)
[Link] = (0.9 * [Link] + 0.1 * today).astype(np.float32)
a = [Link]()
a = pasteurize(a, [Link][-1])
a = scale(neutralize(a))
return [Link](np.float32)
Setting Meaning
"dims": "i" Instruments axis. Auto-extended when universe
grows. Use for per-stock arrays.
"dims": "xi" 2D array. x = free axis (e.g. time), i =
instruments. Use for rank caches.
"dims": "ii" 2D square matrix. Both axes are instruments. Use
for correlation matrices.
"extend": np.float32(0) Fill new instruments with 0. Must match array
dtype EXACTLY.
"extend": Fill new instruments with NaN. Use when missing
np.float64([Link]) history should be ignored.
🔴 extend dtype Must Match Exactly
The extend value must be a NumPy scalar matching the array dtype. np.float32(0) for float32 arrays.
np.float64(0) for float64 arrays. Never use bare Python 0, 0.0, or [Link] alone. The simulation raises a
ValueError if types do not match.
SECTION 8 — Running Simulations
8.1 SimulationSettings Reference
SimulationSettings controls how Brain runs your alpha. You must configure this correctly before running either
simulation path.
Parameter Typical Value Description
instrument_type 'EQUITY' Market type. Always EQUITY
for this competition.
region 'USA' USA D1 Region. Required for
this competition.
delay 1 Days between signal and
execution. 1 is standard.
universe 'TOP3000' The 3000 largest US stocks
by market cap.
lookback 5, 21, 63 Extra history rows. Window
= lookback + 1.
start_date '2020-01-01' BrainLabs sim start date.
end_date '2024-12-31' BrainLabs sim end date.
neutralization 'MARKET' Removes overall market
direction from positions.
pasteurization 'ON' Masks out-of-universe
instruments.
truncation 0.8 Caps max position size.
Prevents over-
concentration.
decay 10 Smooths the alpha signal
over N days.
language 'PYTHON' Required for actual
simulation. Not for
BrainLabs.
visualization True Shows PnL chart. BrainLabs
only.
max_position 'OFF' Position size limit. OFF
for standard use.
max_trade 'OFF' Trade size limit. OFF for
standard use.
8.2 Path 1: BrainLabs — Use This for Development
from brain import Brain, BrainCache
from [Link] import SimulationSettings
brain = Brain(raw=True)
cache = BrainCache(brain)
settings = SimulationSettings(
instrument_type="EQUITY",
region="USA",
delay=1,
universe="TOP3000",
lookback=5,
visualization=True, # shows PnL chart
)
# The 3-step pipeline
alpha_matrix = brain.generate_alpha_matrix(mean_reversion, settings, cache)
positions = brain.generate_alpha_positions(alpha_matrix, settings, cache)
stats = brain.generate_alpha_stats(positions, settings, cache)
8.3 Path 2: Actual Simulation — Use This for Submission
# CELL 1: Alpha definition — ONLY from [Link] import alpha here
from [Link] import alpha
import numpy as np
import [Link] as npt
# ... helper functions and @alpha decorated function here ...
# CELL 2: Submission — Brain imports go here
from brain import Brain
from [Link] import SimulationSettings
brain = Brain(raw=True)
submit_settings = SimulationSettings(
instrument_type="EQUITY", region="USA", delay=1,
universe="TOP3000", lookback=5, decay=10,
neutralization="MARKET", pasteurization="ON",
truncation=0.8, language="PYTHON",
visualization=False, max_position="OFF", max_trade="OFF",
)
alpha_id = [Link](mean_reversion, submit_settings)
alpha_result = brain.get_alpha(alpha_id)
print(alpha_result)
🔴 Critical Cell Separation Rule
When submitting to the Brain backend, your @alpha function and from [Link] import alpha MUST be in
a separate cell from Brain, BrainCache, and SimulationSettings imports. Mixing them causes the simulation to
fail. This is explicitly stated in the documentation.
SECTION 9 — Alpha Strategies for the Competition
Now you understand all the mechanics. Here are proven alpha strategies you can implement, test, and iterate
on. Each is explained from the idea through to the code.
9.1 Mean Reversion — The Baseline
Idea: stocks that recently went up tend to come back down, and vice versa. Bet against recent performance.
@alpha(data=["returns"], store=[])
def mean_reversion(data, store) -> [Link][np.float32]:
# Negative of average recent return
# High return → negative signal → sell
# Low return → positive signal → buy
a = -[Link]([Link], axis=0).astype(np.float32)
a = pasteurize(a, [Link][-1])
a = scale(neutralize(a))
return [Link](np.float32)
Variants to try: Change lookback (5, 10, 21). Use only the last N days: [Link][-N:]. Use nanstd to normalise
by volatility.
9.2 EMA Momentum — Trend Following
Idea: stocks with a consistently rising smoothed return signal have momentum. Follow the trend.
@alpha(
data=["returns"],
store=[{"name": "ema", "dims": "i", "extend": np.float32(0)}],
)
def ema_momentum(data, store) -> [Link][np.float32]:
n = [Link][1]
if [Link] is None:
[Link] = [Link](n, dtype=np.float32)
today = np.nan_to_num([Link][-1].copy(), nan=0.0).astype(np.float32)
[Link] = (0.9 * [Link] + 0.1 * today).astype(np.float32)
# Positive EMA → trending up → buy
a = [Link]()
a = pasteurize(a, [Link][-1])
a = scale(neutralize(a))
return [Link](np.float32)
Variants to try: Adjust the EMA smoothing factor (0.9/0.1). Try 0.8/0.2 for faster response. Try 0.95/0.05 for
slower, more stable signal.
9.3 Cross-Sectional Rank — More Robust Signal
Idea: instead of using raw return values, rank stocks by return each day. The rank-based signal is more robust to
outliers and extreme values.
@alpha(data=["returns"], store=[])
def rank_reversion(data, store) -> [Link][np.float32]:
today = [Link][-1].copy()
# Replace NaN with -inf so they rank last
finite = [Link]([Link](today), -[Link], today)
# Double argsort gives the rank of each stock
ranks = [Link]([Link](finite)).astype(np.float32)
# Put NaN back for missing data
ranks[[Link](today)] = [Link]
# Negative rank → mean reversion (top ranked → sell)
a = -ranks
a = pasteurize(a, [Link][-1])
a = scale(neutralize(a))
return [Link](np.float32)
9.4 Mean of Rank — From the Official Docs
This is the most advanced example directly from the Brain documentation. It caches cross-sectional ranks over
more days than the lookback window allows, giving a smoother signal.
RANK_DAYS = 20
@alpha(
data=["returns"],
store=[{"name": "rank_cache", "dims": "xi",
"extend": np.float64([Link])}],
)
def mean_of_rank(data, store) -> [Link][np.float32]:
today = [Link][-1]
finite = [Link]([Link](today), -[Link], today)
today_rank = [Link]([Link](finite)).astype(np.float64)
today_rank[[Link](today)] = [Link]
if store.rank_cache is None:
store.rank_cache = today_rank[[Link], :]
else:
new_cache = [Link]([store.rank_cache,
today_rank[[Link], :]])
store.rank_cache = new_cache[-RANK_DAYS:]
mean_rank = [Link](store.rank_cache, axis=0)
return (-mean_rank).astype(np.float32)
SECTION 10 — Rules Summary & Competition Checklist
10.1 The 6 Iron Rules from the Docs
Brain enforces these rules on every submission. The Code Validator in your IDE checks all of them automatically.
Rule Requirement Why
Rule 1 Exactly one @alpha Having two decorators
decorator per function breaks the simulation.
Rule 2 Function must accept def my_alpha(data, store) —
exactly 2 parameters always these two.
Rule 3 Return type must be Always end
float32, shape [n_insts] with .astype(np.float32).
Rule 4 Do NOT include "universe" It is always available
in data list automatically.
Rule 5 Do NOT mutate data arrays Always .copy() before
in-place modifying.
Rule 6 [Link] dtype must np.float32(0) not 0.
match array dtype np.float64(0) not 0.0.
10.2 Before Every Submission — Checklist
5. Run in BrainLabs first. Fix any errors. Check Sharpe > 1.5.
6. Confirm @alpha decorator is present.
7. Confirm return is .astype(np.float32).
8. Confirm "universe" is NOT in data=[...].
9. Confirm all data modifications use .copy() first.
10. Confirm pasteurize, neutralize, scale are all applied.
11. Confirm alpha cell has ONLY from [Link] import alpha (no Brain, BrainCache, SimulationSettings).
12. Run Actual Simulation. Wait for result. Check Sharpe.
13. If Sharpe is good, it is submitted — done.
10.3 Competition Timeline
Date Activity Goal
June 1 (TODAY) Competition opens. Start Mean reversion baseline
writing alphas immediately. first.
Week 1-2 Write 5+ alpha variants. Focus on Sharpe > 1.5.
Test all in BrainLabs.
Week 3-4 Iterate best alphas. Try Target Sharpe > 2.0.
rank-based and EMA
variants.
Week 5-6 Final polish. Submit best Actual Simulation for each.
3-5 alphas.
July 12 Deadline. No more All submissions must be in.
submissions after this
date.
🔥 FINAL TIP: Quantity + Quality
Submit multiple alphas. The $30K pool rewards winners across a range of performances. A portfolio of 5 solid
alphas (Sharpe 1.5-2.0) is better than one perfect alpha. Use BrainLabs to test fast, then only use Actual
Simulation on your best candidates.