# Creating Orders

Step-by-step guide for creating, signing, submitting, and managing orders programmatically with the Python SDK.

## Prerequisites

Install the SDK and have your credentials ready:

```bash
pip install turbine-py-client
```

You need a wallet private key and API credentials. If you don't have API credentials yet, the SDK can register them for you (see Auto-Registration below).

## Initialize the Client

```python
import os
import time
from dotenv import load_dotenv
from turbine_client import TurbineClient, Outcome, Side, OrderArgs

load_dotenv()

client = TurbineClient(
    host=os.environ["TURBINE_HOST"],            # https://api.turbinefi.com
    chain_id=int(os.environ.get("CHAIN_ID", "137")),
    private_key=os.environ["TURBINE_PRIVATE_KEY"],
    api_key_id=os.environ["TURBINE_API_KEY_ID"],
    api_private_key=os.environ["TURBINE_API_PRIVATE_KEY"],
)
```

### Auto-Registration

If you don't have `TURBINE_API_KEY_ID` and `TURBINE_API_PRIVATE_KEY` yet, register them first:

```python
credentials = TurbineClient.request_api_credentials(
    host="https://api.turbinefi.com",
    private_key=os.environ["TURBINE_PRIVATE_KEY"],
)

# Save these — the private key is not recoverable
print(f"TURBINE_API_KEY_ID={credentials['api_key_id']}")
print(f"TURBINE_API_PRIVATE_KEY={credentials['api_private_key']}")
```

## Check the Orderbook

Before placing an order, inspect the current orderbook to find appropriate prices:

```python
market_id = "0x..."  # Get from client.get_markets() or client.get_quick_market()

ob = client.get_orderbook(market_id, outcome=Outcome.YES)

print("YES Orderbook:")
print("  Bids (buy orders):")
for level in ob.bids[:5]:
    print(f"    {level.price} (${level.price / 1e6:.4f}) x {level.size / 1e6:.2f} shares")

print("  Asks (sell orders):")
for level in ob.asks[:5]:
    print(f"    {level.price} (${level.price / 1e6:.4f}) x {level.size / 1e6:.2f} shares")

# Calculate mid price
if ob.bids and ob.asks:
    mid = (ob.bids[0].price + ob.asks[0].price) // 2
    print(f"\n  Mid: {mid} (${mid / 1e6:.4f})")
    print(f"  Spread: {ob.asks[0].price - ob.bids[0].price}")
```

## Create and Submit an Order

### Using create\_limit\_buy / create\_limit\_sell

The simplest approach. The client handles signing and nonce generation:

```python
# Buy 10 YES shares at $0.45
signed = client.create_limit_buy(
    market_id=market_id,
    outcome=Outcome.YES,
    price=450000,       # $0.45
    size=10000000,      # 10 shares
    expiration=int(time.time()) + 3600,  # 1 hour
)

print(f"Order hash: {signed.order_hash}")
print(f"Cost if filled: {450000 * 10000000 / 1e6 / 1e6:.2f} USDC")  # $4.50

# Submit to the orderbook
result = client.post_order(signed)
print(f"Result: {result}")
```

```python
# Sell 5 NO shares at $0.60
signed = client.create_limit_sell(
    market_id=market_id,
    outcome=Outcome.NO,
    price=600000,       # $0.60
    size=5000000,       # 5 shares
    expiration=int(time.time()) + 3600,
)

result = client.post_order(signed)
```

### Using OrderArgs

For more control, construct `OrderArgs` explicitly:

```python
args = OrderArgs(
    market_id=market_id,
    side=Side.BUY,
    outcome=Outcome.YES,
    price=500000,                        # $0.50
    size=1000000,                        # 1 share
    expiration=int(time.time()) + 3600,  # 1 hour
    nonce=0,                             # auto-generated
    maker_fee_recipient="0x0000000000000000000000000000000000000000",
)

signed = client.create_order(args)
result = client.post_order(signed)
```

## Price and Size Encoding

All prices and sizes are integers with 6 decimal places:

| Value | Raw        | Meaning                 |
| ----- | ---------- | ----------------------- |
| Price | `500000`   | $0.50 (50% probability) |
| Price | `100000`   | $0.10 (10% probability) |
| Price | `900000`   | $0.90 (90% probability) |
| Size  | `1000000`  | 1 share                 |
| Size  | `10000000` | 10 shares               |
| Size  | `500000`   | 0.5 shares              |

Valid prices: `1` to `999999` (exclusive of 0 and 1,000,000).

Cost to buy: `price * size / 1,000,000`. For example, buying 10 shares at `450000` ($0.45) costs `450000 * 10000000 / 1000000 = 4500000` = $4.50 USDC.

Payout if you win: `size`. So 10 shares pay `10000000` = $10 USDC. Profit = $10 - $4.50 = $5.50.

## USDC Approval

Before your first order can settle, the settlement contract needs USDC approval. Use the gasless max permit (one-time, no native gas needed):

```python
# One-time gasless max approval per settlement contract
result = client.approve_usdc_for_settlement()
print(f"Approval TX: {result}")
```

This signs an EIP-2612 max permit and submits it to the relayer. All subsequent orders on that settlement contract reuse the existing allowance.

To check the current allowance:

```python
allowance = client.get_usdc_allowance()
print(f"Current allowance: {allowance / 1e6:.2f} USDC")
```

