11 KiB
11 KiB
Coding Conventions
Analysis Date: 2026-02-23
Naming Patterns
Files (Python):
- Modules: lowercase with underscores (e.g.,
redis_streams.py,finbert_analyzer.py) - Packages: underscores, not hyphens (e.g.,
news_fetcher,sentiment_analyzer) - Private modules: prefix with underscore (e.g.,
_deduplicate_and_publish)
Files (TypeScript/React):
- Components: PascalCase (e.g.,
MetricsRow.tsx,ProtectedRoute.tsx) - Hooks: camelCase with
useprefix (e.g.,useAuth.ts,useWebSocket.ts) - Utilities/APIs: camelCase (e.g.,
client.ts,auth.ts) - Config files: camelCase with dots (e.g.,
tsconfig.app.json,vite.config.ts)
Functions and Methods:
- Snake_case in Python (e.g.,
_deduplicate_and_publish,ensure_group) - CamelCase in TypeScript/React (e.g.,
startRegistration,useCallback) - Async functions: prefix with
asynckeyword, no special naming convention - Private/internal functions: prefix with underscore in Python (e.g.,
_make_config,_make_signal)
Variables:
- Snake_case in Python for all variables (e.g.,
session_factory,confidence_threshold) - CamelCase in TypeScript (e.g.,
isAuthenticated,loading,token) - Constants: UPPER_CASE with underscores (e.g.,
SEEN_HASHES_KEY = "news:seen_hashes",RAW_STREAM = "test:news:raw") - Loop variables and temporaries: follow standard conventions (e.g.,
msg_id,score,ticker)
Types and Classes:
- Classes: PascalCase (e.g.,
StreamPublisher,TradeEvaluator,BaseStrategy) - Enums: PascalCase (e.g.,
TradeSide,TradeStatus,SignalDirection) - Type hints: use full import path or import explicitly (e.g.,
list[PositionInfo],dict[str, str]) - Generic types: use Python 3.12+ syntax without
typing.prefix (e.g.,dict,list,tuple)
Interfaces (TypeScript):
- PascalCase with
Propssuffix for component props (e.g.,MetricsRowProps) - PascalCase for return types (e.g.,
UseAuthReturn) - Prefix common interfaces:
Iis not used; use descriptive names instead
Code Style
Formatting (Python):
- Line length: 120 characters (configured in
pyproject.tomlunder[tool.ruff]) - Target Python version: 3.12 (configured under
[tool.ruff] target-version) - Formatter: Ruff (for linting and formatting)
- No Black configuration — Ruff is the primary tool
Formatting (TypeScript/React):
- Target: ES2022 (configured in
tsconfig.app.json) - Module resolution: bundler
- Strict mode: enabled (
"strict": true) - JSX mode: react-jsx
Linting (Python):
- Tool: Ruff (
ruff>=0.3) - Key rules: Follow Python 3.12 conventions, enforce async functions
- MyPy: enabled (
mypy>=1.8)warn_return_any = truewarn_unused_configs = true
Linting (TypeScript):
- ESLint with flat config (
eslint.config.jsindashboard/) - Extends:
@eslint/js(recommended)typescript-eslint(recommended)react-hooks(react-hooks/exhaustive-deps)react-refresh(for Vite)
- Global ignores:
dist/ - Files:
**/*.{ts,tsx}
Import Organization
Python Import Order:
from __future__ import annotations(always first if present)- Standard library:
import asyncio,import json,import logging, etc. - Third-party packages:
from redis.asyncio import Redis,from pydantic import BaseModel, etc. - Shared modules:
from shared.config import BaseConfig,from shared.redis_streams import StreamPublisher - Service-specific modules:
from services.news_fetcher.config import NewsFetcherConfig - Absolute imports preferred; relative imports acceptable within a service
TypeScript Import Order:
- React and core libraries:
import { useState, useCallback } from 'react' - Routing:
import { Routes, Route } from 'react-router-dom' - Third-party UI/packages:
import { startRegistration } from '@simplewebauthn/browser' - Internal utilities/API:
import { client } from '../api/client' - Components:
import { MetricsRow } from '../components/MetricsRow' - Hooks:
import { useAuth } from '../hooks/useAuth'
Path Aliases:
- Python: No path aliases configured; use absolute imports from project root
- TypeScript: None configured in
tsconfig.app.json; use relative paths with../
Error Handling
Python Error Handling:
- Use
try/exceptfor external service calls (Redis, database, API calls) - Log all exceptions at the point of handling:
logger.exception("Context: %s", variable) - Specific exception handling before generic:
except IntegrityError:beforeexcept Exception: - Re-raise specific errors or return default/None on fallback paths
- Example from
sentiment_analyzer/main.py:try: async with db_session_factory() as session: # persist to DB except IntegrityError: # Handle duplicate pass except Exception: logger.exception("Error processing article: %s", data.get("title", "<unknown>")) - Async functions use
try/exceptaroundawaitcalls - Use
asyncio.TimeoutErrorfor timeout handling
TypeScript/React Error Handling:
- Catch errors from API calls and set error state:
err?.response?.data?.detail || err?.message - Always provide fallback messages:
'Registration failed'as default - Log error context:
throw errafter setting error state to propagate to caller if needed - Example from
useAuth.ts:catch (err: any) { const message = err?.response?.data?.detail || err?.message || 'Registration failed'; setError(message); throw err; }
Logging
Framework: Python uses logging module; no structured logging library
Patterns:
- Create logger at module level:
logger = logging.getLogger(__name__) - Use appropriate levels:
logger.debug()— Detailed debug info (e.g., "Published to stream:xxx")logger.info()— General informational (e.g., "Created consumer group…")logger.warning()— Warning conditions (e.g., "Order rejected by broker")logger.exception()— Log exceptions with stack trace (always use at exception point)
- Always include context in log messages using
%formatting:logger.info("Message with %s", variable) - Do NOT use f-strings in logging (allows lazy evaluation)
- Log level configuration:
log_levelinBaseConfig(default: "INFO")
Examples from codebase:
logger.debug("Published to %s: %s", self.stream, msg_id)
logger.info("Created consumer group %s on %s", self.group, self.stream)
logger.exception("Ollama analysis failed") # Includes stack trace
logger.warning("Order rejected by Alpaca: %s", exc)
Comments
When to Comment:
- Complex business logic that isn't self-documenting (e.g., weight adjustment formulas, P&L calculations)
- Non-obvious design decisions (e.g., "SADD returns 1 if the member was added (i.e. not already present)")
- Caveats or workarounds (e.g., "BUSYGROUP means group already exists — expected on subsequent starts.")
- Public APIs and exported functions should have docstrings
When NOT to Comment:
- Self-explanatory code (variable names, clear function logic)
- Redundant comments that repeat the code
- Outdated comments (remove or update)
Docstring/Comments:
- Module docstrings: Present at top of file, describe purpose
"""Thin wrappers around redis-py Streams for publish/consume with JSON serialization.""" - Class docstrings: Describe the class purpose and key behavior
class StreamPublisher: """Publishes JSON-encoded messages to a Redis Stream.""" - Method docstrings: Use Google/NumPy style for async methods
async def ensure_group(self) -> None: """Create the consumer group if it does not already exist.""" - Parameters/Returns documented in docstrings for public APIs:
"""Score a single article and publish one ScoredArticle per extracted ticker. Parameters ---------- article: The raw article consumed from the ``news:raw`` stream. """
Inline Comments:
- Prefix with
#and space:# This explains why… - Place above the code line (not at end of line unless very brief)
- Example:
# SADD returns 1 if the member was added (i.e. not already present)
Function Design
Size Guidelines:
- Functions should be focused and single-responsibility
- Async functions in services are typically 5-30 lines (after docstring)
- Helper functions (prefixed with
_) are 3-20 lines - Example:
_deduplicate_and_publishis ~20 lines, handles one concern
Parameters:
- Use keyword arguments for clarity in calls:
await publish(data=article, index=stream) - Type hints are required for all parameters and returns
- Default parameters allowed (e.g.,
batch_size: int = 10) - For configuration, pass config objects rather than many flags:
config: MyConfignotthreshold=0.5, timeout=10
Return Values:
- Use union types for optional/multiple returns:
TradeSignal | None(Python 3.10+ syntax) - Return early for error cases (guard clauses):
if not tickers: logger.debug("No tickers found") return - For async functions, return types should use
Awaitableor direct annotation:async def x() -> PositionInfo:
Module Design
Exports:
- Use
__all__in modules that re-export from submodules - Example from
shared/models/__init__.py:__all__ = [ "Base", "TimestampMixin", # Trading "Strategy", "Signal", # ... ] - Imports are grouped by category (Trading, News, Learning, Auth, Timeseries) with comments
Barrel Files:
shared/models/__init__.pyacts as a barrel, importing all models so Alembic can discover them- Comment each import group for clarity
- Do NOT use wildcard imports (
from x import *)
Config Classes:
- All config classes extend
shared.config.BaseConfig(Pydantic BaseSettings) - Use
model_config = {"env_prefix": "TRADING_"}for environment variable loading - Example:
services/news_fetcher/config.pyextendsBaseConfig
Service Entry Points:
- Located at
services/{service_name}/main.py - Contains
async def main()or async generator functions - Modules imported from shared libraries and service-specific modules
- Signal handling for graceful shutdown
Special Conventions
Redis Stream Constants:
- Define at module level as uppercase strings
- Example:
NEWS_RAW_STREAM = "news:raw",SEEN_HASHES_KEY = "news:seen_hashes"
Pydantic Models:
- Use
model_dump(mode="json")when serializing to JSON (v2 syntax) - Set
model_config = {"from_attributes": True}for ORM mapping - Enum fields use value comparison:
TradeSide.BUY == "BUY"
SQLAlchemy (2.0+):
- Use async sessions:
async_sessionmakerwith async context managers - Use
mapped_columnstyle (notColumn) - Type hints required on model fields
- Example: models in
shared/models/trading.py
Test Naming:
- Test functions:
test_{component}_{scenario}(e.g.,test_publisher_publishes_json) - Test classes:
Test{ComponentName}(e.g.,TestEvaluateProfitableTrade) - Helper functions in tests: prefix with
_make_(e.g.,_make_config,_make_signal)
Convention analysis: 2026-02-23