ritpy 0.1.2
Python wrapper for the Rotman Interactive Trader (RIT) REST API
Requires Python: >=3.10
Keywords: algorithmic-trading, api, finance, rit, rotman, trading

# RIT API Client

[![Python Version](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)

A comprehensive Python wrapper for the Rotman Interactive Trader (RIT) REST API with built-in error handling, rate limiting, and pandas DataFrame support.

## Features

- **Clean API**: Intuitive methods for all RIT endpoints
- **DataFrame Support**: Automatic conversion of API responses to pandas DataFrames
- **Error Handling**: Robust exception handling for all error scenarios
- **Rate Limiting**: Automatic retry logic for rate-limited requests
- **Type Safety**: Type hints throughout for better IDE support
- **Case Flexibility**: Graceful handling of endpoints not available in specific cases
- **Full Coverage**: All endpoints from RIT API specification implemented

## Installation

### From Source (Development)

```bash
# Clone repository
git clone https://github.com/Dadd0/RITpy.git

# Install package in development mode
uv pip install -e .
```

### From PyPI

```bash
# using uv
uv add ritpy

# equivalently, using pip
pip install ritpy
```

### Configuration

1. Create a `.env` file:
   ```bash
   cp .env.example .env
   ```

2. Add your RIT API key:
   ```
   RIT_API_KEY=your_actual_api_key_here
   ```

## Quick Start

```python
from rit_client import RITClient

# Initialize client (automatically loads .env)
client = RITClient()

# Get case information
case_info = client.get_case()
print(f"Case: {case_info['name']}, Status: {case_info['status']}")

# Get all securities as a DataFrame
securities = client.get_securities()
print(securities[['ticker', 'position', 'last', 'bid', 'ask']])

# Get news feed
news = client.get_news(limit=5)
for _, item in news.iterrows():
    print(f"{item['headline']}: {item['body']}")

# Get order book for a security
book = client.get_order_book('CRZY')
print("Bids:", book['bid'])
print("Asks:", book['ask'])
```

## API Reference

### Case Information

```python
# Get case details
case = client.get_case()

# Check if case is active
if client.is_case_active():
    print("Case is running!")
```

### Trader Information

```python
# Get trader info
trader = client.get_trader()
print(f"NLV: ${trader['nlv']:,.2f}")

# Quick NLV check
nlv = client.get_nlv()
```

### Securities

```python
# Get all securities
securities = client.get_securities()

# Get specific security
security = client.get_security('CRZY')

# Get order book
book = client.get_order_book('CRZY', limit=10)

# Get price history (OHLC)
history = client.get_security_history('CRZY', limit=100)

# Get time & sales
tas = client.get_time_and_sales('CRZY', limit=50)
```

### News

```python
# Get latest news
news = client.get_news(limit=10)

# Get news after specific ID
news = client.get_news(after=150)

# Get single most recent news item
latest = client.get_latest_news()
```

### Orders

**Note**: Order endpoints may not be available in all cases (e.g., Commodities case).

```python
# Get open orders
orders = client.get_open_orders()

# Submit a market order (with error handling)
try:
    order = client.submit_order(
        ticker='CRZY',
        order_type='MARKET',
        quantity=100,
        action='BUY'
    )
except EndpointNotAvailableException:
    print("Orders not available in this case")

# Submit a limit order
order = client.submit_order(
    ticker='CRZY',
    order_type='LIMIT',
    quantity=100,
    action='SELL',
    price=15.50
)

# Cancel order
client.cancel_order(order_id=123)

# Cancel all orders
cancelled = client.cancel_all_orders()

# Cancel by ticker
cancelled = client.cancel_orders_by_ticker('CRZY')
```

### Assets (Commodities Cases)

```python
# Get available assets
assets = client.get_assets()

# Get asset history
history = client.get_asset_history(ticker='CONTAINER')

# Lease an asset
lease = client.lease_asset('CONTAINER')

# Use a leased asset (e.g., refinery)
result = client.use_leased_asset(
    lease_id=1,
    from_tickers=['CRUDE'],
    quantities=[100]
)

# Get active leases
leases = client.get_leases()

# Cancel a lease
client.cancel_lease(lease_id=1)
```

### Trading Limits

```python
# Get trading limits
limits = client.get_limits()
print(limits[['name', 'gross', 'net', 'gross_limit', 'net_limit']])
```

### Tenders

```python
# Get active tenders
tenders = client.get_tenders()

# Accept a tender
client.accept_tender(tender_id=5, price=10.50)

# Decline a tender
client.decline_tender(tender_id=5)
```

### Utility Methods

```python
# Get complete market snapshot
snapshot = client.get_market_snapshot()
# Returns: {'case', 'trader', 'securities', 'limits', 'news', 'orders'}

print(snapshot['securities'])
print(snapshot['news'])
```

## Error Handling

The library provides specific exceptions for different error scenarios:

```python
from rit_client import RITClient
from rit_exceptions import (
    RateLimitException,
    AuthenticationException,
    EndpointNotAvailableException,
    OrderException,
    InvalidParameterException
)

client = RITClient()

try:
    order = client.submit_order(
        ticker='CRZY',
        order_type='LIMIT',
        quantity=100,
        action='BUY',
        price=15.0
    )
except EndpointNotAvailableException:
    print("Orders not available in this case (e.g., Commodities)")
except OrderException as e:
    print(f"Order failed: {e}")
except RateLimitException as e:
    print(f"Rate limited, wait {e.wait_time} seconds")
except AuthenticationException:
    print("Check your API key")
```

## Real-Time Data Streaming

```python
import time

client = RITClient()
last_news_id = 0

while client.is_case_active():
    # Get case info
    case = client.get_case()
    print(f"Tick: {case['tick']}")

    # Check for new news
    news = client.get_news(after=last_news_id, limit=10)
    for _, item in news.iterrows():
        if item['news_id'] > last_news_id:
            print(f"NEWS: {item['headline']}")
            last_news_id = item['news_id']

    # Get current securities data
    securities = client.get_securities()

    # Your trading logic here...

    time.sleep(0.5)  # Avoid rate limiting
```

## Data Analysis Example

```python
import pandas as pd

client = RITClient()

# Download historical data
all_history = {}
securities = client.get_securities()

for ticker in securities['ticker']:
    history = client.get_security_history(ticker, limit=100)
    all_history[ticker] = history

# Analyze and save
for ticker, df in all_history.items():
    df.to_csv(f'data/{ticker}_history.csv', index=False)

    # Calculate statistics
    avg_price = df['close'].mean()
    volatility = df['close'].std()
    print(f"{ticker}: Avg={avg_price:.2f}, Vol={volatility:.2f}")
```

## Configuration

All configuration is done via environment variables in `.env`:

```bash
# Required
RIT_API_KEY=your_api_key_here

# Optional (with defaults)
RIT_BASE_URL=http://localhost:9999/v1
RIT_MAX_RETRIES=3
RIT_RETRY_DELAY=0.5
RIT_TIMEOUT=10
```

You can also pass configuration directly:

```python
client = RITClient(
    api_key='your_key',
    base_url='http://localhost:9999/v1',
    max_retries=5,
    timeout=15
)
```

## Rate Limiting

The client automatically handles rate limiting:
- Retries requests when rate limited
- Uses `Retry-After` header from API
- Configurable max retries and delays
- Raises `RateLimitException` if max retries exceeded

## Case Compatibility

Different RIT cases support different endpoints:

| Endpoint | Commodities | Options | Algorithm | Liquidity |
|----------|-------------|---------|-----------|-----------|
| Securities | ✅ | ✅ | ✅ | ✅ |
| Orders (POST) | ❌ | ✅ | ✅ | ✅ |
| Assets | ✅ | ❌ | ❌ | ❌ |
| Leases | ✅ | ❌ | ❌ | ❌ |
| Tenders | ✅* | ❌ | ❌ | ❌ |

*Depends on specific case configuration

The library gracefully handles unavailable endpoints with `EndpointNotAvailableException`.

## Examples

See `example_usage.py` for comprehensive examples:
```bash
python example_usage.py
```

## Module Structure

```
COM/
├── rit_client.py          # Main client class
├── rit_exceptions.py      # Custom exceptions
├── config.py              # Configuration management
├── example_usage.py       # Usage examples
├── .env.example           # Environment template
├── pyproject.toml         # Dependencies
└── README.md             # This file
```

## Troubleshooting

### API Key Issues
```python
# Check if API key is loaded
from config import Config
Config.validate()  # Raises error if key missing
```

### Connection Issues
- Ensure RIT Client is running
- Check that API is enabled in RIT Client (green API icon)
- Verify base URL matches RIT Client port

### Rate Limiting
- Add `time.sleep()` between requests in loops
- Increase `RIT_RETRY_DELAY` in `.env`
- Reduce polling frequency