# AGENTS.md - AI Coding Agent Guidelines for TradeFinder > Automated crypto trading system for BTC/ETH perpetual futures on Binance USDM. ## Quick Reference | Task | Command | |------|---------| | Install | `pip install -e ".[dev]"` or `uv pip install -e ".[dev]"` | | Run all tests | `pytest` | | Run single test | `pytest tests/test_config.py::TestOrderRequest::test_valid_limit_order -v` | | Run test file | `pytest tests/test_config.py -v` | | Lint | `ruff check .` | | Lint fix | `ruff check . --fix` | | Format | `ruff format .` | | Type check | `mypy src/` | | Full check | `ruff check . && ruff format --check . && mypy src/ && pytest` | --- ## Build & Environment - **Python**: 3.12+ required (3.12, 3.13 supported) - pandas-ta requires 3.12+ - **Build system**: `hatchling` with `pyproject.toml` - **Prefer `uv`** for faster installs: `uv pip install -e ".[dev]"` ```bash # Setup uv venv .venv --python 3.12 && source .venv/bin/activate uv pip install -e ".[dev]" ``` --- ## Testing (pytest + pytest-asyncio) ```bash pytest # All tests with coverage pytest tests/test_config.py -v # Single file pytest tests/test_config.py::TestOrderRequest::test_valid_limit_order -v # Single test pytest -k "test_valid" -v # By keyword ``` - `asyncio_mode = "auto"` - no `@pytest.mark.asyncio` needed - Test files: `tests/test_*.py` - Type hints required: `def test_xxx(self) -> None:` --- ## Linting & Type Checking **Ruff** (line-length: 100, target: py311): - Rules: `E`, `W`, `F`, `I`, `B`, `C4`, `UP` (pycodestyle, pyflakes, isort, bugbear, comprehensions, pyupgrade) **MyPy**: `strict = true`, uses `pydantic.mypy` plugin --- ## Code Style Guidelines ### Imports (isort-ordered) ```python from abc import ABC, abstractmethod # 1. stdlib from decimal import Decimal import httpx # 2. third-party import structlog from tradefinder.adapters.types import Order # 3. first-party ``` ### Type Hints (Required - strict mypy) ```python async def get_balance(self, asset: str = "USDT") -> AccountBalance: ... # Use | for unions, built-in generics def cancel_order(self, order_id: str | None = None) -> Order: ... list[str] # Not List[str] dict[str, Any] # Not Dict[str, Any] ``` ### Numeric Values - Always Decimal ```python price = Decimal("50000.00") # String input for precision # NEVER: price = 50000.00 # Float loses precision ``` ### Data Structures ```python @dataclass class Position: symbol: str quantity: Decimal raw: dict[str, Any] = field(default_factory=dict) # Pydantic for config/validation only class Settings(BaseSettings): ... ``` ### Enums ```python class Side(str, Enum): BUY = "BUY" SELL = "SELL" params["side"] = request.side.value # Use .value for API ``` ### Logging (structlog) ```python logger = structlog.get_logger(__name__) logger.info("Order created", order_id=order.id, symbol=symbol) # NEVER log secrets: logger.info(f"API Key: {api_key}") ``` ### Async Patterns ```python async def connect(self) -> None: self._client = httpx.AsyncClient(timeout=30.0) async def disconnect(self) -> None: if self._client: await self._client.aclose() self._client = None ``` ### Error Handling ```python class ExchangeError(Exception): pass class AuthenticationError(ExchangeError): pass try: await self._request(...) except httpx.RequestError as e: raise ExchangeError(f"Request failed: {e}") from e ``` ### Naming Conventions | Type | Convention | Example | |------|------------|---------| | Modules | snake_case | `binance_usdm.py` | | Classes | PascalCase | `BinanceUSDMAdapter` | | Functions/Variables | snake_case | `get_open_orders` | | Constants | UPPER_SNAKE | `MAX_LEVERAGE` | | Private | leading underscore | `_client`, `_sign()` | --- ## Project Structure ``` src/tradefinder/ adapters/ # Exchange connectivity (base.py, binance_usdm.py, types.py) core/ # Core engine (config.py) data/ # Market data (TODO) strategies/ # Trading strategies (TODO) ui/ # Streamlit dashboard (TODO) tests/ test_*.py # Test files ``` --- ## Security Rules (CRITICAL) 1. **NEVER commit `.env` or secrets** - `.gitignore` enforces this 2. **NEVER log API keys** - mask sensitive data 3. **Use `SecretStr`** for credentials in Pydantic settings 4. **Use `Decimal`** for all financial values - never `float` 5. **Validate trading mode** before any exchange operations --- ## Common Patterns ```python # Configuration from tradefinder.core.config import get_settings settings = get_settings() # Creating orders request = OrderRequest( symbol="BTCUSDT", side=Side.BUY, position_side=PositionSide.LONG, order_type=OrderType.LIMIT, quantity=Decimal("0.001"), price=Decimal("50000"), ) request.validate() # Exchange adapter adapter = BinanceUSDMAdapter(settings) await adapter.connect() await adapter.configure_hedge_mode(True) await adapter.configure_margin_type("BTCUSDT", MarginType.ISOLATED) await adapter.disconnect() ```