digdigdig3 0.3.5

Unified async Rust API for 47 exchange connectors (REST + WebSocket). The core layer — pure ExchangeHub + connectors. Higher-level builder, persistence, replay, OB tracker live in `digdigdig3-station`.
Documentation
# Twelvedata Connector


Multi-asset data provider connector for stocks, forex, crypto, ETFs, commodities, and indices.

## ⚠ Important: DATA PROVIDER ONLY


Twelvedata is a **data provider**, not a trading exchange. It provides:
- ✅ Market data (price, quotes, historical OHLCV)
- ✅ 100+ technical indicators
- ✅ Fundamental data (stocks only, Grow+ tier)
- ✅ WebSocket streaming (Pro+ tier)
-**NO trading/order execution**
-**NO account/balance information**
-**NO position management**

Trading-related methods will return `ExchangeError::UnsupportedOperation`.

## Features


### Multi-Asset Support

- **Stocks**: 60,000+ symbols (90+ exchanges globally)
- **Forex**: 200+ pairs (majors, minors, exotics)
- **Crypto**: Thousands of pairs (180+ exchanges)
- **ETFs**: 5,000+ (US + international)
- **Commodities**: 50+ (metals, energy, agriculture)
- **Indices**: 100+ global indices

### Technical Indicators

- 100+ built-in indicators (RSI, MACD, Bollinger Bands, SMA, EMA, etc.)
- Customizable parameters
- Historical indicator values

### Data Quality

- Real-time data (Pro+ plans)
- Historical data back to 1980s-1990s
- Extended hours data (US pre/post-market, Pro+ plans)
- Fundamentals (income statements, balance sheets, earnings, Grow+ plans)

## Quick Start


```rust
use digdigdig3::stocks::us::twelvedata::TwelvedataConnector;
use digdigdig3::core::{Symbol, AccountType};
use digdigdig3::core::traits::{MarketData, ExchangeIdentity};

#[tokio::main]

async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create connector with API key
    let connector = TwelvedataConnector::new("your-api-key-here");

    // Or use environment variable (TWELVEDATA_API_KEY)
    let connector = TwelvedataConnector::from_env();

    // Or use demo key for testing (very limited)
    let connector = TwelvedataConnector::demo();

    // Get current price
    let symbol = Symbol::new("AAPL", "USD");
    let price = connector.get_price(symbol, AccountType::Spot).await?;
    println!("AAPL price: ${}", price);

    // Get full quote
    let ticker = connector.get_ticker(symbol.clone(), AccountType::Spot).await?;
    println!("Ticker: {:#?}", ticker);

    // Get historical klines
    let klines = connector.get_klines(symbol, "1h", Some(100), AccountType::Spot).await?;
    println!("Got {} klines", klines.len());

    Ok(())
}
```

## Environment Setup


Set your API key as an environment variable:

```bash
export TWELVEDATA_API_KEY="your-api-key-here"
```

## Getting an API Key


### Free Tier (Basic Plan)

