Metadata-Version: 2.4
Name: hunter-quant
Version: 0.1.3
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: 3
Requires-Dist: polars>=1.0
Requires-Dist: maturin>=1.7,<2.0 ; extra == 'dev'
Requires-Dist: pytest>=8,<9 ; extra == 'dev'
Provides-Extra: dev
Summary: Hunter quant bot framework Python bindings
Author: Hunter Contributors
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM

# hunter-quant

Python bindings for the Hunter quant trading framework. Built with PyO3 + maturin for high-performance live trading, sandbox, and backtesting.

## Installation

```bash
uv add hunter-quant
```

For projects that do not use `uv`:

```bash
pip install hunter-quant
```

## Development

```bash
cd bindings/python
uv sync --extra dev
uv run maturin develop
uv run pytest
```

Static stub synchronization can be checked without installing the extension:

```bash
uv run python bindings/python/scripts/check_pyi_sync.py
```

Release steps are documented in `docs/release.md`.

## Examples

Runnable strategy examples live in `examples/`:

- `examples/sma_cross.py`
- `examples/supertrend_risk.py`

Install the extension into your active environment before running examples:

```bash
cd bindings/python
uv run maturin develop
uv run python examples/sma_cross.py
```

## Quality Gates

```bash
cargo fmt -p python
cargo check -p python
cargo test -p python
uv run python bindings/python/scripts/check_pyi_sync.py
```

After `maturin develop`, run Python smoke tests:

```bash
cd bindings/python
uv run maturin develop
uv run pytest
```

## Quick Start

```python
import hunter
from hunter import Strategy


class MyStrategy(Strategy):
    def __init__(self):
        self.btc = self.candles(connector_id="hyperliquid_perp", symbol="BTC-USDC", timeframe="15m")
        self.broker = self.broker(connector_id="hyperliquid_perp", address = "", private_key = "", rate_limit = 1200 )

    def on_bar(self):
        close = self.btc.close
        fast = hunter.ta.sma(close, 7)
        slow = hunter.ta.sma(close, 25)

        if fast.cross_over(slow):
            self.broker.market_buy("BTC-USDC", 0.01)
        elif fast.cross_under(slow):
            self.broker.market_sell("BTC-USDC", 0.01)
```

## Run Modes

- `"live"` — real trading on live exchanges
- `"sandbox"` — simulated trading on testnet
- `"backtest"` — historical backtesting

```python
hunter.run(MyStrategy(), mode="backtest")
hunter.run(MyStrategy(), mode="sandbox")
hunter.run(MyStrategy(), mode="live")
```

## Feeds

### CandleFeed

```python
feed = self.candles(connector_id="hyperliquid_perp", symbol="BTC-USDC", timeframe="15m", warmup_period=200)
```

**Properties/Methods:**
- `dataframe` — polars DataFrame
- `open`, `high`, `low`, `close`, `volume`, `start_time` — shortcut to each column as `Series`

### OrderBookFeed

```python
feed = self.orderbook(connector_id="hyperliquid_perp", symbol="BTC-USDC")
```

**Properties:** `best_bid`, `best_ask`, `mid_price`, `spread`, `spread_bps`, `microprice`, `microprice_offset_bps`, `bids`, `asks`, `top_of_book_imbalance`

**Methods:** `cumulative_bid_depth(levels)`, `cumulative_ask_depth(levels)`, `depth_imbalance(levels)`, `depth_pressure(levels, decay)`

### TradeFeed

```python
feed = self.trades(connector_id="hyperliquid_perp", symbol="BTC-USDC", warmup_period=100)
```

- `dataframe` — polars DataFrame

## Series API

```python
series[0]        # latest value (rightmost)
len(series)

series.cross_over(other)
series.cross_under(other)
series.cross(other)
series.to_polars()
```

## TA Module

```python
import hunter

hunter.ta.sma(source, period=20)
hunter.ta.ema(source, period=20)
hunter.ta.rsi(source, period=14)
hunter.ta.bollinger_bands(source, period=20, dev_up=2.0, dev_down=2.0)
hunter.ta.macd(source, fast=12, slow=26, signal=9)
hunter.ta.atr(high, low, close, period=14)
hunter.ta.adx(high, low, close, period=14)
hunter.ta.stoch(high, low, close, k=14, k_slow=3, d=3)
hunter.ta.supertrend(high, low, close, period=10, multiplier=3.0)
hunter.ta.vwap(high, low, close, volume)
hunter.ta.cci(high, low, close, period=20)
hunter.ta.obv(close, volume)
hunter.ta.sar(high, low, acceleration=0.02, maximum=0.2)
hunter.ta.heikin_ashi(open, high, low, close)
hunter.ta.slope(source, n=5)

# Candlestick patterns
hunter.ta.cdl_doji(open, high, low, close, body_percent=0.1)
hunter.ta.cdl_dragonfly_doji(open, high, low, close, body_percent=1.0)
hunter.ta.cdl_gravestone_doji(open, high, low, close, body_percent=1.0)
hunter.ta.cdl_hammer(open, high, low, close, period=20, factor=1.5)
hunter.ta.cdl_inverted_hammer(open, high, low, close, period=20, factor=1.5)
hunter.ta.cdl_long_shadow(open, high, low, close, period=10, shadow_factor=1.5)
hunter.ta.cdl_marubozu(open, high, low, close, period=10, shadow_percent=0.1)
```

