Metadata-Version: 2.4
Name: deltain
Version: 1.0.0
Summary: Python SDK for Delta Exchange India — perpetual futures broker with bracket orders and OHLCV candle history
Author-email: Manjur Friend <friendmanjur.crypto@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/friendmanjur/delta-exchange-india-sdk
Project-URL: Repository, https://github.com/friendmanjur/delta-exchange-india-sdk
Project-URL: Bug Tracker, https://github.com/friendmanjur/delta-exchange-india-sdk/issues
Project-URL: Documentation, https://github.com/friendmanjur/delta-exchange-india-sdk#readme
Keywords: delta exchange,india,crypto,futures,trading,sdk,api,algo,fintech
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Programming Language :: Python :: 3
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: Operating System :: OS Independent
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28
Dynamic: license-file

# deltain

[![PyPI version](https://img.shields.io/pypi/v/deltain.svg)](https://pypi.org/project/deltain/)
[![Python](https://img.shields.io/pypi/pyversions/deltain.svg)](https://pypi.org/project/deltain/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Python SDK for [Delta Exchange India](https://india.delta.exchange) — the INR-settled perpetual futures exchange. Built from scratch with a clean abstract broker interface, automatic retry logic, and a full live integration test suite verified against the Delta Exchange testnet.

```bash
pip install deltain
```

---

## Features

| Capability | Details |
|---|---|
| **Market & Limit Orders** | IOC market orders, GTC limit orders |
| **Stop-Loss Orders** | Standalone stop-loss with `close_on_trigger` |
| **Bracket Orders** | Entry + SL + TP in a single atomic API call |
| **OHLCV Candle History** | 1-minute candles, auto-paginated across any date range |
| **Bracket Support Check** | `is_bracket_order_supported()` per symbol |
| **Order Status & Cancel** | Query by `client_order_id`, idempotent cancel |
| **Auto-reconnect** | Retries on 502 Bad Gateway and connection errors |
| **Testnet / Mainnet** | Single flag to switch environments |
| **Abstract Base Class** | `Broker` ABC so you can swap exchanges without changing strategy code |

---

## Installation

```bash
pip install deltain
```

**Requirements:** Python >= 3.10, `requests` >= 2.28

Or install from source:

```bash
git clone https://github.com/friendmanjur/delta-exchange-india-sdk.git
cd delta-exchange-india-sdk
pip install .
```

---

## Quick Start

```python
from deltain import DeltaBroker

broker = DeltaBroker(
    api_key    = "YOUR_KEY",
    api_secret = "YOUR_SECRET",
    testnet    = True,   # flip to False for live trading
)
broker.login()

# Live price
price = broker.get_price("BTCUSD")
print(f"BTCUSD: {price:.2f}")

# Market order
fill = broker.place_market_order("BTCUSD", "buy", qty=1)

# Bracket order - entry + stop-loss + take-profit in one call
broker.place_bracket_order(
    symbol            = "BTCUSD",
    side              = "buy",
    qty               = 1,
    limit_price       = price,
    stop_loss_price   = round(price * 0.95, 1),
    take_profit_price = round(price * 1.05, 1),
    oid               = "my-signal-001",
)

# 1-minute candles - auto-paginated, any range
import time
now = (int(time.time()) // 60) * 60
candles = broker.get_one_minute_candles(
    exchange    = "delta_india",
    symbol      = "BTCUSD",
    start_epoch = now - 7 * 24 * 3600,   # 7 days
    end_epoch   = now,
)
print(f"Fetched {len(candles)} candles")
```

---

## API Reference

### `DeltaBroker(api_key, api_secret, testnet=False, timeout=10, retry_delay=1.0)`

| Method | Description |
|---|---|
| `login()` | Verify credentials against `/v2/profile`. Must be called first. |
| `get_price(symbol)` | Returns the current mark price as a `float`. |
| `place_market_order(symbol, side, qty, oid=None)` | IOC market order. Returns average fill price. |
| `place_stoploss_order(symbol, side, qty, price, oid)` | GTC stop-loss market order. |
| `place_target_order(symbol, side, qty, price, oid)` | GTC reduce-only limit order. |
| `place_bracket_order(symbol, side, qty, limit_price, stop_loss_price, take_profit_price, ...)` | Atomic bracket order. Returns `{"delta_id", "avg_fill", "status"}`. |
| `get_order_status(oid)` | Returns `{"status", "price", "raw_state"}`. Status: `OPEN` `TRIGGERED` `CANCELLED` `UNKNOWN`. |
| `cancel_order(oid)` | Idempotent cancel - safe to call on already-closed orders. |
| `is_bracket_order_supported(symbol)` | `True` for perpetual/dated futures. Never raises. |
| `get_one_minute_candles(exchange, symbol, start_epoch, end_epoch)` | Paginated 1-min OHLCV. Returns list of `{"time", "open", "high", "low", "close", "volume"}`. |
| `is_connected()` | Ping - returns `True` if credentials are valid and exchange is reachable. |
| `relogin()` | Re-authenticate without creating a new broker instance. |

### `place_bracket_order` full signature

```python
broker.place_bracket_order(
    symbol                  = "BTCUSD",
    side                    = "buy",               # "buy" or "sell"
    qty                     = 1,
    limit_price             = 67000.0,             # entry limit price
    stop_loss_price         = 63650.0,             # SL trigger price
    take_profit_price       = 70350.0,             # TP trigger price
    stop_loss_limit_price   = None,                # optional - market SL if omitted
    take_profit_limit_price = None,                # optional - market TP if omitted
    stop_trigger_method     = "last_traded_price", # or "mark_price" / "index_price"
    oid                     = "signal-btc-001",    # optional client_order_id
)
```

### `get_one_minute_candles` boundary alignment rule

Delta Exchange applies an **asymmetric rounding rule** to timestamps, discovered and verified via 32 live boundary tests:

```
aligned_start = ceil(start / 60) * 60   <- CEILING
aligned_end   = floor(end   / 60) * 60  <- FLOOR
```

**Always align your timestamps to minute boundaries** for predictable results:

```python
# Correct - always align first
start = (your_epoch // 60) * 60
end   = (your_epoch // 60) * 60
```

| Start offset | End offset | Candles returned |
|---|---|---|
| `:00` | `:00` to `:59` | 1 candle (the `:00` candle) |
| `:00` | `:60` | 2 candles |
| `:01` to `:59` | less than `:60` | **0 candles** - start ceils past end |
| `:01` to `:59` | `:60` to `:119` | 1 candle (only the `:60` candle) |

---

## Abstract Broker Interface

Write strategy code once, swap the exchange later:

```python
from deltain import Broker

class MyOtherBroker(Broker):
    def get_price(self, symbol): ...
    def place_market_order(self, symbol, side, qty): ...
    def place_stoploss_order(self, symbol, side, qty, price, oid): ...
    def place_target_order(self, symbol, side, qty, price, oid): ...
    def place_bracket_order(self, symbol, side, qty, limit_price,
                            stop_loss_price, take_profit_price, ...): ...
    def get_order_status(self, oid): ...
    def cancel_order(self, oid): ...
    # is_bracket_order_supported() defaults to True
    # get_one_minute_candles() raises NotImplementedError until overridden
```

---

## Tests

All tests run against **testnet only** - no live funds are ever used.

```bash
export DELTA_API_KEY="your_testnet_key"
export DELTA_API_SECRET="your_testnet_secret"

# Bracket order tests
python3 tests/test_bracket_order.py

# Candle stress tests
python3 tests/stress_test_candles.py

# Run specific groups
python3 tests/stress_test_candles.py single multi
python3 tests/stress_test_candles.py pagination
python3 tests/stress_test_candles.py edge
```

| Test file | Assertions | What is covered |
|---|---|---|
| `test_bracket_order.py` | 24 | Login guard, position pre-flight, bracket lifecycle, SL/TP child visibility, idempotent cancel |
| `stress_test_candles.py` | 400+ | Schema, OHLC sanity, pagination up to 30 days, multi-symbol, 32 second-boundary combinations |

---

## Project Structure

```
delta-exchange-india-sdk/
├── deltain/
│   ├── __init__.py
│   └── broker.py              <- Broker ABC + DeltaBroker (820 lines)
├── tests/
│   ├── test_bracket_order.py
│   └── stress_test_candles.py
├── examples/
│   └── quickstart.py
├── pyproject.toml
├── CHANGELOG.md
├── LICENSE
└── README.md
```

---

## Environment Variables

| Variable | Description |
|---|---|
| `DELTA_API_KEY` | Your Delta Exchange India API key |
| `DELTA_API_SECRET` | Your Delta Exchange India API secret |

Get testnet credentials at [testnet.delta.exchange](https://testnet.delta.exchange).

---

## Security

- **Never commit credentials.** Use environment variables or a secrets manager.
- `.gitignore` excludes `.env` and `secrets.py`.
- All tests are hard-coded to `testnet=True`.

---

## Changelog

See [CHANGELOG.md](CHANGELOG.md).

---

## License

MIT - see [LICENSE](LICENSE).

---

## Acknowledgements

Built on the [Delta Exchange India REST API](https://docs.delta.exchange).
Retry and connection patterns ported from the Shoonya C++ trading system.
