Metadata-Version: 2.4
Name: edge-sdk
Version: 0.3.1
Summary: Python SDK for accessing Edge's agricultural commodity data including CME timeseries, USDA, CFTC, and market data
Author-email: Muiez Makkawi <muiez@try-edge.com>, Daniel Nachajon <daniel@try-edge.com>
License: MIT
Project-URL: Homepage, https://try-edge.com
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: httpx>=0.25
Requires-Dist: pandas>=2.0
Requires-Dist: numpy>=1.24
Requires-Dist: python-dateutil>=2.8
Requires-Dist: pydantic>=2.0
Requires-Dist: python-dotenv>=1.0
Requires-Dist: typing-extensions>=4.8
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: pytest-cov>=4.1; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
Requires-Dist: black>=23.0; extra == "dev"
Requires-Dist: ruff>=0.1; extra == "dev"
Requires-Dist: mypy>=1.5; extra == "dev"
Provides-Extra: polars
Requires-Dist: polars>=0.20; extra == "polars"
Provides-Extra: async
Requires-Dist: httpx[http2]; extra == "async"

# Edge SDK

Python SDK for accessing Edge's agricultural commodity data API. Get real-time and historical CME futures data, USDA market reports, and more.

## Installation

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

## Quick Start

```python
from edge import Edge

# Initialize the client with your API key
edge = Edge(api_key="your-api-key")

# Get CME futures data
corn_data = edge.cme.get_futures_prices(
    symbol="ZCH5",
    start_date="2025-01-01",
    end_date="2025-01-31"
)

print(corn_data.head())
```

## Authentication

**API Key Required**: All API endpoints require a valid API key. Get your API key from the Edge platform.

### Configuration Options

**Option 1: Pass API key directly**
```python
from edge import Edge

edge = Edge(api_key="edge_your_api_key_here")
```

**Option 2: Use environment variables**
```bash
export EDGE_API_KEY="edge_your_api_key_here"
```

```python
from edge import Edge

edge = Edge()  # Automatically uses EDGE_API_KEY from environment
```

**Option 3: Use .env file**
```bash
# .env file
EDGE_API_KEY=edge_your_api_key_here
```

```python
from edge import Edge

edge = Edge()  # Automatically loads from .env
```

## API Reference

### Core Client Methods

#### `health_check()`
Check API health status.

```python
status = edge.health_check()
print(status)  # {'status': 'healthy', 'timestamp': '...'}
```

#### `list_sources()`
List all available data sources.

```python
sources = edge.list_sources()
print(sources)
```

#### `list_commodities(category=None, is_active=True)`
List available commodities.

```python
# All commodities
all_commodities = edge.list_commodities()

# Filter by category
livestock = edge.list_commodities(category="LIVESTOCK")
```

#### `list_regions(region_type=None, country_code=None)`
List geographic regions.

```python
# All regions
regions = edge.list_regions()

# Filter by type
countries = edge.list_regions(region_type="COUNTRY")

# Filter by country
us_states = edge.list_regions(country_code="US")
```

#### `list_themes()`
List time series themes.

```python
themes = edge.list_themes()
```

#### `search_series(search_term, source_name=None, limit=50)`
Search for time series by keyword.

```python
# Search all sources
results = edge.search_series("corn")

# Search specific source
usda_corn = edge.search_series("corn", source_name="USDA", limit=10)
```

#### `get_series(series_code, source_name="USDA", start_date=None, end_date=None, limit=None)`
Get time series data (convenience method).

```python
data = edge.get_series(
    series_code="SOME_SERIES_CODE",
    source_name="USDA",
    start_date="2024-01-01",
    end_date="2024-12-31"
)
```

### CME Service (`edge.cme`)

Access CME futures and calendar spread data.

#### `list_symbols(curve, start_date=None, end_date=None)`
List all available CME symbols for a specific commodity curve. Returns live contract listings including individual contracts, calendar spreads, and continuous contracts.

**Parameters:**
- `curve` (str): The CME curve/root symbol (e.g., "ZC", "HE", "LE")
- `start_date` (str | date | datetime, optional): Start date for historical symbol resolution
- `end_date` (str | date | datetime, optional): End date for historical symbol resolution