## Broker

```python
broker = self.broker(connector_id="hyperliquid_perp")

# Order shortcuts
broker.market_buy("BTC-USDC", 0.01)
broker.market_sell("BTC-USDC", 0.01)
broker.limit_buy("BTC-USDC", 0.01, price=50000)
broker.limit_sell("BTC-USDC", 0.01, price=55000)

# Conditional orders
broker.stop_market_buy("BTC-USDC", 0.01, trigger_price=51000)
broker.take_profit_market_sell("BTC-USDC", 0.01, trigger_price=60000)
broker.stop_limit_buy("BTC-USDC", 0.01, trigger_price=51000, limit_price=51200)
broker.take_profit_limit_sell("BTC-USDC", 0.01, trigger_price=60000, limit_price=59000)

# Batch orders
broker.place_orders([
    {"symbol": "BTC-USDC", "side": "buy", "quantity": 0.01, "order_type": "market"},
    {"symbol": "BTC-USDC", "side": "sell", "quantity": 0.01, "order_type": "limit", "price": 60000},
])

# Order management
broker.get_order(order_id)
broker.get_opened_orders("BTC-USDC")
broker.get_orders()
broker.get_order_by_client_id("my-client-id")
broker.cancel_order(order_id)
broker.cancel_orders([id1, id2])

# Positions
position = broker.get_position("BTC-USDC")
position.unrealized_pnl(current_price=55000)
position.unrealized_pnl_percentage(current_price=55000)
broker.get_positions()
broker.close_position("BTC-USDC")
broker.get_orders_in_position("BTC-USDC")

# Stop loss / take profit
broker.with_stop_loss("BTC-USDC", price=48000)
broker.with_take_profit("BTC-USDC", price=65000)
broker.stop_loss_price("BTC-USDC")
broker.take_profit_price("BTC-USDC")
broker.clear_stop_loss("BTC-USDC")
broker.clear_take_profit("BTC-USDC")

# Market info
market = broker.get_market("BTC-USDC")
market.align_price(50000.123)
market.align_quantity(0.012345)

# Status
broker.running_status()
broker.current_price("BTC-USDC")
```

## Risk Rules

### Before-order rules

```python
from hunter import MaxNetPositionRule, StopLossPauseRule, OrderSizeRule

strategy.add_before_order_rule(
    MaxNetPositionRule(max_abs_position_size=1.5),
    StopLossPauseRule(stop_loss_pause_mins=60, max_loss_nums=3),
    OrderSizeRule(min_order_size=0.001, max_order_size=1.0),
)
```

### After-order rules

```python
from hunter import OrderTimeoutRule, MaxDrawdownRule

strategy.add_after_order_rule(
    OrderTimeoutRule(timeout_secs=300),
    MaxDrawdownRule(drawdown_limit=0.1, limit_type="amount"),
)
```

## Notifier

```python
from hunter import Notifier

strategy.add_notifier(Notifier.logger(level="info"))
strategy.add_notifier(Notifier.telegram(token="xxx", recipients=[123456789]))
strategy.add_notifier(Notifier.dingtalk(token="xxx", secret=None))
strategy.add_notifier(
    Notifier.email(
        username="u@example.com",
        password="xxx",
        server="smtp.example.com",
        from_="u@example.com",
        recipients=["alerts@example.com"],
    )
)

# Send notification within a strategy
self.notify("Subject", "Message body")
```

## Callbacks

```python
class MyStrategy(Strategy):
    def on_ready(self):              # strategy initialized
    def on_bar(self):                # new candle
    def on_orderbook(self):          # orderbook updated
    def on_trade(self):              # trade occurred
    def on_order_update(self, order):  # order status changed
    def on_stop(self):               # strategy stopped
```

Callbacks must be synchronous functions. `async def` callbacks are rejected at runtime.

## Event Objects

```python
# Bar
bar.feed, bar.symbol, bar.interval
bar.open, bar.high, bar.low, bar.close, bar.volume, bar.timestamp

# OrderBook
ob.symbol, ob.best_bid, ob.best_ask, ob.spread, ob.microprice

# Trade
trade.symbol, trade.price, trade.quantity, trade.side, trade.timestamp

# Fill
fill.fill_id, fill.order_id, fill.price, fill.quantity

# Order
order.id, order.symbol, order.side, order.order_type
order.quantity, order.executed_quantity, order.average_price
order.status, order.created_at, order.updated_at

# Position
position.symbol, position.side, position.quantity, position.average_price
position.unrealized_pnl(current_price)
position.unrealized_pnl_percentage(current_price)
```

## Backtest Example

```python
import hunter
from hunter import Strategy


class SupertrendCross(Strategy):
    def __init__(self):
        self.btc = self.candles(
            connector_id="hyperliquid_perp", symbol="BTC-USDC", timeframe="15m",
        )
        self.broker = self.broker(connector_id="hyperliquid_perp")
        self.with_trailing_stop(3600)

    def on_bar(self):
        st = hunter.ta.supertrend(self.btc.high, self.btc.low, self.btc.close, 10, 3.0)

        if st.trend.cross_over(st.line):
            self.broker.market_buy("BTC-USDC", 0.01)
        elif st.trend.cross_under(st.line):
            self.broker.market_sell("BTC-USDC", 0.01)


hunter.run(SupertrendCross(), mode="backtest")
```

