Thank you for your interest in contributing to py_rt! This guide will help you get started.
- Code of Conduct
- Getting Started
- Development Workflow
- Coding Standards
- Testing Requirements
- Pull Request Process
- Commit Conventions
We expect all contributors to:
- Be respectful and inclusive
- Provide constructive feedback
- Focus on what is best for the community
- Show empathy towards others
- Rust 1.70+ - Install rustup
- Python 3.11+ - Install Python
- uv - Fast Python package manager:
pip install uv - Git - Version control
- Fork the repository on GitHub
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/RustAlgorithmTrading.git
cd RustAlgorithmTrading- Add upstream remote:
git remote add upstream https://github.com/SamoraDC/RustAlgorithmTrading.git# Python dependencies
uv sync --dev
# Rust dependencies
cd rust
cargo build# Install pre-commit
pip install pre-commit
# Install hooks
pre-commit installAlways create a new branch for your work:
git checkout -b feature/my-new-feature
# or
git checkout -b fix/bug-descriptionBranch naming conventions:
feature/- New featuresfix/- Bug fixesdocs/- Documentation changesrefactor/- Code refactoringtest/- Test additions/improvementsperf/- Performance improvements
Follow our coding standards when making changes.
# Python tests
uv run pytest
# Rust tests
cd rust
cargo test --workspace
# Integration tests
cargo test --workspace --test '*'# Python
uv run ruff check src/
uv run mypy src/
# Rust
cd rust
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warningsFollow our commit conventions:
git add .
git commit -m "feat: add new momentum indicator"git push origin feature/my-new-featureThen create a pull request on GitHub.
We follow PEP 8 with these specifics:
- Line length: 88 characters (Black default)
- Imports: Sorted with
isort - Type hints: Required for all functions
- Docstrings: Google style
from typing import List, Optional
import pandas as pd
import numpy as np
def calculate_moving_average(
prices: pd.Series,
window: int = 20,
min_periods: Optional[int] = None
) -> pd.Series:
"""Calculate simple moving average.
Args:
prices: Price series to calculate MA on
window: Number of periods for MA
min_periods: Minimum periods required for calculation
Returns:
Series containing moving average values
Raises:
ValueError: If window is less than 1
Example:
>>> prices = pd.Series([100, 102, 101, 103, 105])
>>> ma = calculate_moving_average(prices, window=3)
"""
if window < 1:
raise ValueError("Window must be at least 1")
return prices.rolling(
window=window,
min_periods=min_periods or window
).mean()# Format code
uv run black src/ tests/
# Sort imports
uv run isort src/ tests/
# Lint
uv run ruff check src/ tests/
# Type check
uv run mypy src/We follow Rust API Guidelines with these specifics:
- Line length: 100 characters
- Formatting:
rustfmtwith default settings - Linting: All
clippywarnings must be resolved - Documentation: All public items must have doc comments
/// Calculate exponential moving average.
///
/// # Arguments
///
/// * `prices` - Slice of price values
/// * `period` - Number of periods for EMA
///
/// # Returns
///
/// Vector containing EMA values
///
/// # Errors
///
/// Returns `Error::InvalidPeriod` if period is 0
///
/// # Example
///
/// ```
/// use common::indicators::ema;
///
/// let prices = vec![100.0, 102.0, 101.0, 103.0, 105.0];
/// let ema_values = ema(&prices, 3).unwrap();
/// ```
pub fn ema(prices: &[f64], period: usize) -> Result<Vec<f64>, Error> {
if period == 0 {
return Err(Error::InvalidPeriod);
}
let multiplier = 2.0 / (period as f64 + 1.0);
let mut result = Vec::with_capacity(prices.len());
// Initialize with SMA
let sma: f64 = prices[..period].iter().sum::<f64>() / period as f64;
result.push(sma);
// Calculate EMA
for &price in &prices[period..] {
let ema_value = (price - result.last().unwrap()) * multiplier
+ result.last().unwrap();
result.push(ema_value);
}
Ok(result)
}# Format code
cargo fmt --all
# Lint
cargo clippy --all-targets --all-features -- -D warnings
# Check docs
cargo doc --no-deps --document-private-itemsAll Python code must have:
- Unit tests: Test individual functions
- Integration tests: Test component interactions
- Minimum coverage: 80%
import pytest
from src.strategies.momentum import MomentumStrategy
class TestMomentumStrategy:
"""Test suite for momentum strategy."""
@pytest.fixture
def strategy(self):
"""Create strategy instance for tests."""
return MomentumStrategy(lookback=20, threshold=0.02)
def test_signal_generation(self, strategy, sample_data):
"""Test signal generation on sample data."""
signals = strategy.generate_signals(sample_data)
assert len(signals) == len(sample_data)
assert signals.isin([-1, 0, 1]).all()
def test_position_sizing(self, strategy):
"""Test position size calculation."""
size = strategy.calculate_position_size(
signal=1,
capital=100000.0,
price=150.0
)
assert size > 0
assert size * 150.0 <= 100000.0 * strategy.position_pctRun tests:
# All tests
uv run pytest
# With coverage
uv run pytest --cov=src --cov-report=html
# Specific test file
uv run pytest tests/unit/python/test_strategies.py -vAll Rust code must have:
- Unit tests: In same file as code
- Integration tests: In
tests/directory - Doc tests: In documentation examples
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ema_calculation() {
let prices = vec![100.0, 102.0, 101.0, 103.0, 105.0];
let ema_values = ema(&prices, 3).unwrap();
assert_eq!(ema_values.len(), prices.len() - 2);
assert!(ema_values.last().unwrap() > &100.0);
}
#[test]
fn test_ema_invalid_period() {
let prices = vec![100.0, 102.0];
let result = ema(&prices, 0);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::InvalidPeriod));
}
#[tokio::test]
async fn test_order_execution() {
let engine = ExecutionEngine::new_test();
let order = Order::new_market_buy("AAPL", 100);
let response = engine.execute(order).await.unwrap();
assert_eq!(response.status, OrderStatus::Filled);
}
}Run tests:
# All tests
cargo test --workspace
# Specific test
cargo test test_ema_calculation
# With output
cargo test -- --nocapture
# Integration tests only
cargo test --workspace --test '*'- Add/update docstrings for new functions
- Update
CHANGELOG.mdwith your changes - Update README if needed
All checks must pass:
- Tests (Python and Rust)
- Linting (ruff, clippy)
- Type checking (mypy)
- Formatting (black, rustfmt)
- Coverage (>80%)
Use this template:
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] All tests passing locally
## Checklist
- [ ] Code follows style guidelines
- [ ] Documentation updated
- [ ] CHANGELOG.md updated
- [ ] Tests achieve >80% coverage
- [ ] No clippy/ruff warnings
## Related Issues
Closes #123- Address reviewer feedback promptly
- Keep discussion focused and professional
- Update PR based on suggestions
PRs require:
- At least 1 approval from maintainer
- All CI checks passing
- No merge conflicts
- Up to date with main branch
We use Conventional Commits specification.
<type>(<scope>): <subject>
<body>
<footer>
feat: New featurefix: Bug fixdocs: Documentation onlystyle: Code style changes (formatting)refactor: Code refactoringperf: Performance improvementstest: Test additions/updatesbuild: Build system changesci: CI configuration changeschore: Other changes (dependencies, etc.)
# Feature
git commit -m "feat(strategies): add RSI indicator support"
# Bug fix
git commit -m "fix(risk): correct position size calculation for short positions"
# Documentation
git commit -m "docs(api): update REST API endpoint examples"
# Breaking change
git commit -m "feat(execution)!: change order routing to async-only
BREAKING CHANGE: OrderRouter.execute() is now async and must be awaited"Use these scopes:
market-data- Market data servicesignal-bridge- Signal bridge componentrisk- Risk managerexecution- Execution enginestrategies- Trading strategiesbacktesting- Backtesting engineapi- API clientsdocs- Documentationci- CI/CD
- GitHub Issues: Report bugs
- GitHub Discussions: Ask questions
- Documentation: Read the docs
Use this template:
**Description**
Clear description of the bug
**To Reproduce**
Steps to reproduce:
1.
2.
3.
**Expected Behavior**
What should happen
**Actual Behavior**
What actually happens
**Environment**
- OS: [e.g., Ubuntu 22.04]
- Python: [e.g., 3.11.5]
- Rust: [e.g., 1.75.0]
- Version: [e.g., 0.1.0]
**Logs**Paste relevant logs here
Use this template:
**Problem**
What problem does this solve?
**Solution**
Proposed solution
**Alternatives**
Other solutions considered
**Additional Context**
Any other contextBy contributing, you agree that your contributions will be licensed under the Apache License 2.0.
Thank you for contributing to py_rt!