From 021049b2590006621c350ab882ff4884d1676135 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Jun 2026 07:29:42 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20GridBot=20als=20zweite=20Paper-Engine?= =?UTF-8?q?=20=E2=80=94=20No-Stop-XRP-Grid=20live?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit processGridCycle (Paritätstest gegen runGridBacktest), GridEngine mit DB-Recovery (grid_state/grid_lots, bot_state id=2), bot-Spalte in paper_trades/equity_snapshots, /api/grid, Dashboard-Panel. Bewusster Paper-Probelauf trotz Gate-Fail (User-Entscheidung 2026-06-10). Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 4 +- drizzle/0002_burly_joystick.sql | 25 ++ drizzle/meta/0002_snapshot.json | 604 +++++++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + public/index.html | 34 +- src/server/api/server.ts | 95 ++++- src/server/db/schema.ts | 37 +- src/server/index.ts | 16 +- src/server/live/engine.ts | 7 +- src/server/live/grid-cycle.test.ts | 85 ++++ src/server/live/grid-cycle.ts | 189 +++++++++ src/server/live/grid-engine.ts | 181 +++++++++ 12 files changed, 1267 insertions(+), 17 deletions(-) create mode 100644 drizzle/0002_burly_joystick.sql create mode 100644 drizzle/meta/0002_snapshot.json create mode 100644 src/server/live/grid-cycle.test.ts create mode 100644 src/server/live/grid-cycle.ts create mode 100644 src/server/live/grid-engine.ts diff --git a/CLAUDE.md b/CLAUDE.md index ab2aff8..ffd8724 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,8 @@ # trade-kuns -Multi-Pair-Trendfolge-Paper-Bot (BTC/ETH/SOL/XRP_USDT): Donchian-20-Breakout auf 4h, EMA-200 + ADX-20-Filter, Chandelier-Trailing-Stop (3×ATR), long-only, fixe Parameter. **Paper-only — keine Order-Ausführung.** +Zwei Paper-Engines in einem Prozess (**paper-only — keine Order-Ausführung**): +1. **Trend-Bot** (BTC/ETH/SOL/XRP_USDT): Donchian-20-Breakout auf 4h, EMA-200 + ADX-20-Filter, Chandelier-Trailing-Stop (3×ATR), long-only, fixe Parameter. State: `bot_state` id=1. +2. **GridBot** (nur XRP): No-Stop-ATR-Grid — 8 Levels, Spacing 3×ATR(4h), nie Verlust-Verkäufe, verlustfreies Re-Center bei leerem Grid außerhalb der Range. State: `bot_state` id=2, `grid_state`/`grid_lots`. Spec/Validierung: `docs/walkforward-grid-2026-06-10.md`. ## Stack & Befehle - Bun 1.3 + TypeScript, Drizzle (Postgres), Zod. Tests collocated (`*.test.ts`). diff --git a/drizzle/0002_burly_joystick.sql b/drizzle/0002_burly_joystick.sql new file mode 100644 index 0000000..c877f05 --- /dev/null +++ b/drizzle/0002_burly_joystick.sql @@ -0,0 +1,25 @@ +CREATE TABLE "grid_lots" ( + "id" serial PRIMARY KEY NOT NULL, + "pair" varchar(16) NOT NULL, + "level_idx" integer NOT NULL, + "qty" double precision NOT NULL, + "entry_ts" timestamp with time zone NOT NULL, + "entry_price" double precision NOT NULL, + "entry_cost" double precision NOT NULL, + "risk_amount" double precision NOT NULL +); +--> statement-breakpoint +CREATE TABLE "grid_state" ( + "pair" varchar(16) PRIMARY KEY NOT NULL, + "center" double precision NOT NULL, + "spacing" double precision NOT NULL, + "lower_bound" double precision NOT NULL, + "upper_bound" double precision NOT NULL, + "budget_per_level" double precision NOT NULL, + "activated_ts" timestamp with time zone NOT NULL +); +--> statement-breakpoint +ALTER TABLE "equity_snapshots" ADD COLUMN "bot" text DEFAULT 'trend' NOT NULL;--> statement-breakpoint +ALTER TABLE "equity_snapshots" DROP CONSTRAINT "equity_snapshots_pkey";--> statement-breakpoint +ALTER TABLE "equity_snapshots" ADD CONSTRAINT "equity_snapshots_bot_ts_pk" PRIMARY KEY("bot","ts");--> statement-breakpoint +ALTER TABLE "paper_trades" ADD COLUMN "bot" text DEFAULT 'trend' NOT NULL; \ No newline at end of file diff --git a/drizzle/meta/0002_snapshot.json b/drizzle/meta/0002_snapshot.json new file mode 100644 index 0000000..1ba2c46 --- /dev/null +++ b/drizzle/meta/0002_snapshot.json @@ -0,0 +1,604 @@ +{ + "id": "2e351b16-12b9-4c9a-a777-29cd5cf06dd4", + "prevId": "38fbc5fc-4ef1-4dae-b408-21bcafa513b7", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.backtest_runs": { + "name": "backtest_runs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "result": { + "name": "result", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bot_state": { + "name": "bot_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "cash": { + "name": "cash", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "start_capital": { + "name": "start_capital", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "cursor_ts": { + "name": "cursor_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.candles": { + "name": "candles", + "schema": "", + "columns": { + "pair": { + "name": "pair", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "ts": { + "name": "ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "open": { + "name": "open", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "high": { + "name": "high", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "low": { + "name": "low", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "close": { + "name": "close", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "volume": { + "name": "volume", + "type": "double precision", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "candles_pair_ts_pk": { + "name": "candles_pair_ts_pk", + "columns": [ + "pair", + "ts" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.decision_logs": { + "name": "decision_logs", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "pair": { + "name": "pair", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "bar_ts": { + "name": "bar_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "signal": { + "name": "signal", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "blocked_by": { + "name": "blocked_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "close": { + "name": "close", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "atr": { + "name": "atr", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "adx": { + "name": "adx", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "donchian_high": { + "name": "donchian_high", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "trend_ema": { + "name": "trend_ema", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "price_after_4h": { + "name": "price_after_4h", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "price_after_24h": { + "name": "price_after_24h", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "price_after_72h": { + "name": "price_after_72h", + "type": "double precision", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "decision_logs_pair_bar_ts": { + "name": "decision_logs_pair_bar_ts", + "columns": [ + { + "expression": "pair", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "bar_ts", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.equity_snapshots": { + "name": "equity_snapshots", + "schema": "", + "columns": { + "bot": { + "name": "bot", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'trend'" + }, + "ts": { + "name": "ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "equity": { + "name": "equity", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "cash": { + "name": "cash", + "type": "double precision", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "equity_snapshots_bot_ts_pk": { + "name": "equity_snapshots_bot_ts_pk", + "columns": [ + "bot", + "ts" + ] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.grid_lots": { + "name": "grid_lots", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "pair": { + "name": "pair", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "level_idx": { + "name": "level_idx", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "qty": { + "name": "qty", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "entry_ts": { + "name": "entry_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "entry_price": { + "name": "entry_price", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "entry_cost": { + "name": "entry_cost", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "risk_amount": { + "name": "risk_amount", + "type": "double precision", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.grid_state": { + "name": "grid_state", + "schema": "", + "columns": { + "pair": { + "name": "pair", + "type": "varchar(16)", + "primaryKey": true, + "notNull": true + }, + "center": { + "name": "center", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "spacing": { + "name": "spacing", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "lower_bound": { + "name": "lower_bound", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "upper_bound": { + "name": "upper_bound", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "budget_per_level": { + "name": "budget_per_level", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "activated_ts": { + "name": "activated_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.paper_trades": { + "name": "paper_trades", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "bot": { + "name": "bot", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'trend'" + }, + "pair": { + "name": "pair", + "type": "varchar(16)", + "primaryKey": false, + "notNull": true + }, + "side": { + "name": "side", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entry_ts": { + "name": "entry_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "entry_price": { + "name": "entry_price", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "exit_ts": { + "name": "exit_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "exit_price": { + "name": "exit_price", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "qty": { + "name": "qty", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "pnl": { + "name": "pnl", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "r": { + "name": "r", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "exit_reason": { + "name": "exit_reason", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.positions": { + "name": "positions", + "schema": "", + "columns": { + "pair": { + "name": "pair", + "type": "varchar(16)", + "primaryKey": true, + "notNull": true + }, + "side": { + "name": "side", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "qty": { + "name": "qty", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "entry_ts": { + "name": "entry_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "entry_price": { + "name": "entry_price", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "entry_cost": { + "name": "entry_cost", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "initial_stop": { + "name": "initial_stop", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "stop": { + "name": "stop", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "trail_extreme": { + "name": "trail_extreme", + "type": "double precision", + "primaryKey": false, + "notNull": true + }, + "risk_amount": { + "name": "risk_amount", + "type": "double precision", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 0c4934a..e5cd5b5 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -15,6 +15,13 @@ "when": 1781071452889, "tag": "0001_certain_omega_red", "breakpoints": true + }, + { + "idx": 2, + "version": "7", + "when": 1781076227862, + "tag": "0002_burly_joystick", + "breakpoints": true } ] } \ No newline at end of file diff --git a/public/index.html b/public/index.html index 63d847e..2db2521 100644 --- a/public/index.html +++ b/public/index.html @@ -52,6 +52,14 @@

Letzte Entscheidungen (4h-Bars)

+
+

GridBot XRP No-Stop · 3×ATR · 8 Levels · Paper

+
+
+

Offene Lots

+

Grid-Trades

+
+