# Horizon Strategy Agent

LLM that writes trading strategies using the Horizon SDK. Always up to date.

## Problem

LLMs hallucinate API signatures ~43% of the time when coding against unfamiliar libraries.
We fix this with auto-generated SDK reference from live introspection — the LLM never sees stale docs.

## Architecture

```
┌──────────────────────────────────────────────────────────┐
│  GitHub Action (on every push to main)                   │
│  maturin build → python tools/generate_sdk_ref.py        │
│  → commits sdk_reference.md to repo (or Vercel Blob)     │
└──────────────────────┬───────────────────────────────────┘
                       │ auto-sync
                       ▼
┌──────────────────────────────────────────────────────────┐
│  Vercel (Next.js + AI SDK)                               │
│                                                          │
│  System prompt = sdk_reference.md + strategy_examples.md │
│  Claude generates strategy code                          │
│  Tool calls → Railway worker for validation/deploy       │
│  Streams results back to UI                              │
└──────────────────────┬───────────────────────────────────┘
                       │ HTTP (tool calls)
                       ▼
┌──────────────────────────────────────────────────────────┐
│  Railway Worker (existing)                               │
│                                                          │
│  POST /validate  → syntax + import + risk check          │
│  POST /backtest  → paper backtest with historical data   │
│  POST /deploy    → start hz.run() as background job      │
│  POST /status    → check running strategies              │
│  POST /stop      → graceful shutdown                     │
└──────────────────────────────────────────────────────────┘
```

## What Exists Now

| File | Status | What It Does |
|------|--------|-------------|
| `tools/generate_sdk_ref.py` | Done | Introspects live `horizon` module → 8,500-line markdown with every function signature, class, enum, docstring. Run: `python tools/generate_sdk_ref.py -o tools/sdk_reference.md` |
| `tools/sdk_reference.md` | Done | The generated reference (285KB). This is the LLM's "brain" for Horizon. |
| `tools/strategy_examples.md` | Done | 15 complete strategy patterns covering all composition styles. |

## How the LLM Gets Its Knowledge

The Vercel AI SDK app injects two files as system context:

1. **`sdk_reference.md`** (285KB, ~70K tokens) — every public function with exact signature
2. **`strategy_examples.md`** (8KB) — 15 runnable strategy templates

```typescript
// lib/sdk-context.ts
import { readFileSync } from 'fs';
// or: fetch from Vercel Blob / S3 / API

const sdkRef = readFileSync('sdk_reference.md', 'utf-8');
const examples = readFileSync('strategy_examples.md', 'utf-8');

export const systemPrompt = `You are a Horizon SDK strategy generator.

IMPORTANT: Use ONLY the functions and signatures documented below.
Do NOT guess or invent function signatures.

${sdkRef}

## Strategy Examples

${examples}
`;
```

### Context Size Strategy

285KB (~70K tokens) fits in Claude's 200K context window. Options if you need to trim:

1. **Full injection** (recommended): inject entire `sdk_reference.md` as system prompt. Most reliable.
2. **Sectioned**: split into chunks by group, use tool calls to retrieve relevant sections on demand.
3. **Hybrid**: inject "How to Write a Strategy" + "Core Types" + "Core Entry Points" in system prompt (~5K tokens), rest via RAG tool.

## Auto-Sync: How It Stays Current

When you push code changes to `src/` or `python/`:

```yaml
# .github/workflows/sync-sdk-ref.yml
name: Sync SDK Reference
on:
  push:
    branches: [main]
    paths: ['src/**', 'python/**']

jobs:
  generate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Install maturin
        run: pip install maturin

      - name: Build Horizon
        run: maturin develop --features skip-auth

      - name: Generate SDK reference
        run: python tools/generate_sdk_ref.py -o tools/sdk_reference.md

      - name: Commit if changed
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add tools/sdk_reference.md
          git diff --cached --quiet || git commit -m "Auto-update SDK reference"
          git push
```

New Rust function → reference updates → LLM sees it next request.

## Vercel App (Next.js + AI SDK)

```typescript
// app/api/strategy/route.ts
import { streamText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';
import { systemPrompt } from '@/lib/sdk-context';

const RAILWAY_URL = process.env.RAILWAY_WORKER_URL!;

export async function POST(req: Request) {
  const { messages } = await req.json();

  const { stream } = await streamText({
    model: anthropic('claude-sonnet-4-6'),
    system: systemPrompt,
    tools: {
      validate_strategy: {
        description: 'Validate Python strategy code for syntax and import errors',
        parameters: z.object({ code: z.string() }),
        execute: async ({ code }) => {
          const res = await fetch(`${RAILWAY_URL}/validate`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ code }),
          });
          return await res.json();
        },
      },
      backtest_strategy: {
        description: 'Backtest strategy on historical data',
        parameters: z.object({
          code: z.string(),
          markets: z.array(z.string()),
          days: z.number().int().min(7).max(365).optional(),
        }),
        execute: async ({ code, markets, days }) => {
          const res = await fetch(`${RAILWAY_URL}/backtest`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ code, markets, lookback_days: days || 90 }),
          });
          return await res.json();
        },
      },
      deploy_strategy: {
        description: 'Deploy validated strategy to paper or live trading',
        parameters: z.object({
          code: z.string(),
          mode: z.enum(['paper', 'live']).default('paper'),
        }),
        execute: async ({ code, mode }) => {
          const res = await fetch(`${RAILWAY_URL}/deploy`, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ code, mode }),
          });
          return await res.json();
        },
      },
    },
    messages,
  });

  return stream.toDataStreamResponse();
}
```

## Flow

```
User: "Make me a market maker for polymarket crypto markets"
  │
  ▼
Claude (sdk_reference.md + examples in context):
  → Generates strategy code with correct signatures
  → Calls validate_strategy tool → Railway worker checks syntax/imports
  │
  ├─ FAIL → reads error, fixes code, re-validates
  │
  ├─ PASS → calls backtest_strategy tool
  │         → reviews Sharpe, drawdown, win rate
  │         → if poor metrics, rewrites strategy
  │
  └─ PASS → asks user to confirm
            → calls deploy_strategy tool
            → Railway starts hz.run()
```

## What's Left to Build

| # | Component | What |
|---|-----------|------|
| 1 | `.github/workflows/sync-sdk-ref.yml` | Copy the YAML above into your repo |
| 2 | Vercel app | `npx create-next-app` + AI SDK + the route above |
| 3 | Railway endpoints | Add `/validate`, `/backtest`, `/deploy` to your existing worker |
