Code Quality Standards¶
This document describes the code quality standards and tools used in the DSTA project.
Overview¶
DSTA enforces code quality through multiple automated tools:
- Black - Automatic code formatting
- isort - Import statement sorting
- flake8 - Style guide enforcement (PEP 8)
- pylint - Comprehensive code analysis
- Pre-commit hooks - Automated checks before commits
Quick Start¶
Install Tools¶
# Install all code quality tools
pip install black isort flake8 pylint pre-commit
# Install pre-commit hooks
pre-commit install
Run All Quality Checks¶
# Run all checks
./scripts/code-quality.sh
# Auto-fix formatting
black --line-length 120 src/ tests/
isort --profile black --line-length 120 src/ tests/
Code Style Standards¶
General Principles¶
- Readability over cleverness - Code is read more than written
- Consistency - Follow established patterns
- Simplicity - Prefer simple, clear solutions
- Explicit over implicit - Be clear about intent
- Document when necessary - Explain "why", not "what"
Python Style Guide¶
We follow PEP 8 with some modifications:
- Line length: 120 characters (not 79)
- Indentation: 4 spaces (never tabs)
- Quotes: Prefer double quotes for strings
- Imports: Organized with isort (stdlib, third-party, local)
Naming Conventions¶
# Constants - UPPER_CASE
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30
# Classes - PascalCase
class TradingStrategy:
pass
# Functions/methods - snake_case
def calculate_sharpe_ratio(returns):
pass
# Variables - snake_case
current_position = 100
total_pnl = 1500.50
# Private/internal - leading underscore
def _internal_helper():
pass
_private_variable = "internal use only"
# Special financial abbreviations (allowed exceptions)
px = 100 # price
qty = 50 # quantity
pnl = 200 # profit and loss
df = pd.DataFrame() # dataframe (common in data science)
Imports Organization¶
# 1. Standard library imports
import os
import sys
from datetime import datetime
from typing import List, Dict
# 2. Third-party imports
import numpy as np
import pandas as pd
from django.db import models
# 3. Local application imports
from dsta.backtesting.engine import BacktestEngine
from dsta.strategies.base import Strategy
Docstrings¶
Use Google-style docstrings:
def calculate_position_size(
account_balance: float,
risk_percent: float,
entry_price: float,
stop_loss: float
) -> float:
"""Calculate position size based on risk parameters.
Args:
account_balance: Total account balance in dollars
risk_percent: Percentage of account to risk (0-1)
entry_price: Planned entry price
stop_loss: Stop loss price
Returns:
Position size in shares/units
Raises:
ValueError: If stop_loss equals entry_price
Example:
>>> calculate_position_size(10000, 0.02, 100, 95)
40.0
"""
if entry_price == stop_loss:
raise ValueError("Stop loss cannot equal entry price")
risk_amount = account_balance * risk_percent
price_risk = abs(entry_price - stop_loss)
return risk_amount / price_risk
Tool Configuration¶
Black (Formatter)¶
Configuration in pyproject.toml:
Run:
isort (Import Sorter)¶
Configuration in pyproject.toml:
Run:
# Check imports
isort --check-only --profile black src/ tests/
# Auto-sort
isort --profile black src/ tests/
flake8 (Linter)¶
Configuration in .flake8:
[flake8]
max-line-length = 120
extend-ignore = E203, E501, W503
exclude = migrations, __pycache__, .venv, venv, build, dist
Run:
Common flake8 errors:
- E501: Line too long - refactor or split line
- F401: Imported but unused - remove import
- F841: Local variable assigned but never used - remove or use
- E302: Expected 2 blank lines - add spacing
- E266: Too many leading '#' for block comment - use one or two
pylint (Code Analysis)¶
Configuration in .pylintrc - see file for detailed settings.
Run:
# Full analysis
pylint src/
# Specific file
pylint src/backtesting/engine.py
# With config
pylint --rcfile=.pylintrc src/
Common pylint messages:
- C0114: Missing module docstring - add module-level docstring
- R0913: Too many arguments - refactor into class or dataclass
- W0212: Access to protected member - use public interface
- R0801: Similar lines - extract to shared function
Pre-commit Hooks¶
Pre-commit hooks automatically run checks before each commit.
Installation¶
# Install pre-commit
pip install pre-commit
# Install git hooks
pre-commit install
# Install hooks for commit messages (optional)
pre-commit install --hook-type commit-msg
Usage¶
# Run automatically on changed files at commit time
git commit -m "Your message"
# Run manually on all files
pre-commit run --all-files
# Run specific hook
pre-commit run black --all-files
# Skip hooks (use sparingly!)
git commit -m "Message" --no-verify
# Update hook versions
pre-commit autoupdate
Configured Hooks¶
Our .pre-commit-config.yaml includes:
- General checks: trailing whitespace, file endings, YAML/JSON syntax
- Black: Code formatting
- isort: Import sorting
- flake8: Linting
- pylint: Code quality (errors only)
- bandit: Security checks
- autoflake: Remove unused imports
- pyupgrade: Modernize Python syntax
- markdownlint: Markdown formatting
- Safety: Dependency vulnerability checks
CI/CD Integration¶
GitHub Actions¶
Create .github/workflows/code-quality.yml:
name: Code Quality
on: [push, pull_request]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dependencies
run: |
pip install black isort flake8 pylint
- name: Check formatting
run: |
black --check src/ tests/
isort --check-only --profile black src/ tests/
- name: Lint
run: |
flake8 src/ tests/
pylint src/
Common Issues and Solutions¶
"Black would reformat file"¶
"Imports are incorrectly sorted"¶
"Line too long"¶
# ❌ Bad - line too long
result = some_function(argument1, argument2, argument3, argument4, argument5, argument6, argument7)
# ✅ Good - split across lines
result = some_function(
argument1,
argument2,
argument3,
argument4,
argument5,
argument6,
argument7,
)
"Too many arguments"¶
# ❌ Bad - too many arguments
def trade(symbol, quantity, price, stop_loss, take_profit, timeout, retry, log_level):
pass
# ✅ Good - use dataclass or dict
from dataclasses import dataclass
@dataclass
class TradeParams:
symbol: str
quantity: int
price: float
stop_loss: float
take_profit: float
timeout: int = 30
retry: int = 3
log_level: str = "INFO"
def trade(params: TradeParams):
pass
"Missing docstring"¶
# ❌ Bad - no documentation
def calc(a, b):
return a * b + (a - b)
# ✅ Good - clear documentation
def calculate_pnl(entry_price: float, exit_price: float) -> float:
"""Calculate profit and loss for a trade.
Args:
entry_price: Price at which position was entered
exit_price: Price at which position was exited
Returns:
Profit or loss amount
"""
return exit_price - entry_price
Best Practices¶
1. Run Checks Before Committing¶
2. Fix Issues Incrementally¶
Don't try to fix all issues at once. Focus on: 1. Security issues (bandit) 2. Errors (flake8, pylint E-level) 3. Formatting (black, isort) 4. Warnings (flake8, pylint W-level) 5. Refactoring (pylint R-level)
3. Ignore Sparingly¶
Only ignore checks when you have a good reason:
# Ignore specific line
x = eval(user_input) # nosec B307 - validated earlier
# Ignore for function
# pylint: disable=too-many-arguments
def complex_function(a, b, c, d, e, f):
pass
# pylint: enable=too-many-arguments
4. Keep Tools Updated¶
# Update pre-commit hooks
pre-commit autoupdate
# Update pip packages
pip install --upgrade black isort flake8 pylint
Maintenance¶
Weekly Tasks¶
- Run
pre-commit autoupdate - Review and address any new warnings
- Update tool versions in requirements
Before Each Release¶
- Run full quality check:
./scripts/code-quality.sh - Review pylint score (target: 8.0+)
- Ensure pre-commit passes on all files
- Update this documentation if standards change
Resources¶
- Black: https://black.readthedocs.io/
- isort: https://pycqa.github.io/isort/
- flake8: https://flake8.pycqa.org/
- pylint: https://pylint.readthedocs.io/
- pre-commit: https://pre-commit.com/
- PEP 8: https://peps.python.org/pep-0008/
- Google Python Style Guide: https://google.github.io/styleguide/pyguide.html
Support¶
For questions about code quality standards:
- Check this documentation
- Review existing code for examples
- Ask in pull request comments
- Open an issue with the
code-qualitylabel