feat: Schema für Live-Engine (positions, paper_trades, decision_logs, bot_state, equity_snapshots)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 06:11:44 +00:00
parent 69a0a7bee3
commit c5d71bba74
4 changed files with 593 additions and 1 deletions

View File

@@ -0,0 +1,58 @@
CREATE TABLE "bot_state" (
"id" integer PRIMARY KEY NOT NULL,
"cash" double precision NOT NULL,
"start_capital" double precision NOT NULL,
"cursor_ts" timestamp with time zone NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
CREATE TABLE "decision_logs" (
"id" serial PRIMARY KEY NOT NULL,
"pair" varchar(16) NOT NULL,
"bar_ts" timestamp with time zone NOT NULL,
"signal" text,
"blocked_by" text,
"close" double precision NOT NULL,
"atr" double precision,
"adx" double precision,
"donchian_high" double precision,
"trend_ema" double precision,
"price_after_4h" double precision,
"price_after_24h" double precision,
"price_after_72h" double precision
);
--> statement-breakpoint
CREATE TABLE "equity_snapshots" (
"ts" timestamp with time zone PRIMARY KEY NOT NULL,
"equity" double precision NOT NULL,
"cash" double precision NOT NULL
);
--> statement-breakpoint
CREATE TABLE "paper_trades" (
"id" serial PRIMARY KEY NOT NULL,
"pair" varchar(16) NOT NULL,
"side" text NOT NULL,
"entry_ts" timestamp with time zone NOT NULL,
"entry_price" double precision NOT NULL,
"exit_ts" timestamp with time zone NOT NULL,
"exit_price" double precision NOT NULL,
"qty" double precision NOT NULL,
"pnl" double precision NOT NULL,
"r" double precision NOT NULL,
"exit_reason" text NOT NULL
);
--> statement-breakpoint
CREATE TABLE "positions" (
"pair" varchar(16) PRIMARY KEY NOT NULL,
"side" text 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,
"initial_stop" double precision NOT NULL,
"stop" double precision NOT NULL,
"trail_extreme" double precision NOT NULL,
"risk_amount" double precision NOT NULL
);
--> statement-breakpoint
CREATE UNIQUE INDEX "decision_logs_pair_bar_ts" ON "decision_logs" USING btree ("pair","bar_ts");

View File

@@ -0,0 +1,466 @@
{
"id": "38fbc5fc-4ef1-4dae-b408-21bcafa513b7",
"prevId": "00b411bc-669e-4667-881c-c9161fa42bb0",
"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": {
"ts": {
"name": "ts",
"type": "timestamp with time zone",
"primaryKey": true,
"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": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.paper_trades": {
"name": "paper_trades",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "serial",
"primaryKey": true,
"notNull": true
},
"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": {}
}
}

View File

@@ -8,6 +8,13 @@
"when": 1781038570957,
"tag": "0000_nifty_brood",
"breakpoints": true
},
{
"idx": 1,
"version": "7",
"when": 1781071452889,
"tag": "0001_certain_omega_red",
"breakpoints": true
}
]
}

View File

@@ -1,4 +1,4 @@
import { doublePrecision, jsonb, pgTable, primaryKey, serial, text, timestamp, varchar } from 'drizzle-orm/pg-core';
import { doublePrecision, integer, jsonb, pgTable, primaryKey, serial, text, timestamp, uniqueIndex, varchar } from 'drizzle-orm/pg-core';
export const candles = pgTable(
'candles',
@@ -14,6 +14,67 @@ export const candles = pgTable(
(t) => [primaryKey({ columns: [t.pair, t.ts] })],
);
export const positions = pgTable('positions', {
pair: varchar('pair', { length: 16 }).primaryKey(),
side: text('side').notNull(), // 'long' | 'short'
qty: doublePrecision('qty').notNull(),
entryTs: timestamp('entry_ts', { withTimezone: true }).notNull(),
entryPrice: doublePrecision('entry_price').notNull(),
entryCost: doublePrecision('entry_cost').notNull(),
initialStop: doublePrecision('initial_stop').notNull(),
stop: doublePrecision('stop').notNull(),
trailExtreme: doublePrecision('trail_extreme').notNull(),
riskAmount: doublePrecision('risk_amount').notNull(),
});
export const paperTrades = pgTable('paper_trades', {
id: serial('id').primaryKey(),
pair: varchar('pair', { length: 16 }).notNull(),
side: text('side').notNull(),
entryTs: timestamp('entry_ts', { withTimezone: true }).notNull(),
entryPrice: doublePrecision('entry_price').notNull(),
exitTs: timestamp('exit_ts', { withTimezone: true }).notNull(),
exitPrice: doublePrecision('exit_price').notNull(),
qty: doublePrecision('qty').notNull(),
pnl: doublePrecision('pnl').notNull(),
r: doublePrecision('r').notNull(),
exitReason: text('exit_reason').notNull(),
});
export const decisionLogs = pgTable(
'decision_logs',
{
id: serial('id').primaryKey(),
pair: varchar('pair', { length: 16 }).notNull(),
barTs: timestamp('bar_ts', { withTimezone: true }).notNull(), // Start der 4h-Bar
signal: text('signal'), // 'long' | null
blockedBy: text('blocked_by'), // Evaluation.blockedBy | 'position_open' | 'max_positions' | Sizing-Block
close: doublePrecision('close').notNull(),
atr: doublePrecision('atr'),
adx: doublePrecision('adx'),
donchianHigh: doublePrecision('donchian_high'),
trendEma: doublePrecision('trend_ema'),
priceAfter4h: doublePrecision('price_after_4h'),
priceAfter24h: doublePrecision('price_after_24h'),
priceAfter72h: doublePrecision('price_after_72h'),
},
(t) => [uniqueIndex('decision_logs_pair_bar_ts').on(t.pair, t.barTs)],
);
export const botState = pgTable('bot_state', {
id: integer('id').primaryKey(), // immer 1
cash: doublePrecision('cash').notNull(),
startCapital: doublePrecision('start_capital').notNull(),
cursorTs: timestamp('cursor_ts', { withTimezone: true }).notNull(), // letzte verarbeitete 15m-Candle
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
});
export const equitySnapshots = pgTable('equity_snapshots', {
ts: timestamp('ts', { withTimezone: true }).primaryKey(), // 4h-Bucket
equity: doublePrecision('equity').notNull(),
cash: doublePrecision('cash').notNull(),
});
export const backtestRuns = pgTable('backtest_runs', {
id: serial('id').primaryKey(),
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),