Metadata-Version: 2.4
Name: lse-data
Version: 0.9.2
Summary: Python SDK for London Strategic Edge: paper-account trading, real-time market data, historical candles
Project-URL: Homepage, https://londonstrategicedge.com
Project-URL: Documentation, https://github.com/londonstrategicedge/lse-data
Project-URL: Repository, https://github.com/londonstrategicedge/lse-data
Project-URL: Issues, https://github.com/londonstrategicedge/lse-data/issues
Author-email: London Strategic Edge <support@londonstrategicedge.com>
License-Expression: MIT
License-File: LICENSE
Keywords: crypto,forex,market-data,options,real-time,stocks,trading,websocket
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial :: Investment
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.8
Requires-Dist: websockets>=11.0
Provides-Extra: pandas
Requires-Dist: pandas>=1.5; extra == 'pandas'
Provides-Extra: progress
Requires-Dist: tqdm>=4.0; extra == 'progress'
Description-Content-Type: text/markdown

# lse-data

Python SDK for London Strategic Edge — paper-account trading and market data for 12,000+ instruments including stocks, forex, crypto, ETFs, options, and commodities. Trade a paper account with a 12-digit api_key, stream live ticks, pull historical candles, all from one package.

## Install

```bash
pip install lse-data
```

## Command-line (easiest)

After install, save your key once and use the `lse` command anywhere:

```bash
lse auth lse_live_xxxxxxxxxxxx

# Fetch 30 days of gold 1h candles
lse candles XAU/USD --days 30 --timeframe 1h

# Write 5 years of gold 1m to CSV
lse candles XAU/USD --start 2021-01-01 --end 2026-01-01 --timeframe 1m --csv gold_5y.csv

# Stream live ticks
lse stream XAU/USD BTC/USD AAPL

# Browse the symbol catalog
lse catalog --category commodity
```

## Quick start

```python
from lse import LSE

client = LSE(api_key="your_api_key")

for tick in client.stream(["BTC/USD", "AAPL", "EUR/USD"]):
    print(f"{tick.symbol}: ${tick.price}")
```

