For most quant teams, the answer changes as the organization grows, but there's a fairly common evolution:
- Solo or small team → one repository.
- Medium-sized team (5–30 developers/researchers) → monorepo with clear boundaries.
- Large organizations with independent teams → a hybrid approach (monorepo for core infrastructure, separate repos for independent services).
For a systematic trading firm, I'd generally recommend a monorepo unless you have a compelling reason not to.
Why monorepos work well in quant
Quant systems tend to have a lot of shared infrastructure:
- Market data loaders
- Security master
- Portfolio objects
- Risk calculations
- Order management interfaces
- Feature engineering
- Simulation engines
- Execution adapters
- Analytics
These are expensive to version independently.
Instead of:
strategy_a
└── utils_v3
strategy_b
└── utils_v4
strategy_c
└── copied_utils
you get
shared/
market_data/
portfolio/
risk/
execution/
strategies/
statarb/
momentum/
options/
Everyone uses the same APIs.
That dramatically reduces duplicated infrastructure.
But isolate strategies
A mistake is allowing every strategy to import everything.
Instead:
repo/
core/
data/
execution/
risk/
simulation/
libraries/
indicators/
optimization/
ml/
statistics/
strategies/
statarb/
config/
signals.py
portfolio.py
trend/
...
volarb/
...
research/
Each strategy owns only:
- alpha generation
- portfolio construction
- parameters
Everything else is shared.
Separate research from production
This is probably the most important boundary.
Research code optimizes for:
- speed of experimentation
- notebooks
- trying ideas
- changing models
Production optimizes for:
- reproducibility
- monitoring
- stability
- testing
Don't try to make one codebase serve both purposes.
I like:
research/
notebooks/
experiments/
feature_tests/
scratch/
production/
live/
backtest/
execution/
shared/
Research can be messy.
Production cannot.
Shared code lives in libraries
When research produces something valuable, promote it.
Example:
Research notebook:
rolling_entropy(prices)
After it's validated:
shared/features/entropy.py
Now both
- backtests
- production
- future research
use the same implementation.
Think of research as incubating reusable components.
Avoid notebook dependencies
A common anti-pattern:
production imports notebook code
Never.
Instead:
Notebook
↓
Prototype
↓
shared library
↓
production
Production should never depend on notebooks.
Version datasets, not just code
Many failed reproductions happen because:
git checkout old commit
but
market_data/
has changed.
Track:
- dataset versions
- feature versions
- corporate action versions
- model versions
A backtest should specify both:
code commit
+
data snapshot
Feature store approach
Rather than every strategy rebuilding features:
returns
rolling_vol
rolling_beta
sector exposure
earnings surprises
compute once.
Strategies consume features.
That makes validation easier.
Production pipeline
A clean separation is
Raw market data
↓
Normalization
↓
Feature generation
↓
Alpha
↓
Portfolio optimization
↓
Risk limits
↓
Execution
Each stage has a clear interface.
Strategies usually only replace the Alpha stage.
Testing strategy
Different layers deserve different tests.
Core libraries
95%+ unit coverage
Execution
integration tests
Risk
regression tests
Strategies
expected behavior tests
Research
almost no tests
Trying to heavily unit-test exploratory notebooks is usually wasted effort.
Configuration over branching
Avoid:
strategy_a_live.py
strategy_a_live_v2.py
strategy_a_live_final.py
strategy_a_live_final2.py
Instead:
strategy/
signal.py
configs/
conservative.yaml
aggressive.yaml
asia.yaml
us.yaml
One implementation.
Many configurations.
Code ownership
Even inside a monorepo, establish ownership.
For example:
| Directory | Owner |
|---|
| core/data | Data engineering |
| core/execution | Execution team |
| core/risk | Risk team |
| strategies/statarb | StatArb team |
| strategies/options | Options team |
This keeps changes to shared infrastructure from becoming a free-for-all while still making cross-cutting refactors possible.
When separate repositories make sense
Separate repositories become attractive when components have genuinely independent lifecycles or deployment requirements, such as:
- Execution infrastructure with strict operational controls.
- A market data ingestion service used by many unrelated projects.
- An internal machine learning platform shared beyond trading.
- Public/open-source libraries.
- Teams with autonomous release schedules and minimal shared code.
Even then, I'd avoid splitting repositories just because there are multiple strategies. Most strategies benefit from sharing data models, backtesting infrastructure, and risk logic.
A practical structure
A layout like this scales well for many quant organizations:
quant/
core/
data/
execution/
portfolio/
risk/
simulation/
libraries/
features/
statistics/
optimization/
ml/
strategies/
statarb/
momentum/
options/
futures/
research/
notebooks/
experiments/
archived/
services/
live_trading/
scheduler/
monitoring/
configs/
tests/
docs/
The key principle is to treat research as an experimental workspace and production as a product. Research is free to move quickly and iterate, but anything that needs to be reliable—feature calculations, risk models, execution logic, data access, and utilities—should be promoted into shared, tested libraries with stable interfaces before production depends on it. This lets researchers experiment without destabilizing live systems while ensuring production code remains reproducible and maintainable.