Metadata-Version: 2.4
Name: trade-sandbox
Version: 0.1.3
Summary: A unified sandbox for testing trading algorithms against historical and live market data.
Author: Arnav Agarwal
License: MIT
Project-URL: Homepage, https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox
Project-URL: Repository, https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox
Project-URL: Documentation, https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/blob/main/API_GUIDE.md
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic>=2.0
Requires-Dist: pandas>=2.0
Requires-Dist: polars>=0.20
Requires-Dist: pyarrow>=14.0
Requires-Dist: matplotlib>=3.5
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Dynamic: license-file

# trade-sandbox

<p align="center">
  <img src="https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/raw/main/assets/trade-sandbox.png" alt="trade-sandbox logo" width="500">
</p>

<br/>

*A unified, mode-agnostic execution sandbox for testing and running trading strategies.*

[![PyPI version](https://img.shields.io/pypi/v/trade-sandbox.svg)](https://pypi.org/project/trade-sandbox/)
[![Python Support](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg)](https://pypi.org/project/trade-sandbox/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)](https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/actions)

`trade-sandbox` is a high-performance Python library designed to bridge the gap between backtesting and live trading. Write your trading strategies once against a single interface, and run them seamlessly across historical backtests, live paper-trading feeds, or warm-start transitions—without modifying a single line of strategy code.

---

## Key Features

*   **Mode-Agnostic Strategy API**: The same strategy runs identically in a backtest (`HistoricalWindow`), paper trading (`LiveWindow`), or a sequential warm-start handoff (`HistoricalWindow + LiveWindow`).
*   **Arbitrary Precision Money Math**: Enforces Python's `Decimal` type across all transactions, cash pools, fees, and positions to prevent floating-point representation bugs.
*   **Execution Quality Realism**: Configurable knobs for execution latency queueing, tick-to-fill slippage (absolute/percentage spreads), and composable transaction fees (flat + percentage).
*   **Multi-Currency Portfolio & FX**: Built-in support for trading across assets in different currencies with automatic FX rate conversion and base-currency portfolio valuation.
*   **Pre-Trade & Post-Trade Risk Guardrails**: Composable risk modules to enforce drawdown limit halts, gross leverage thresholds, and position concentration caps.
*   **FIFO Trade Matching**: Analyzes raw fill logs using a First-In, First-Out queueing pipeline to reconstruct realized trades and compute exact performance metrics (Sharpe, Sortino, Calmar, Profit Factor).

---

## Architecture

`trade-sandbox` separates the time clock, order matching, and account state from the strategy logic. This ensures that strategies are isolated from their execution context.

```mermaid
graph TD
    Data[Data Ingestion / Ticks] -->|Chronological Feed| Session[Session Harness]
    Session -->|Market Price Updates| Portfolio[Portfolio Manager]
    Session -->|on_tick Callback| Strategy[User Strategy]
    
    Strategy -->|submit_order| Broker[Paper Broker]
    Broker -->|Pre-Trade Checks| Risk[Risk Engine]
    Risk -->|Check Order & Portfolio| Engine[Matching Engine]
    
    Engine -->|Queue Latency Delay| Book[Simulated Order Book]
    Book -->|Fills / Slippage / Fees| Broker
    Broker -->|apply_fill| Portfolio
    Broker -->|on_order_filled| Strategy

    style Data fill:#f5f7f8,stroke:#cfd8dc,stroke-width:1px,color:#37474f
    style Strategy fill:#e8f4fd,stroke:#b3e5fc,stroke-width:1px,color:#0277bd
    style Portfolio fill:#eef7ee,stroke:#c8e6c9,stroke-width:1px,color:#2e7d32
    style Engine fill:#fffbee,stroke:#ffe082,stroke-width:1px,color:#f57f17
    style Risk fill:#fdf2f2,stroke:#ffcdd2,stroke-width:1px,color:#c62828
```

During a **Warm Start** transition, the session context shifts from simulated time (historical replay) to real wall-clock time (live execution) without resetting strategy memory:

```mermaid
sequenceDiagram
    autonumber
    participant Hist as Historical Feed (Simulated Clock)
    participant Live as Live Feed (Wall Clock)
    participant Strategy as Strategy API
    participant Engine as Matching Engine

    Note over Hist,Strategy: Phase 1: Warm Start / Historical Replay
    rect rgba(30, 144, 255, 0.05)
        Hist->>Strategy: Feed Historical Ticks (UTC)
        Strategy->>Engine: Submit Orders
        Engine->>Strategy: Return Simulated Fills
    end

    Note over Live,Strategy: Phase 2: Live Transition (Seamless handoff)
    rect rgba(46, 139, 87, 0.05)
        Live->>Strategy: Stream Real-Time Ticks (UTC)
        Strategy->>Engine: Submit Orders
        Engine->>Strategy: Return Live Fills
    end
```

---

## Installation

Install `trade-sandbox` from PyPI:

```bash
pip install trade-sandbox
```

Or install from source in editable mode:

```bash
git clone https://github.com/arnav-agarwal/trade-sandbox.git
cd trade-sandbox
pip install -e .
```

To install development dependencies (for running tests):

```bash
pip install -e .[dev]
```

---

## Quick Start Example

This example defines a simple Exponential Moving Average (EMA) crossover strategy, runs it against a simulated historical window, and checks performance metrics.

```python
import datetime
from decimal import Decimal
from trade_sandbox import Session, HistoricalWindow, Strategy, Tick, ConstantSlippageModel

class EMACrossover(Strategy):
    def __init__(self, fast: int = 3, slow: int = 8):
        super().__init__()
        self.fast_span = fast
        self.slow_span = slow
        self.prices = []

    def on_tick(self, tick: Tick):
        self.prices.append(tick.last)
        if len(self.prices) < self.slow_span:
            return

        # Simple moving average calculations
        fast_ma = sum(self.prices[-self.fast_span:]) / self.fast_span
        slow_ma = sum(self.prices[-self.slow_span:]) / self.slow_span
        
        # Check current position size
        current_pos = self.portfolio.positions.get(tick.symbol)
        position_size = current_pos.size if current_pos else Decimal("0")

        if fast_ma > slow_ma and position_size <= 0:
            # Buy signal: go long
            qty = Decimal("1")
            if position_size < 0:
                qty += abs(position_size)
            self.submit_order(tick.symbol, "BUY", "MARKET", qty)
            print(f"[{tick.timestamp}] BUY Order Submitted @ {tick.last}")

        elif fast_ma < slow_ma and position_size >= 0:
            # Sell signal: go short
            qty = Decimal("1")
            if position_size > 0:
                qty += position_size
            self.submit_order(tick.symbol, "SELL", "MARKET", qty)
            print(f"[{tick.timestamp}] SELL Order Submitted @ {tick.last}")

    def on_order_filled(self, fill):
        print(f"  -> Filled: {fill.size} units @ {fill.price:.2f} (Fee: {fill.fee:.2f})")

# 1. Define the historical timeframe
window = HistoricalWindow(
    start="2026-08-01T09:30:00Z",
    end="2026-08-01T10:30:00Z",
    resolution="1m"
)

# 2. Setup the strategy and backtesting harness
strategy = EMACrossover()
session = Session(
    strategy=strategy,
    market="BINANCE:BTCUSDT",
    window=window,
    initial_cash=Decimal("50000.00"),
    base_currency="USDT",
    slippage_model=ConstantSlippageModel(percent=Decimal("0.0005"))  # 0.05% slippage
)

# 3. Create mock historical ticks
start_dt = datetime.datetime(2026, 8, 1, 9, 30, tzinfo=datetime.timezone.utc)
ticks = [
    Tick(
        timestamp=start_dt + datetime.timedelta(minutes=i),
        symbol="BINANCE:BTCUSDT", exchange="BINANCE", currency="USDT",
        bid=Decimal("60000.00") + (i * 10),
        ask=Decimal("60010.00") + (i * 10),
        last=Decimal("60005.00") + (i * 10),
        bid_size=Decimal("5.0"), ask_size=Decimal("5.0"), volume=Decimal("100")
    )
    for i in range(30)
]

# 4. Run the session
result = session.run(ticks)

# 5. Check performance
print("\n--- Summary Statistics ---")
print(f"Final Value:    {result.equity_curve[-1].total_equity_base_ccy:.2f} USDT")
print(f"Total Return:   {result.performance_metrics['total_return']:.2%}")
print(f"Sharpe Ratio:   {result.performance_metrics['sharpe_ratio']:.2f}")
print(f"Win Rate:       {result.performance_metrics['win_rate']:.2%}")
print(f"Profit Factor:  {result.performance_metrics['profit_factor']:.2f}")
```

To run the pre-configured [demonstration script](https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/blob/main/demo.py), which evaluates 3 different strategies simultaneously using `Session.compare`, displays a performance leaderboard, and plots their equity curves:

```bash
python demo.py
```

### Multi-Strategy Leaderboard Output:
```
====================================================
                Strategy Leaderboard                
====================================================
| Rank | Strategy ID    | Total Return | Sharpe Ratio | Sortino Ratio | Max Drawdown | Fills |
|------|----------------|--------------|--------------|---------------|--------------|-------|
| 1    | EMA_Crossover  | 7.47%        | 0.24         | 0.34          | 5.61%        | 9     |
| 2    | Buy_and_Hold   | 4.89%        | 0.16         | 0.26          | 6.41%        | 1     |
| 3    | Mean_Reversion | -8.62%       | -0.44        | -0.49         | 8.62%        | 15    |
====================================================
```

### Comparative Equity Chart:
<p align="center">
  <img src="https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/raw/main/assets/strategy_comparison.png" alt="Multi-Strategy Equity Curve Comparison" width="600">
</p>

<br/>

---

## API Reference

> [!TIP]
> For a highly detailed module-by-module breakdown, parameter types, financial equations, and custom extension guides, check out the complete [API Guide](https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/blob/main/API_GUIDE.md).

### `Strategy` Lifecycle Hook Methods

When developing a strategy, you subclass `Strategy` and implement any of these event callbacks:

```python
class MyStrategy(Strategy):
    def on_session_start(self):
        """Executed when the session begins initialization."""

    def on_tick(self, tick: Tick):
        """Executed whenever a new price update tick arrives."""

    def on_order_filled(self, fill: Fill):
        """Executed whenever one of the strategy's orders has been executed."""

    def on_order_rejected(self, order: Order, reason: str):
        """Executed if an order is rejected by the Risk Engine."""

    def on_session_end(self):
        """Executed when the session has finished processing."""
```

Within these handlers, you can access strategy properties:
*   `self.portfolio`: Reference to your active [Portfolio](https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/blob/main/src/trade_sandbox/core/portfolio.py).
*   `self.broker`: Reference to the [Broker](https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/blob/main/src/trade_sandbox/core/broker.py) client.
*   `self.submit_order(symbol, side, order_type, size, ...)`: Submits an order.

### Composable Time Windows

Set up your time execution boundaries:
*   `HistoricalWindow(start: Union[str, datetime], end: Union[str, datetime], resolution: str)`
*   `LiveWindow(duration: Optional[Union[str, timedelta]], until: Optional[Union[str, datetime]])`

Combine them using the `+` operator to run a **warm start** backtest that transitions directly into live paper trading:
```python
window = HistoricalWindow(start="30d ago", end="now") + LiveWindow(duration="4h")
```

---

## Verification & Testing

We enforce code correctness through automated unit and integration tests covering the matching engine, portfolio cash revaluations, drawdown checks, order queues, and seed reproducibility:

```bash
# Run pytest test suite
$env:PYTHONPATH="src;."; python -m pytest tests/
```

---

## Roadmap

1.  **Phase 1: Core Framework [COMPLETED]**: Simulated execution queues, Decimal-safe portfolio manager, pre/post-trade risk limits, and historical replays.
2.  **Phase 2: Gym/Gymnasium Wrapper**: Reinforcement Learning (RL) wrappers built over the core simulation engine.
3.  **Phase 3: Real-time Paper Trading**: Connect the paper broker directly to live CCXT websocket feeds.
4.  **Phase 4: Multi-Calendar & Multi-Market**: Integrate timezone-aware exchange trading calendars and multi-market assets.
5.  **Phase 5: Performance Reports**: Add advanced analytics tearsheets, walk-forward testing, and Monte Carlo risk stress test suites.
6.  **Phase 6: Live Execution Gateway**: Multi-broker gateways (Binance, IBKR, regional APIs) for real capital allocation.

---

## License

This project is licensed under the terms of the [MIT License](https://github.com/ArnavAgarwal-Mr-AR/trade-sandbox/blob/main/LICENSE).