**Date Range Behavior:**
- Without dates: Returns currently tradeable symbols
- With dates: Returns all symbols that were tradeable during the specified period (including expired contracts)
- Future dates are automatically handled - uses latest available data (yesterday)

```python
# Get currently tradeable Corn futures contracts
corn_symbols = edge.cme.list_symbols(curve="ZC")
print(corn_symbols)
# ['ZC.c.0', 'ZC.c.1', 'ZCH6', 'ZCK6', 'ZCN6', 'ZCU6', 'ZCZ6',
#  'ZCH6-ZCK6', 'ZCH6-ZCN6', ...]

# Get symbols that were tradeable during Q1 2024 (historical)
historical_corn = edge.cme.list_symbols(
    curve="ZC",
    start_date="2024-01-01",
    end_date="2024-03-31"
)
# Returns: ['ZC.c.0', 'ZC.c.1', 'ZCH4', 'ZCK4', 'ZCN4', ...]
# Includes 2024 contracts that have since expired

# Get symbols from last 30 days (auto-handles today's date)
from datetime import date, timedelta
recent_hogs = edge.cme.list_symbols(
    curve="HE",
    start_date=(date.today() - timedelta(days=30)).isoformat(),
    end_date=date.today().isoformat()  # Automatically uses yesterday
)

# Get Live Cattle contracts
cattle_symbols = edge.cme.list_symbols(curve="LE")
```

#### `get_futures_prices(symbol, start_date=None, end_date=None, limit=None)`
Get OHLCV data for a specific futures contract. No default limit - returns all available historical data.

```python
# Get corn December 2025 futures
corn_data = edge.cme.get_futures_prices(
    symbol="ZCZ5",
    start_date="2025-01-01",
    end_date="2025-01-31"
)

# Get continuous front-month contract (recommended for long-term analysis)
front_month = edge.cme.get_futures_prices(
    symbol="ZC.c.0",  # Front month continuous
    start_date="2020-01-01"
)

# Get 3 years of historical data
three_years = edge.cme.get_futures_prices(
    symbol="ZC.c.0",
    start_date="2022-01-01",
    end_date="2025-01-01"
)

# Returns DataFrame with columns:
# observation_datetime, open, high, low, close, volume,
# open_interest, settlement_price, bid_price, ask_price, etc.
```

**Symbol Types:**
- **Individual contracts**: `ZCH5` (Corn March 2025), `HEG5` (Lean Hogs February 2025)
- **Calendar spreads**: `ZCH5-ZCK5`, `HEG5-HEJ5`
- **Continuous contracts**: `ZC.c.0` (front month), `ZC.c.1` (second month), up to `ZC.c.5`

#### `get_spread_data(spread_symbol, start_date=None, end_date=None, limit=None)`
Get calendar spread data between two contract months.

```python
# Get Dec 2025 - Mar 2026 corn spread
spread = edge.cme.get_spread_data(
    spread_symbol="ZCZ5-ZCH6",
    start_date="2025-01-01",
    end_date="2025-01-31"
)

# Returns DataFrame with OHLCV for the spread
```

**Supported CME Curves:**
- **Grains**: ZC (Corn), ZS (Soybeans), ZW (Wheat), ZL (Soybean Oil), ZM (Soybean Meal)
- **Livestock**: LE (Live Cattle), GF (Feeder Cattle), HE (Lean Hogs)
- **Energy**: CL (Crude Oil), NG (Natural Gas), RB (Gasoline), HO (Heating Oil)
- **Softs**: SB (Sugar), KC (Coffee), CT (Cotton), CC (Cocoa)
- **Metals**: GC (Gold), SI (Silver), HG (Copper)
- **Financials**: ES (S&P 500), NQ (Nasdaq), YM (Dow)

### Time Series Service (`edge.time_series`)

Generic time series data access.

#### `get_data(series_code, source_name, start_date=None, end_date=None, limit=None)`
Get time series data for any source.

```python
data = edge.time_series.get_data(
    series_code="BEEF_CUTOUT_SERIES",
    source_name="USDA",
    start_date="2024-01-01"
)
```