### Per-Order Permit (Alternative)

You can attach a permit to an individual order instead:

```python
permit = client.sign_usdc_permit(value=5000000)  # $5 USDC
signed.permit_signature = permit
result = client.post_order(signed)
```

## Order Lifecycle

{% stepper %}
{% step %}

### Created

`create_limit_buy()` or `create_order()` signs the order locally and returns a `SignedOrder`.
{% endstep %}

{% step %}

### Submitted

`post_order()` sends it to the API. The matching engine checks for compatible counter-orders.
{% endstep %}

{% step %}

### Matched

If a compatible order exists, a trade is created. Both orders are matched at the maker's price.
{% endstep %}

{% step %}

### Settled

The matched trade is settled on-chain via the settlement contract.
{% endstep %}

{% step %}

### Failed / Pending

Check `get_failed_trades()` / `get_pending_trades()` for settlement status.
{% endstep %}
{% endstepper %}

## Cancel Orders

### Cancel a Single Order

```python
result = client.cancel_order(
    order_hash="0xOrderHash...",
    market_id=market_id,    # optional validation
    side=Side.BUY,          # optional validation
)
print(f"Cancelled: {result}")
```

### Cancel All Orders for a Market

```python
result = client.cancel_market_orders(market_id=market_id)
print(f"Cancelled all: {result}")
```

## Monitor Order Status

### Check Your Open Orders

```python
orders = client.get_orders(market_id=market_id, status="open")

for o in orders:
    side = "BUY" if o.side == 0 else "SELL"
    outcome = "YES" if o.outcome == 0 else "NO"
    filled_pct = o.filled_size / o.size * 100 if o.size > 0 else 0
    print(
        f"{side} {outcome}: {o.remaining_size / 1e6:.2f} remaining "
        f"@ {o.price} (${o.price / 1e6:.4f}) | "
        f"filled: {filled_pct:.1f}% | {o.status}"
    )
```

### Check a Specific Order

```python
order = client.get_order("0xOrderHash...")
print(f"Status: {order.status}")
print(f"Filled: {order.filled_size / 1e6:.2f} / {order.size / 1e6:.2f}")
print(f"Remaining: {order.remaining_size / 1e6:.2f}")
```

### Monitor Failed and Pending Trades

After orders match, trades go through on-chain settlement. Monitor the pipeline:

```python
# Trades waiting for settlement TX
pending = client.get_pending_trades()
for t in pending:
    print(f"Pending: {t.fill_size / 1e6:.2f} @ {t.fill_price} (${t.fill_price / 1e6:.4f}) | tx={t.tx_hash}")

# Trades that failed settlement
failed = client.get_failed_trades()
for t in failed:
    print(f"Failed: {t.fill_size / 1e6:.2f} @ {t.fill_price} (${t.fill_price / 1e6:.4f}) | reason={t.reason}")

# Check a specific settlement TX
status = client.get_settlement_status("0xTxHash...")
print(f"Found: {status.found}, Status: {status.status}, Error: {status.error}")
```

## Complete Example

A script that checks the orderbook, places a buy order, and monitors it:

```python
import os
import time
from dotenv import load_dotenv
from turbine_client import TurbineClient, Outcome, Side

load_dotenv()

client = TurbineClient(
    host=os.environ["TURBINE_HOST"],
    chain_id=int(os.environ.get("CHAIN_ID", "137")),
    private_key=os.environ["TURBINE_PRIVATE_KEY"],
    api_key_id=os.environ["TURBINE_API_KEY_ID"],
    api_private_key=os.environ["TURBINE_API_PRIVATE_KEY"],
)

# Get active BTC quick market
qm = client.get_quick_market("BTC")
market_id = qm.market_id
print(f"Market: Will BTC be above ${qm.start_price / 1e8:,.2f}?")
print(f"Market ID: {market_id}")

# Ensure USDC approval (gasless, one-time)
allowance = client.get_usdc_allowance()
if allowance < 2**128:
    print("Approving USDC (gasless)...")
    client.approve_usdc_for_settlement()

# Check orderbook
ob = client.get_orderbook(market_id, outcome=Outcome.YES)
if ob.asks:
    best_ask = ob.asks[0]
    print(f"Best YES ask: {best_ask.price} (${best_ask.price / 1e6:.4f}) x {best_ask.size / 1e6:.2f}")

# Place a limit buy for YES at $0.45
signed = client.create_limit_buy(
    market_id=market_id,
    outcome=Outcome.YES,
    price=450000,       # $0.45
    size=2000000,       # 2 shares
    expiration=int(time.time()) + 900,  # 15 minutes
)

print(f"\nSubmitting order: BUY 2 YES @ 450000 ($0.45)")
print(f"Order hash: {signed.order_hash}")
print(f"Cost if filled: $0.90 USDC")

result = client.post_order(signed)
print(f"Response: {result}")

# Wait and check status
time.sleep(5)
order = client.get_order(signed.order_hash)
print(f"\nOrder status: {order.status}")
print(f"Filled: {order.filled_size / 1e6:.2f} / {order.size / 1e6:.2f}")

if order.status == "open" and order.remaining_size > 0:
    print("Order still open. Cancelling...")
    client.cancel_order(signed.order_hash)
    print("Cancelled.")
```