1. Sign up at [https://twelvedata.com/pricing]https://twelvedata.com/pricing
2. Free tier includes:
   - 8 API calls per minute
   - 800 API calls per day
   - Basic endpoints (price, quote, time_series)
   - No WebSocket access

### Demo Key

For initial testing without signup:
```rust
let connector = TwelvedataConnector::demo();
```

**WARNING**: Demo key has severe rate limits and limited functionality.

### Paid Tiers

- **Grow** ($29+/mo): 55-377 calls/min, fundamentals, historical data
- **Pro** ($99+/mo): 610-1,597 calls/min, WebSocket, extended hours
- **Ultra** ($329+/mo): 2,584+ calls/min, advanced features
- **Enterprise**: Custom pricing

## Supported Methods


### Core Traits (Implemented)


```rust
// ExchangeIdentity
connector.exchange_id() -> ExchangeId
connector.exchange_name() -> &str
connector.exchange_type() -> ExchangeType::DataProvider
connector.is_testnet() -> bool

// MarketData
connector.get_price(symbol, account_type) -> f64
connector.get_ticker(symbol, account_type) -> Ticker
connector.get_klines(symbol, interval, limit, account_type) -> Vec<Kline>
connector.ping() -> ()

// Trading, Account, Positions
// ALL methods return ExchangeError::UnsupportedOperation
```

### Extended Methods (Provider-Specific)


```rust
// Symbol search
connector.symbol_search("AAPL") -> Value

// Reference data
connector.get_stocks() -> Value
connector.get_forex_pairs() -> Value
connector.get_cryptocurrencies() -> Value

// Market info
connector.market_state("NASDAQ") -> Value

// Technical indicators
connector.rsi(&symbol, "1day", 14) -> Value
connector.macd(&symbol, "1day") -> Value
```

## Symbol Formats


### Stocks

```rust
Symbol::new("AAPL", "USD")  // Apple stock
```

### Forex

```rust
Symbol::new("EUR", "USD")  // EUR/USD pair
```

### Crypto

```rust
Symbol::new("BTC", "USD")   // Bitcoin
Symbol::new("ETH", "USDT")  // Ethereum
```

## Intervals


Supported intervals for klines:
- Minutes: `1m`, `5m`, `15m`, `30m`, `45m`
- Hours: `1h`, `2h`, `4h`
- Days: `1d`
- Weeks: `1w`
- Months: `1M`

## Rate Limiting


### Free Tier Limits

- **8 requests per minute**
- **800 requests per day**
- No burst allowance

### Handling Rate Limits


The connector automatically handles rate limit errors:

```rust
match connector.get_price(symbol, AccountType::Spot).await {
    Err(ExchangeError::RateLimitExceeded { retry_after, message }) => {
        println!("Rate limit hit: {}", message);
        // Implement exponential backoff
    }
    Ok(price) => println!("Price: {}", price),
    Err(e) => eprintln!("Error: {}", e),
}
```

## Error Handling


### Common Errors


```rust
ExchangeError::Api { code, message }           // API returned error
ExchangeError::RateLimitExceeded { .. }        // Rate limit hit
ExchangeError::Auth(..)                        // Invalid API key
ExchangeError::PermissionDenied(..)            // Requires higher tier
ExchangeError::UnsupportedOperation(..)        // Trading/account methods
ExchangeError::Parse(..)                       // JSON parsing failed
ExchangeError::Network(..)                     // Network/HTTP error
```

## Testing


### Run Integration Tests


```bash
# Set API key

export TWELVEDATA_API_KEY="your-key"

# Run all tests

cargo test --package digdigdig3 --test twelvedata_integration -- --nocapture

# Run specific test

cargo test --package digdigdig3 --test twelvedata_integration test_get_price -- --nocapture

# Run with demo key (limited)

cargo test --package digdigdig3 --test twelvedata_integration test_demo_connection -- --nocapture
```

## Important Notes


### 1. String Numerics

Time series values are returned as **strings** to preserve precision:
```json
{
  "open": "149.50000",
  "close": "150.25000"
}
```
The parser handles conversion to `f64`.

### 2. Null Values

Many fields may be `null` when data is unavailable. The parser handles this defensively:
```json
{
  "volume": null,
  "fifty_two_week": { "high": null }
}
```

### 3. Credit System

Different endpoints cost different credits:
- Basic data: 1 credit per symbol
- Technical indicators: 1 credit
- Fundamentals: 10-50 credits
- Market movers: 100 credits per request

### 4. WebSocket (Pro+ Only)

WebSocket streaming is only available on Pro+ plans:
```rust
use digdigdig3::stocks::us::twelvedata::TwelvedataWebSocket;

let ws = TwelvedataWebSocket::new("your-api-key");
// Connection, subscription, heartbeat management required
```

## Files Structure


```
twelvedata/
├── mod.rs           - Module exports and documentation
├── endpoints.rs     - API endpoints and symbol formatting
├── auth.rs          - API key authentication (simple header-based)
├── parser.rs        - JSON response parsing
├── connector.rs     - Main connector + trait implementations
├── websocket.rs     - WebSocket connector (Pro+ tier)
├── research/        - API research documentation (8 files, 3682 lines)
└── README.md        - This file
```

## Resources


- **Official Docs**: https://twelvedata.com/docs
- **Pricing**: https://twelvedata.com/pricing
- **Support**: https://support.twelvedata.com/
- **API Playground**: https://twelvedata.com/docs#request-parameters

## License


Part of the NEMO trading system. See project root for license information.