# Infrastructure: Hotkeys & Bot Control

## Overview

In the original LuckySt terminal, a human trader controls the market making bot via keyboard hotkeys. In the agentic version (LuckySt2), **Claude agents (Luckbots) replace the human** and send the same commands programmatically via Redis.

The terminal doesn't know or care whether commands come from a human pressing keys or an agent pushing Redis messages — the execution is identical.

## Command Reference

All commands are JSON objects pushed to the Redis queue:
```
Key: trading:instance:{instance_id}:command
```

### Core Trading Controls

| Command | Redis Payload | Human Hotkey Equivalent | Description |
|---------|--------------|------------------------|-------------|
| Toggle Pause | `{"action": "toggle_pause"}` | `Space` | Pause/resume trading |
| Single Fire | `{"action": "single_fire"}` | `F` | Execute exactly 1 cycle, then pause |
| Stop | `{"action": "stop"}` | `S` | Complete current cycle, then stop |
| Force Stop | `{"action": "force_stop"}` | `Ctrl+C` | Cancel all orders immediately |

### JUMP Controls

| Command | Redis Payload | Human Hotkey Equivalent | Description |
|---------|--------------|------------------------|-------------|
| Toggle Jump (Market 1) | `{"action": "toggle_jump", "market_index": 0}` | `1` | Penny ahead on market 1 |
| Toggle Jump (Market 2) | `{"action": "toggle_jump", "market_index": 1}` | `2` | Penny ahead on market 2 |

### Advanced Controls

| Command | Redis Payload | Human Hotkey Equivalent | Description |
|---------|--------------|------------------------|-------------|
| Toggle Fair Value | `{"action": "toggle_fair_value"}` | `V` | Enable/disable fair value tracking |

## How Agents Send Commands

### Via Redis (Direct — Preferred for Agents)

```python
import redis
import json

r = redis.Redis(host='localhost', port=6379, db=0)

def send_command(instance_id: int, action: str, **kwargs):
    """Send a command to a trading instance"""
    command = {"action": action, **kwargs}
    r.rpush(f"trading:instance:{instance_id}:command", json.dumps(command))
```

### Via REST API (Through FastAPI)

```
POST /api/terminal/instances/{instance_id}/control
{"action": "toggle_pause"}

POST /api/terminal/instances/{instance_id}/jump
{"market_index": 0}

POST /api/terminal/instances/{instance_id}/fair_value
{}
```

## Agent Decision Loop

A typical Luckbot control loop:

```python
async def agent_control_loop(instance_id: int):
    """Main agent decision loop — agent runs in JOIN continuously,
    uses JUMP tactically on specific markets."""
    while True:
        # 1. Read terminal status
        status = await get_instance_status(instance_id)

        # 2. Read external data (Pyth, CCXT)
        spot_price = await get_pyth_price(BTC_PRICE_ID)
        vol = await calculate_vol_from_ccxt("BTC/USDT")

        # 3. Calculate fair value / CDF signal
        fair_value = compute_cdf_probability(spot_price, vol, strike, time_to_expiry)

        # 4. Decide on JUMP for specific markets
        for market_idx in range(num_markets):
            if should_jump(status, market_idx, fair_value):
                send_command(instance_id, "toggle_jump", market_index=market_idx)

        # 5. Decide on pause/fire actions
        if should_pause(status, fair_value):
            send_command(instance_id, "toggle_pause")
        elif should_single_fire(status):
            send_command(instance_id, "single_fire")

        # 6. Wait before next decision
        await asyncio.sleep(5)  # Agents decide every 5 seconds
```

## Command Processing on Terminal Side

The terminal processes commands via `process_redis_commands_async()` in `mm_core.py`:

1. Pop all pending commands from Redis queue (LPOP loop)
2. Parse JSON payload
3. Execute the action (toggle jump, pause, stop, etc.)
4. Continue with next trading tick

Commands are processed at the **start** of every tick (every ~1 second), so there's at most a 1-second delay between an agent sending a command and it taking effect.

## Reading Terminal State

Agents can read the terminal's current state:

### Via Redis
```python
status_data = r.get(f"trading:instance:{instance_id}:status")
state_data = r.get(f"trading:instance:{instance_id}:state")
```

### Via REST API
```
GET /api/terminal/instances/{instance_id}/status
```

### Via WebSocket
Subscribe to the WebSocket endpoint for real-time streaming updates.

## Safety Rules for Agents

1. **Never spam commands** — respect the 1-second tick cycle
2. **Always check status before acting** — don't send pause if already paused
3. **Use single_fire for testing** — verify strategy works before continuous mode
4. **Force stop is last resort** — prefer graceful stop to complete the cycle
5. **Log all commands sent** — for debugging and syndicate audit trail
6. **Rate limits apply** — REST API endpoints have per-minute limits

## Multi-Instance Control

A single agent can control multiple trading instances:
- Each instance has its own Redis command queue
- Agent maintains state for each instance separately
- Cross-instance coordination (e.g., total portfolio risk) is the agent's responsibility