Get your API key at [londonstrategicedge.com/data](https://londonstrategicedge.com/data).

## Features

- Historical OHLCV candles for 4,000+ instruments (REST, no WebSocket needed)
- Real-time WebSocket streaming with auto-reconnect
- 4,000+ symbols: US/UK/EU/Asia stocks, crypto, forex, indices, commodities, ETFs
- 81,000+ option contracts via `subscribe_options()`
- Symbol catalog API for discovery before subscribing
- Server-side symbol validation (invalid symbols rejected immediately)
- Dynamic subscribe/unsubscribe at runtime
- Graceful disconnect (from callbacks or other threads)
- Sync and async interfaces
- Callback and iterator patterns
- Paper-account trading via `BrueTrading` (MetaTrader-style 12-digit api_key, no JWT)
- Zero config, just an API key

## Paper-account trading

Each paper account on the LSE platform is auto-issued a 12-digit `api_key` at creation. That key alone is the credential for trading the account, the same way MetaTrader's expert advisor login number works. Use it for strategy bots, CI runners, or your own desktop terminal.

```python
from lse import BrueTrading

trading = BrueTrading("047382910556")    # 12-digit key from /paper-trading

trading.buy("EUR/USD", 1, sl=1.10, tp=1.25)
trading.sell("XAU/USD", 0.5)
trading.buy_limit("EUR/USD", 1, price=1.05)

print(trading.account())                  # {'balance': ..., 'leverage': 50, ...}
for p in trading.positions():
    print(p)

trading.close_all("EUR/USD")              # flatten one symbol
trading.close_all()                       # flatten everything
```

Full method list: `account`, `positions`, `orders`, `buy`, `sell`, `buy_limit`, `sell_limit`, `buy_stop`, `sell_stop`, `close`, `close_all`. Errors raise `BrueTradingError` with the HTTP status preserved on the exception. Full reference: [londonstrategicedge.com/docs/brue-trading](https://londonstrategicedge.com/docs/brue-trading).

## Usage

### Historical candles

Fetch OHLCV candles for any instrument. No WebSocket connection needed.

```python
from lse import LSE

client = LSE(api_key="your_key")

# As a list of dicts
result = client.candles("XAU/USD", start="2021-01-01", end="2026-01-01", timeframe="1d")
print(f"{result['rows']} candles, plan: {result['plan']}")
for c in result["data"][-3:]:
    print(f"{c['timestamp']}  close={c['close']}")

# As a pandas DataFrame (pip install lse-data[pandas])
df = client.candles("AAPL", "2025-01-01", "2026-01-01", timeframe="1h", as_dataframe=True)
print(df.tail())
```

**Supported timeframes:** `1m`, `5m`, `15m`, `30m`, `1h`, `2h`, `4h`, `1d`

Works for all 4,000+ instruments: stocks, crypto, forex, ETFs, commodities, indices.

### Large downloads with live progress

For multi-year 1m pulls (which exceed the 2M row single-call cap), pass `progress=True`. The SDK auto-chunks the request and shows a tqdm bar. Requires `pip install lse-data[progress]`.

```python
from lse import LSE

client = LSE(api_key="your_key")

df = client.candles(
    "XAU/USD",
    start="2021-01-01",
    end="2026-01-01",
    timeframe="1m",
    progress=True,
    as_dataframe=True,
)
# XAU/USD 1m: 100%|████████████| 61/61 [01:35<00:00,  1.6s/chunk]
# -> 1,872,343 rows

df.to_csv("gold_5y.csv")
```

Want custom chunk sizes? Pass `chunk_days=30` (or any int). Defaults per timeframe: 1m=30d, 5m=180d, 15m=365d, 1h=1825d, 1d=3650d.

### Stream ticks (simplest)

```python
from lse import LSE

client = LSE(api_key="your_key")

for tick in client.stream(["BTC/USD", "ETH/USD", "AAPL"]):
    print(f"{tick.symbol:12s} ${tick.price:>12,.2f}")
```

### Callback style

```python
from lse import LSE

def on_tick(tick):
    print(f"{tick.symbol}: {tick.price}")

client = LSE(api_key="your_key")
client.on("tick", on_tick)
client.connect(symbols=["BTC/USD", "ETH/USD"])
```

### Async streaming

```python
import asyncio
from lse import LSE

async def main():
    client = LSE(api_key="your_key")
    async for tick in client.stream_async(["BTC/USD"]):
        print(tick)

asyncio.run(main())
```

### Save to CSV

```python
import csv, datetime
from lse import LSE

client = LSE(api_key="your_key")

with open("ticks.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["time", "symbol", "price", "bid", "ask"])

    for tick in client.stream(["BTC/USD", "ETH/USD"]):
        writer.writerow([
            datetime.datetime.now().isoformat(),
            tick.symbol, tick.price, tick.bid, tick.ask,
        ])
```

## Tick object

Each tick has these fields:

| Field | Type | Description |
|-------|------|-------------|
| `symbol` | `str` | Instrument symbol (e.g. `BTC/USD`, `AAPL`) |
| `price` | `float` | Latest price |
| `bid` | `float` | Bid price (if available) |
| `ask` | `float` | Ask price (if available) |
| `volume` | `float` | Volume (if available) |
| `timestamp` | `float` | Unix timestamp |
| `name` | `str` | Human-readable name (e.g. `Apple Inc.`) |

## Events

When using the callback style with `.on()`:

| Event | Callback args | Description |
|-------|---------------|-------------|
| `tick` | `Tick` | New price tick |
| `connected` | (none) | WebSocket connected |
| `authenticated` | (none) | API key accepted |
| `disconnected` | (none) | Connection lost (will auto-reconnect) |
| `error` | `str` | Error message |

## Symbol catalog

Query available instruments before subscribing. No WebSocket connection needed.

```python
from lse import LSE

client = LSE(api_key="your_key")

# Get all available symbols
all_symbols = client.catalog()
print(f"{len(all_symbols)} instruments available")

# Filter by category: stock, crypto, forex, etf, commodity, index
stocks = client.catalog(category="stock")
crypto = client.catalog(category="crypto")

# Each entry has symbol, display_name, and category
for s in crypto[:5]:
    print(f"{s['symbol']:12s} {s['display_name']:20s} {s['category']}")
```

The catalog returns every subscribable instrument. Use it to build symbol pickers, validate user input, or discover what is available.

### Available categories

| Category | Count | Examples |
|----------|-------|---------|
| stock | ~3,987 | AAPL, NVDA, TSLA, 0005.HK, 7203.T |
| forex | ~62 | EUR/USD, GBP/JPY, USD/CHF |
| crypto | ~58 | BTC/USD, ETH/USD, SOL/USD |
| etf | ~25 | SPY, QQQ, IWM, GLD |
| commodity | ~23 | XAU/USD, WTICO/USD, NATGAS/USD |
| index | ~13 | US30, NAS100, UK100 |

## Options streaming

Subscribe to entire options chains by underlying. One call gives you every contract (calls + puts, all strikes, all expiries).

```python
from lse import LSE

client = LSE(api_key="your_key")

def on_tick(tick):
    print(f"{tick.symbol}: ${tick.price:.2f}")

client.on("tick", on_tick)
client.subscribe_options(["AAPL", "TSLA"])
client.connect()
```

To stop receiving a chain:

```python
client.unsubscribe_options(["TSLA"])
```

## Unsubscribe

Remove symbols at runtime without disconnecting:

```python
client.unsubscribe(["BTC/USD"])      # stop one symbol
client.subscribe(["SOL/USD"])        # add another
```

## Disconnect

Stop the stream and exit cleanly. Works from callbacks or another thread:

```python
tick_count = 0

def on_tick(tick):
    global tick_count
    tick_count += 1
    if tick_count >= 100:
        client.disconnect()  # connect()/stream() returns

client.on("tick", on_tick)
client.connect(symbols=["BTC/USD"])
print("Done, collected 100 ticks")
```

Async version:

```python
await client.disconnect_async()
```

## Requirements

- Python 3.8+
- `websockets` (installed automatically)

## License

MIT