#### `get_metadata(series_code=None, source_name=None, theme_name=None, commodity_code=None, region_code=None, limit=50)`
Get metadata about available time series.

```python
# Get all USDA series metadata
usda_meta = edge.time_series.get_metadata(source_name="USDA", limit=100)

# Filter by commodity
corn_series = edge.time_series.get_metadata(commodity_code="CORN")
```

#### `search(search_term, source_name=None, limit=50)`
Search time series by description or code.

```python
results = edge.time_series.search("beef prices", limit=20)
```

#### `get_multiple(series_configs, start_date=None, end_date=None, limit=None)`
Get multiple time series in one request.

```python
configs = [
    {"series_code": "ZCH5", "source_name": "CME"},
    {"series_code": "ZCN5", "source_name": "CME"},
]
data = edge.time_series.get_multiple(
    series_configs=configs,
    start_date="2025-01-01"
)
```

## Usage Examples

### Example 1: Listing Available Contracts

```python
from edge import Edge

# Initialize client
edge = Edge(api_key="your-api-key")

# List currently tradeable Corn futures contracts
corn_symbols = edge.cme.list_symbols(curve="ZC")
print(f"Found {len(corn_symbols)} Corn contracts")

# Separate individual contracts from spreads and continuous
raw_contracts = [s for s in corn_symbols if '.' not in s]
continuous = [s for s in corn_symbols if '.' in s]
individual = [s for s in raw_contracts if '-' not in s]
spreads = [s for s in raw_contracts if '-' in s]

print(f"Individual contracts: {individual}")
print(f"Calendar spreads: {spreads[:5]}")  # First 5 spreads
print(f"Continuous contracts: {continuous}")

# List historical contracts from Q1 2024
historical_symbols = edge.cme.list_symbols(
    curve="ZC",
    start_date="2024-01-01",
    end_date="2024-03-31"
)
print(f"\nQ1 2024 contracts: {[s for s in historical_symbols if '.' not in s and '-' not in s]}")

edge.close()
```

### Example 2: Getting Historical Data

```python
from edge import Edge
from datetime import datetime, timedelta

edge = Edge(api_key="your-api-key")

# Get 3 years of continuous front-month data
end_date = datetime.now()
start_date = end_date - timedelta(days=3*365)

corn_data = edge.cme.get_futures_prices(
    symbol="ZC.c.0",  # Front month continuous
    start_date=start_date.strftime("%Y-%m-%d"),
    end_date=end_date.strftime("%Y-%m-%d")
)

print(f"Retrieved {len(corn_data)} records")
print(f"Date range: {corn_data['observation_datetime'].min()} to {corn_data['observation_datetime'].max()}")
print(f"Price range: ${corn_data['close'].min():.2f} - ${corn_data['close'].max():.2f}")

# Calculate statistics
print(f"Average Close: ${corn_data['close'].mean():.2f}")
print(f"High: ${corn_data['high'].max():.2f}")
print(f"Low: ${corn_data['low'].min():.2f}")

edge.close()
```

### Example 3: Multiple Commodities Analysis

```python
from edge import Edge
import pandas as pd

edge = Edge(api_key="your-api-key")

commodities = {
    "ZC": "Corn",
    "ZS": "Soybeans",
    "HE": "Lean Hogs",
    "LE": "Live Cattle",
    "CL": "Crude Oil"
}

# Fetch data for all commodities
all_data = {}
for symbol, name in commodities.items():
    print(f"Fetching {name} ({symbol})...")

    df = edge.cme.get_futures_prices(
        symbol=f"{symbol}.c.0",  # Front month continuous
        start_date="2024-01-01"
    )

    if not df.empty:
        all_data[symbol] = df
        print(f"  ✓ Retrieved {len(df)} records")

# Create combined price matrix
price_data = {}
for symbol, df in all_data.items():
    df_clean = df[['observation_datetime', 'close']].copy()
    df_clean['observation_datetime'] = pd.to_datetime(df_clean['observation_datetime'])
    df_clean = df_clean.set_index('observation_datetime')
    df_clean.columns = [symbol]
    price_data[symbol] = df_clean

combined_df = pd.concat(price_data.values(), axis=1)
combined_df = combined_df.sort_index()

# Calculate correlation matrix
print("\nCorrelation Matrix:")
print(combined_df.corr().round(2))

# Save to CSV
combined_df.to_csv("combined_futures_prices.csv")

edge.close()
```

