Thanks for your interest in contributing to alpaca-trader. This document covers development setup, code standards, and the review process.
-
Clone the repo and install dependencies:
git clone https://github.com/costajohnt/alpaca-trader.git cd alpaca-trader uv sync -
Configure environment variables:
cp .env.example .env
Fill in your API keys in
.env. At minimum you need Alpaca credentials. See.env.examplefor all available options. -
Always paper trade first:
# In your .env file — this must be set before running any trading scripts ALPACA_PAPER=trueNever run against a live account during development. All testing and development must use Alpaca's paper trading environment.
-
Set up pre-commit hooks:
pre-commit install
This runs Ruff linting and format checks automatically on every commit.
- Never commit directly to
main. Always create a feature branch. - Use descriptive kebab-case branch names:
add-earnings-guardfix-trailing-stop-calculationupdate-sector-mappings
- Pull requests require CI to pass (lint + tests) before merge.
- Keep PRs focused on a single concern. Split large changes into smaller PRs when possible.
This project uses Ruff for linting and formatting, configured in pyproject.toml.
- Line length: 120 characters
- Target version: Python 3.14+
- Type hints: Use full type hints with modern Python 3.14+ syntax
- Lint rules: E, F, W, I (pyflakes, pycodestyle, isort)
Always use the project's structured logging — never bare print() for warnings, errors, or operational messages:
from strategies.log import get_logger
logger = get_logger(__name__)
logger.info("Processing %s", ticker)
logger.warning("Skipping %s — insufficient data", ticker)
logger.error("API call failed for %s: %s", ticker, err)uv run ruff check . # lint
uv run ruff format --check . # format check
uv run ruff check --fix . # auto-fix lint issues
uv run ruff format . # auto-formatuv run python -m pytest tests/ -vWith coverage:
uv run python -m pytest tests/ -v --cov=strategies --cov=scripts --cov-report=term-missing --cov-fail-under=58- Overall minimum: 58% (CI enforced)
strategies/package: aim for ~78%+- Branch coverage is enabled — both paths of conditionals should be tested
- Place tests in
tests/with filenames matchingtest_*.py - Test files for
strategies/foo.pyshould be namedtests/test_foo.py - Test files for
scripts/bar.pyshould be namedtests/test_bar.py - Mock external API calls (Alpaca, Finnhub, OpenRouter, yfinance) — tests must not make real network requests
The following files must not be modified without careful review and discussion. These are core safety mechanisms that protect against financial loss:
| File | Purpose |
|---|---|
strategies/risk_guard.py |
Portfolio-level kill switch (daily loss + drawdown limits) |
strategies/resilience.py |
API retry logic with exponential backoff + circuit breaker |
scripts/strategy_agent.py |
Autonomous strategy improvement agent |
scripts/agent_tools.py |
Agent safety constraints (hardcoded floors, no-touch list) |
tests/ |
All test files |
.github/workflows/ |
CI and deployment pipelines |
If your change touches any of these files, call it out explicitly in your PR description and explain why the change is necessary.
These are non-negotiable safety rules for any code that interacts with order execution:
- Always paper trade first. Set
ALPACA_PAPER=truein.env. Never test against a live account. - Never exceed 5% position size. No single position may exceed 5% of account equity. This is enforced in
strategies/sizing.py. - All changes must pass backtesting. Run
uv run python scripts/pipeline_backtest.pyto validate that strategy changes do not degrade performance (Sharpe ratio must remain >= 0.3). - Respect sector guard limits. Maximum 3 positions / 40% exposure per known sector; 5 positions / 50% for Unknown sector.
- Strategies produce signals; execution is separate. Keep signal generation and order execution as distinct concerns.
- 4-layer safety on all entries. Technical triggers are necessary but not sufficient — fundamentals, sentiment, and insider signals can each veto a trade.
Before submitting a pull request, confirm that:
- I created a feature branch (not committing to
main) - My code passes
uv run ruff check .with no errors - My code passes
uv run ruff format --check .with no errors - I added or updated tests for my changes
- All tests pass:
uv run python -m pytest tests/ -v - Coverage remains at or above 58%
- I used structured logging (
get_logger) instead ofprint() - I did not modify safety-critical files without discussion
- If touching trading logic: I ran backtesting to validate the change
- If touching position sizing: I confirmed the 5% cap is respected
- My PR description explains what changed and why