# Edge SDK

Python SDK for Edge's commodity market data platform. Access historical daily bars, real-time quotes, intraday OHLC, options data, and symbology.

## Installation

```bash
pip install edge-sdk
```

## Quick Start

```python
from edge import Edge

edge = Edge(api_key="your-key")
# Or: export EDGE_API_KEY=your-key

# Historical daily bars — bare roots expand to front-month
df = edge.history("ZC", start="1Y")             # ZC → ZC*1, last 1 year
df = edge.history("ZCZ26", start="2025-01-01")  # explicit contract — no expansion

# Multiple symbols
df = edge.history(["ZC", "ZS", "LE"], start="2025-01-01")

# Price matrix (date × symbol), ready for analysis
matrix = edge.prices(["ZC", "ZS", "LE", "CL"], start="1Y")

# Quick last price
edge.last("ZC")                    # → 453.25 (float)
edge.last(["ZC", "ZS", "LE"])     # → Series

# Real-time quotes
df = edge.quote(["ZC", "ZS"])

# Search
df = edge.search("soybeans")

# Returns & correlation
ret  = edge.returns(["ZC", "ZS", "LE", "CL"], start="1Y")
corr = edge.correlation(["ZC", "ZS", "LE", "CL"], start="3Y")
```

## Symbols

The convenience methods (`history`, `quote`, `prices`, `last`, `returns`, `correlation`) auto-expand **bare root symbols** to front-month continuous:

| You pass | Sent to API | Why |
|----------|-------------|-----|
| `"ZC"` | `ZC*1` | Bare root → front-month expansion |
| `"ZC*1"` | `ZC*1` | Already qualified — no change |
| `"ZC*2"` | `ZC*2` | Second month — no change |
| `"ZCZ26"` | `ZCZ26` | Specific contract — no change |
| `"ZCZ26\|500C"` | `ZCZ26\|500C` | Option symbol — no change |

To **skip expansion entirely**, use the service layer directly:

```python
# These pass symbols through raw — no expansion, no exchange inference
df = edge.futures.historical_bars("ZC", exchange="CBOT", start_date="2025-01-01")
df = edge.futures.latest_quotes("ZC*1", exchange="CME")
```

Exchange is auto-inferred from the commodity registry in convenience methods (ZC→CBOT, CL→NYMEX, etc.). You can always override it: `edge.history("ZC", start="1Y", exchange="CBOT")`.

## Relative Dates

Convenience methods accept shorthand strings for `start` and `end`:

| Shorthand | Meaning |
|-----------|---------|
| `"1Y"` | 1 year ago |
| `"6M"` | 6 months ago |
| `"3M"` | 3 months ago |
| `"1M"` | 1 month ago |
| `"2W"` | 2 weeks ago |
| `"30D"` | 30 days ago |
| `"YTD"` | January 1st of this year |
| `"MTD"` | 1st of this month |
| `"QTD"` | 1st of this quarter |

ISO date strings (`"2025-01-01"`), `date`, and `datetime` objects are also accepted.

## Authentication

The SDK sends an API key via `X-API-Key` header to the market-data-service.

| Method | Example |
|--------|---------|
| Constructor | `Edge(api_key="your-key")` |
| Env var | `export EDGE_API_KEY=your-key` then `Edge()` |

## Supported Commodities

| Sector | Symbols | Exchange |
|--------|---------|----------|
| Grains | ZC (Corn), ZS (Soybeans), ZW (Wheat), ZL (Soy Oil), ZM (Soy Meal) | CBOT |
| Livestock | LE (Live Cattle), GF (Feeder Cattle), HE (Lean Hogs) | CME |
| Energy | CL (Crude Oil), NG (Nat Gas), RB (Gasoline), HO (Heating Oil) | NYMEX |
| Dairy | DL (Class III Milk), DK (Class IV), BJ (Cheese), BD (Butter), DF (NFDM), DG (Whey) | CME |

All covered by the default `exchange="CME"` (backend expands CME → CME/CBOT/NYMEX/COMEX).

## API Reference

### Convenience Methods (Edge)

```python
edge.history(symbols, start, end, exchange=None, max_records=None)  # daily OHLCV
edge.quote(symbols, exchange=None)                                   # latest prices
edge.prices(symbols, start, end, field="close")                      # pivoted matrix
edge.last(symbols)                                                   # scalar or Series
edge.returns(symbols, start, end)                                    # daily % returns
edge.correlation(symbols, start, end)                                # correlation matrix
edge.search(query, limit=20)                                         # symbol search
edge.health_check()                                                  # service health
```

### Futures Service (`edge.futures`)

No symbol expansion — passes symbols through raw. Requires explicit `exchange`.

```python
edge.futures.historical_bars(symbols, exchange, start_date, end_date, max_records)
edge.futures.latest_quotes(symbols, exchange, start, end)
edge.futures.intraday_bars(symbols, exchange, interval, start, end)  # 1m/5m/15m/1h/4h
edge.futures.search_symbols(query, exchange, limit)
edge.futures.search_options(query, exchange, limit)
edge.futures.option_eod(symbol, max_records, order, volume)
edge.futures.events(symbols, exchange, since, lookback_seconds, msg_types, limit)
```

### Symbology Service (`edge.symbology`)

```python
edge.symbology.resolve(symbol)           # → dict (canonical instrument)
edge.symbology.translate(symbol, to_provider)
edge.symbology.search(query, limit)      # → DataFrame
edge.symbology.validate(symbols)         # → DataFrame
```

## Error Handling

```python
from edge.exceptions import (
    EdgeAPIError,             # base
    AuthenticationError,      # 401
    DataNotFoundError,        # 404
    ValidationError,          # 400/422
    ServiceUnavailableError,  # 503
    ConfigurationError,
)

try:
    df = edge.history("ZC", start="1Y")
except AuthenticationError:
    print("Invalid API key")
except ServiceUnavailableError:
    print("Market data service is down")
```

## Configuration

| Env var | Default | Description |
|---------|---------|-------------|
| `EDGE_BASE_URL` | Edge hosted service | Override to point at a different instance |
| `EDGE_API_KEY` | (empty) | API key for production |

```python
edge = Edge(
    base_url="https://market-data.try-edge.com",
    api_key="edge_abc123",
    timeout=120,  # seconds (default 120)
)
```

## Requirements

- Python >= 3.10
- httpx >= 0.25
- pandas >= 2.0