### Example 4: Spread Analysis

```python
from edge import Edge

edge = Edge(api_key="your-api-key")

# Get calendar spread data
spread = edge.cme.get_spread_data(
    spread_symbol="ZCZ5-ZCH6",
    start_date="2025-01-01"
)

print(f"Current Spread: ${spread.iloc[-1]['close']:.2f}")
print(f"Average Spread: ${spread['close'].mean():.2f}")
print(f"Spread Range: ${spread['close'].min():.2f} to ${spread['close'].max():.2f}")

edge.close()
```

### Working with DataFrames

All data is returned as pandas DataFrames for easy analysis:

```python
import matplotlib.pyplot as plt

# Get CME data
data = edge.cme.get_futures_prices("ZCH5", start_date="2025-01-01")

# Plot OHLC
data.plot(x="observation_datetime", y=["open", "high", "low", "close"], figsize=(12, 6))
plt.title("Corn Futures - March 2025")
plt.show()

# Calculate statistics
print(data[["open", "high", "low", "close"]].describe())

# Resample to weekly
weekly = data.set_index("observation_datetime")["close"].resample("W").mean()
```

### Error Handling

```python
from edge import Edge
from edge.exceptions import EdgeAPIError, DataNotFoundError, AuthenticationError

try:
    edge = Edge(api_key="your-api-key")
    data = edge.cme.get_futures_prices("INVALID_SYMBOL")
except AuthenticationError as e:
    print(f"Authentication failed: {e}")
except DataNotFoundError as e:
    print(f"Data not found: {e}")
except EdgeAPIError as e:
    print(f"API error: {e}")
```

### Context Manager

Use the client as a context manager for automatic cleanup:

```python
with Edge(api_key="your-key") as edge:
    data = edge.cme.get_futures_prices("ZCH5")
    print(data.head())
    # Client automatically closes when done
```

### Advanced Configuration

```python
# Custom timeout for large data requests
edge = Edge(
    api_key="your-api-key",
    timeout=120  # 120 second timeout for large historical datasets
)

# Get limited results for testing
data = edge.cme.get_futures_prices(
    symbol="ZCH5",
    limit=100  # Only get 100 most recent records
)
```

## Data Returned

### CME Futures Data

CME futures data includes the following fields:

| Field | Type | Description |
|-------|------|-------------|
| `observation_datetime` | datetime | Timestamp of the observation |
| `open` | float | Opening price |
| `high` | float | High price |
| `low` | float | Low price |
| `close` | float | Closing price |
| `volume` | float | Trading volume |
| `open_interest` | float | Open interest |
| `settlement_price` | float | Settlement price |
| `bid_price` | float | Bid price |
| `ask_price` | float | Ask price |
| `bid_size` | float | Bid size |
| `ask_size` | float | Ask size |

### Time Series Data

Generic time series data includes:

| Field | Type | Description |
|-------|------|-------------|
| `observation_date` | date | Date of observation |
| `value` | float | Primary value |
| `value_high` | float | High value (if applicable) |
| `value_low` | float | Low value (if applicable) |
| `volume` | float | Volume (if applicable) |
| `quality_flag` | string | Data quality indicator |

## Exception Types

The SDK provides specific exception types for better error handling:

- **`EdgeAPIError`**: Base exception for all API errors
- **`AuthenticationError`**: Raised when API key is invalid or missing
- **`DataNotFoundError`**: Raised when requested data is not found (404)

```python
from edge.exceptions import EdgeAPIError, AuthenticationError, DataNotFoundError
```

## Requirements

- Python 3.9+
- pandas>=2.0
- httpx>=0.25
- python-dotenv>=1.0

## Support

For issues or questions, please contact muiez@try-edge.com

## License

MIT License - see LICENSE file for details.
