← Terminal
API Reference

Five Endpoints.
One Clean Interface.

All Cloudflare Pages Functions under /api/*. Every response is JSON. The Supabase service key never reaches the browser.

GET/api/status

Current wallet balance, PnL since start, open position details, and active bot settings.

GET/api/history

Paginated list of all closed trades, newest first. Supports limit and offset query params.

POST/api/settings

Persist strategy config (EMA periods, leverage, SL %, TP %) to Supabase.

POST/api/execute-trade

Open or close a leveraged position. Atomically updates both positions and wallet.

POST/api/backtest

In-memory EMA simulation against historical Binance klines. No DB writes.

GET

/api/status

Returns balance, PnL, the active open position (or null), and persisted bot settings.
Response — 200 OK
JSON
{
  "balance":         987.42,
  "pnl":             -12.58,
  "pnl_percent":     -1.258,
  "active_position": {
    "id":          "3fa8…",
    "side":        "LONG",
    "entry":       67420.00,
    "stop_loss":   66071.16,
    "take_profit": 70791.00,
    "leverage":    3
  },
  "settings": {
    "strategy":    "ema_cross",
    "leverage":    3,
    "stop_loss":   0.02,
    "take_profit": 0.05,
    "indicators":  { "short_ema": 9, "long_ema": 21 }
  }
}
GET

/api/history

Closed trades ordered newest-first. Query: ?limit=50&offset=0
Query Params
limit integer Records to return (default 50, max 200). optional
offset integer Records to skip for pagination (default 0). optional
Response — 200 OK
JSON
{
  "trades": [{
    "id":          "3fa8…",
    "side":        "LONG",
    "entry_price": 67420.00,
    "close_price": 70791.00,
    "leverage":    3,
    "size":        0.04231,
    "pnl":         142.60,
    "closed_at":   "2025-04-01T18:47Z"
  }],
  "total": 87
}
POST

/api/settings

Saves strategy configuration. Returns {"success":true} on success.
Body Fields
strategy string Always "ema_cross" required
leverage integer 1–10 required
stop_loss number Fraction e.g. 0.02 = 2% required
take_profit number Fraction e.g. 0.05 = 5% required
indicators object short_ema and long_ema periods required
Request Body
JSON
{
  "strategy":    "ema_cross",
  "leverage":    5,
  "stop_loss":   0.03,
  "take_profit": 0.08,
  "indicators": {
    "short_ema": 12,
    "long_ema":  26
  }
}
POST

/api/execute-trade

Opens or closes a simulated position. Atomically writes to positions and wallet. Sizing: margin = balance × 0.95, size = (margin × leverage) / entry.
Open a Position
Body — action: "OPEN"
actionstring"OPEN" required
sidestring"LONG" or "SHORT" required
entry_pricenumberCurrent BTC/USDT price required
leverageinteger1–10 required
stop_loss_pricenumberAbsolute price trigger optional
take_profit_pricenumberAbsolute price trigger optional
Response — 200 OK
JSON
{
  "success":  true,
  "position": {
    "id":          "3fa8…",
    "side":        "LONG",
    "entry_price": 67420.00,
    "leverage":    3,
    "size":        0.04231,
    "margin":      950.00,
    "stop_loss":   66071.16,
    "take_profit": 70791.00,
    "status":      "OPEN"
  }
}
Close a Position
Body — action: "CLOSE"
actionstring"CLOSE" required
position_idUUIDID of the open position required
close_pricenumberCurrent BTC/USDT price required
reasonstringTP · SL · SIGNAL · MANUAL optional
Response — 200 OK
JSON
{
  "success":     true,
  "pnl":         +142.60,
  "new_balance": 1092.60
}
Error Codes
StatusCondition
409Open while a position already exists
404Position not found or already closed
400Zero balance or invalid parameters
POST

/api/backtest

Fetches historical Binance klines server-side and simulates EMA-cross in memory. Does not touch Supabase. Interval is auto-selected: 1h ≤90d · 4h ≤365d · 1d longer.
Body Fields
start_dateISO 8601Window start required
end_dateISO 8601Window end required
settingsobjectSame shape as POST /api/settings required
Response — 200 OK
JSON
{
  "total_trades":       42,
  "winning_trades":     26,
  "losing_trades":      16,
  "win_rate":           61.9,
  "total_pnl":         +312.44,
  "total_pnl_percent": +31.24,
  "max_drawdown":       18.7,
  "final_balance":     1312.44,
  "interval":          "1d",
  "equity_curve": [
    { "time": 1711929600, "value": 1000.00 },
    // one entry per candle…
  ]
}

Database Schema

Three Supabase PostgreSQL tables. All writes go through Cloudflare — the service key never leaves the server.

wallet

single row
CREATE TABLE wallet (
  id           UUID         PRIMARY KEY,
  balance      NUMERIC(18,8) DEFAULT 1000,
  last_updated TIMESTAMPTZ  DEFAULT NOW()
);

positions

every trade
CREATE TABLE positions (
  id          UUID         PRIMARY KEY,
  side        TEXT         CHECK ('LONG'|'SHORT'),
  entry_price NUMERIC(18,8),
  close_price NUMERIC(18,8),
  leverage    INTEGER      CHECK (1..10),
  size        NUMERIC(18,8),
  margin      NUMERIC(18,8),
  status      TEXT         CHECK ('OPEN'|'CLOSED'),
  pnl         NUMERIC(18,8),
  stop_loss   NUMERIC(18,8),
  take_profit NUMERIC(18,8),
  created_at  TIMESTAMPTZ,
  closed_at   TIMESTAMPTZ
);

bot_settings

single row · JSONB
CREATE TABLE bot_settings (
  id         UUID  PRIMARY KEY,
  config     JSONB DEFAULT '{
    "strategy":    "ema_cross",
    "leverage":    3,
    "stop_loss":   0.02,
    "take_profit": 0.05,
    "indicators":  {
      "short_ema": 9,
      "long_ema":  21
    }
  }',
  updated_at TIMESTAMPTZ DEFAULT NOW()
);