Fly-Brain Trader v3: a mushroom-body reward learner, at five checkpoints
A small, biologically inspired reward learner modelled on the fruit fly's mushroom body. There's no backprop and no gradient descent. The network learns online through a three-factor, dopamine-gated plasticity rule: presynaptic activity × postsynaptic target × a reward-prediction-error signal. The reward signal is loss-averse ("pain" for losses weighted 2×, "happiness" for gains). It also gets counterfactual ("fictive") updates for actions it didn't take, and a performance bonus ("love") while it beats a benchmark.
The repository releases the same network at five points in its training run: random init → synthetic curriculum → real-data pre-training → validation → blind test. You can inspect what each stage did to the weights.
Research artefact, not a trading model. In the blind test (Jan 2024 – Oct 2026) it returned +3.0% a year in US dollars, against +22.5% for the S&P 500. Its stock picks beat random picks made on the same schedule (raw p = 0.041), but that isn't significant after correcting for the 10 tests in the study (Holm p = 0.41). Don't use it to trade. "Pain", "happiness" and "love" are scalar reward terms in an update rule. The model doesn't experience anything.
Paper: paper.pdf, Pain, Happiness and Love in a Fly-Brain Trader: Blind Tests of a Connectome-Inspired Reward Learner in Equity Markets (working paper, Oct 2026)
Code, training and evaluation scripts: github.com/MikhailPeganov/fly-brain-trader
Model summary
| Stock brain | Market brain | |
|---|---|---|
| Task | Rank stocks each week (score → top-20 long book) | Decide long vs short each week |
| Inputs | 22 features | 10 features |
| Input encoding | 110 projection neurons (5 Gaussian tuning curves per feature, σ = 1.2) | 50 projection neurons |
| Hidden layer | 2,000 Kenyon cells, k-WTA (top 100 = 5% active) | 500 Kenyon cells, top 25 active |
| Input → hidden | Fixed sparse random projection, 7 inputs per cell, log-normal weights (14,000 non-zeros) | Fixed, 3,500 non-zeros |
| Hidden → output | Plastic: 2 output neurons × (2,000 KC + 110 direct) = 4,220 trainable synapses | 2 × 550 = 1,100 trainable synapses |
| Output | balance d = (L − S)/(L + S), divided by its running RMS | Same, compared with its own running mean/std |
This is a random-features model with a learned linear readout. The expansion layer is never trained. Only the readout synapses change, and they change through a local reward-modulated rule, not a loss gradient. The direct projection-neuron → output pathway (lateral-horn-like) lets simple monotone rules be learned without relying on sparse conjunctions.
Training objective: the plasticity rule
Every week, each position's outcome u (next-week return in volatility units, c = trading cost) is turned into a loss-averse valence. Losses are weighted λ = 2× as heavily as gains, as in prospect theory:
r = pos · u − c
v = max(r, 0) − λ · max(−r, 0) λ = 2
The output neuron for the side that was traded receives v. The side that wasn't traded receives a weaker fictive signal (φ = 0.3), computed from what it would have earned. Each signal is taken relative to a running baseline b, which gives a reward-prediction error as in the Schultz–Dayan–Montague account of dopamine. That error then updates only the synapses of the Kenyon cells that were active:
b ← b + 0.01 (mean(g) − b) running baseline (RPE reference)
w ← w + η (g − b)ᵀ a / n three-factor update, η = 0.01
w ← w + ρ (w₀ − w) homeostasis, ρ = 2×10⁻⁴, w₀ = 1
w ← clip(w, 0, 3) synapses stay excitatory and bounded
Love (performance shaping): once a week the trader compares its trailing 52-week USD return with the S&P 500's. When it leads by Λ > 0, the synapses behind its current holdings and its current long/short call get extra reinforcement:
Δw_side = η · min(Λ / 0.10, 1) · aᵀ 1[held] / n
Falling behind triggers no extra update. A slow "tonic pain" trace (pain_fast, pain_slow in the config) raises the trading threshold after runs of losses.
Training procedure
One continuous run (seed 0), weekly steps, with no resets between stages. Each checkpoint is a snapshot of the same weights.
| Stage | Checkpoint | Data | Steps | Learning rate | Exploration |
|---|---|---|---|---|---|
| 0 | 0_untrained |
none: random wiring, all readout synapses at w₀ = 1 | 0 | — | — |
| 1 | 1_after_school |
Synthetic curriculum: 5 simulated markets × up to 6 interleaved rounds. Each lesson is a fresh 100-stock, 1,300-day episode (~208 weekly updates) | up to 30 lessons | 0.03 | Gaussian score noise σ = 0.5 |
| 2 | 2_after_pretrain |
Tokyo small caps, Mar 2017 – Dec 2021 | ~250 weeks | 0.01 | σ = 0.5 |
| 3 | 3_after_validation |
Tokyo small caps, 2022 – 23 (the only period used to choose settings) | 97 weeks | 0.01 | none |
| 4 | 4_after_blind_test |
Tokyo small caps, Jan 2024 – Oct 2026, never seen during design | 136 weeks | 0.01 | none |
Evaluation is prequential. In stages 3 and 4 the model keeps learning online, but each week's decision is made before it learns from that week's outcome. Every reported return is therefore out-of-sample at the time of the decision. The test period was simulated once, after all settings were locked in code.
Curriculum ("maths school"). Before it sees real prices, the brain is trained on synthetic markets that each follow one textbook mechanism: random walk, momentum (AR(1) drift), mean reversion (Ornstein–Uhlenbeck), low-volatility premium with clustered volatility, and news shocks that move prices the next week. Worlds are interleaved and shuffled each round so later lessons don't overwrite earlier ones. After each lesson, the brain sits an exam on a fresh episode. The score is the rank IC against next-week returns, and the pass mark is half the IC of the hidden true signal (the "oracle").
Hyperparameter selection. Only one setting was tuned: the market brain's shorting threshold θ ∈ {0.75, 1.5}, chosen on 2022–23 validation. θ = 1.5 was locked before any test data was read. Everything else was fixed in advance.
Hyperparameters
| Name | Value | Meaning |
|---|---|---|
n_kc, k |
2,000, 100 | Kenyon cells, active cells (5% sparsity) |
| fan-in | 7 | random projection neurons per Kenyon cell |
T, sigma |
5, 1.2 | tuning curves per feature, tuning width |
lr (η) |
0.01 (0.03 in school) | readout learning rate |
lam (λ) |
2.0 | loss aversion (pain weight) |
fictive (φ) |
0.3 | counterfactual update strength |
homeo (ρ) |
2 × 10⁻⁴ | pull towards w₀ |
w0, wmax |
1.0, 3.0 | baseline and max synaptic weight |
direct |
1.0 | direct-pathway gain |
| θ (market brain) | 1.5 σ | short when this much more bearish than usual |
| love cap | 10 pts | lead over the S&P 500 that gives full-strength love |
Training dynamics
Mean absolute change per readout synapse, computed from the released checkpoints:
| Stage | Stock brain | Market brain |
|---|---|---|
| School | 0.0220 | 0.153 |
| Pre-train 2017–21 | 0.0031 | 0.032 |
| Validation 2022–23 | 0.0012 | 0.013 |
| Blind test 2024–26 | 0.0012 | 0.019 |
- Most of the learning happens in the curriculum. School moves the stock brain about 7× more than five years of real Tokyo data. Real markets are low-signal, and the loss-averse RPE update mostly cancels out.
- Weights stay close to the homeostatic set point. The stock brain's readout ends at mean 0.998 and std 0.034 around w₀ = 1. What it learns lives in small, consistent offsets, not large weights.
- The random projection never changes.
pn_to_kcis identical across all five checkpoints, which you can check by diffing the safetensors.
What it learned
Preference per feature is measured as the slope of the long-vs-short balance as that one feature sweeps from −2 to +2, with all others held at zero. The curriculum installs strong news preferences: positive on tone and bullish keywords, negative on bearish keywords. It also installs a strong preference for the least-squares forecast. Real Tokyo data then gradually shifts the brain towards lower volatility, lower beta and away from lottery-like jumps (MAX), the same tilts the paper finds in the holdings.
Curriculum exams (final, this seed)
| Synthetic world | Rank IC | Oracle IC | Result |
|---|---|---|---|
| Random walk | −0.000 | 0 | pass (no false signal) |
| Momentum | 0.042 | 0.115 | fail (37% of oracle) |
| Mean reversion | 0.014 | 0.231 | fail (6%) |
| Low-volatility premium | 0.038 | 0.029 | pass |
| News predicts next week | 0.041 | 0.054 | pass |
A real capacity limit. Across three seeds the learner never acquired momentum and mean reversion together. That held even with the direct pathway, regime features, longer schooling and different learning rates. The two rules map the same inputs to opposite actions ("buy recent winners" vs "buy recent losers"). A single-layer, loss-averse readout averages them away instead of gating on regime. News, which conflicts with nothing, was learned by all three seeds.
Evaluation (blind test, Tokyo small caps, USD, after costs)
| Metric | Value |
|---|---|
| Annual return | +3.0% (S&P 500 +22.5%) |
| Sharpe | 0.26 |
| Max drawdown | −19% |
| Weeks short | 12% |
| Picks vs 2,000 random pickers on the same schedule | p = 0.041 raw · 0.41 Holm |
| Long/short timing vs random weeks | p = 0.26 |
| 2022–23 validation | −15.2% a year (S&P 500 +1.2%) |
Costs are half the live quoted spread, plus the price impact of a $10k order (Amihud) and a 0.10% commission. Only stocks with at least $100k of daily turnover are eligible. The testing framework was itself validated: on 40 placebo markets, 2 came out significant at 5%, and planted signals were detected (IC 0.06 → p = 0.003). Retraining with the training script reproduces the blind-test return exactly (reproduces_published_result: true in training_log.json).
Quick start
import numpy as np
from flybrain_v3_minimal import FlyBrain, FEATURES
brain = FlyBrain.load("checkpoints/4_after_blind_test", "stock_brain")
X = np.clip(np.random.default_rng(0).normal(size=(100, len(FEATURES))), -2.5, 2.5) # 100 made-up stocks
balance, score = brain.decide(X) # rank by score; balance > 0 leans "long"
top20 = np.argsort(-score)[:20]
# one online learning step: pain/happiness from what happened next (returns in volatility units)
brain.feel(X, position=np.isin(np.arange(100), top20).astype(float), outcome=np.random.normal(size=100))
flybrain_v3_minimal.py needs only numpy and safetensors. Its forward pass and plasticity rule match the training code's decisions exactly. Run python flybrain_v3_minimal.py to load every checkpoint and print its top picks on the same random input. That shows the ranking emerge between 0_untrained (no preference) and 1_after_school.
Files
checkpoints/<stage>/stock_brain.safetensors pn_to_kc (110×2000, fixed) · w_long, w_short (2110, plastic) · tuning_centers
checkpoints/<stage>/market_brain.safetensors pn_to_kc (50×500) · w_long, w_short (550) · tuning_centers
checkpoints/<stage>/config.json sizes, learning settings and running state (baselines, gain control, tonic pain)
training_log.json exam scores, validation and blind-test metrics, learned preferences per stage
Inputs (22 features, cross-sectional normal scores clipped to ±2.5)
- Quant factors: 12-1 momentum, 1-month and 1-week return, distance to 52-week high, 60-day volatility, biggest daily jump (MAX), Amihud illiquidity, volume trend, 3-month return vs market, share of no-trade days
- Simple-model features: 50/200-day trend, time-series momentum, Bollinger z-score, rolling least-squares forecast, EWMA volatility, market beta
- News: company-level volume, tone, bullish and bearish keyword share. These were zero in real Tokyo data, because no company news history was available, so they were only trained in school.
- Regime: whether trend or mean-reversion rules have been paying recently
Limitations
- One market, one seed, and one 2.8-year test window. Treat the Tokyo picking signal as a hypothesis for forward testing.
- Survivorship bias: the universe is today's listings. Spreads are a single 2026 snapshot.
- Historical news is market-level only. The curriculum's synthetic markets are stylised.
- This model is inspired by the mushroom body. It isn't a simulation of the measured FlyWire connectome.
- Retraining on freshly downloaded data won't reproduce these exact numbers if the vendor's history changes.
Data
No market or news data is included, only the model's weights, configs and results. Training used Yahoo Finance prices, live Yahoo quotes for spreads, and the GDELT 2.0 news archive, each under its own terms. The GitHub repository's scripts download them.
Citation
@misc{peganov2026flybrain,
title = {Pain, Happiness and Love in a Fly-Brain Trader: Blind Tests of a Connectome-Inspired Reward Learner in Equity Markets},
author = {Peganov, Mikhail},
year = {2026},
note = {Working paper}
}
License
MIT

