diff --git a/drizzle/0003_kind_sheva_callister.sql b/drizzle/0003_kind_sheva_callister.sql new file mode 100644 index 0000000..bb2f4e9 --- /dev/null +++ b/drizzle/0003_kind_sheva_callister.sql @@ -0,0 +1,30 @@ +CREATE TABLE "trump_events" ( + "id" serial PRIMARY KEY NOT NULL, + "source" text NOT NULL, + "token" text NOT NULL, + "instrument" varchar(16), + "event_ts" timestamp with time zone NOT NULL, + "ref" text NOT NULL, + "notional_usd" double precision, + "consumed_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "trump_positions" ( + "pair" varchar(16) PRIMARY KEY 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, + "exit_due_ts" timestamp with time zone NOT NULL, + "event_id" integer NOT NULL +); +--> statement-breakpoint +CREATE TABLE "trump_signal_state" ( + "id" integer PRIMARY KEY NOT NULL, + "last_block" integer NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE UNIQUE INDEX "trump_events_source_ref_token" ON "trump_events" USING btree ("source","ref","token"); \ No newline at end of file diff --git a/drizzle/meta/0003_snapshot.json b/drizzle/meta/0003_snapshot.json new file mode 100644 index 0000000..9d97d24 --- /dev/null +++ b/drizzle/meta/0003_snapshot.json @@ -0,0 +1,793 @@ +{ + "id": "5990c015-8158-412e-8e40-28370581d6d8", + "prevId": "2e351b16-12b9-4c9a-a777-29cd5cf06dd4", + "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 + }, + "public.trump_events": { + "name": "trump_events", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "instrument": { + "name": "instrument", + "type": "varchar(16)", + "primaryKey": false, + "notNull": false + }, + "event_ts": { + "name": "event_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ref": { + "name": "ref", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notional_usd": { + "name": "notional_usd", + "type": "double precision", + "primaryKey": false, + "notNull": false + }, + "consumed_at": { + "name": "consumed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "trump_events_source_ref_token": { + "name": "trump_events_source_ref_token", + "columns": [ + { + "expression": "source", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "ref", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "token", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trump_positions": { + "name": "trump_positions", + "schema": "", + "columns": { + "pair": { + "name": "pair", + "type": "varchar(16)", + "primaryKey": true, + "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 + }, + "exit_due_ts": { + "name": "exit_due_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "event_id": { + "name": "event_id", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.trump_signal_state": { + "name": "trump_signal_state", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true + }, + "last_block": { + "name": "last_block", + "type": "integer", + "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 + } + }, + "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 e5cd5b5..bed8722 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1781076227862, "tag": "0002_burly_joystick", "breakpoints": true + }, + { + "idx": 3, + "version": "7", + "when": 1781250542971, + "tag": "0003_kind_sheva_callister", + "breakpoints": true } ] } \ No newline at end of file diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index abd40a1..78eeedf 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -111,3 +111,39 @@ export const backtestRuns = pgTable('backtest_runs', { config: jsonb('config').notNull(), result: jsonb('result').notNull(), }); + +/** „Trump kauft"-Events (on-chain Transfers in Watchlist-Wallets, Truth-Social-Erwähnungen). */ +export const trumpEvents = pgTable( + 'trump_events', + { + id: serial('id').primaryKey(), + source: text('source').notNull(), // 'onchain' | 'truth' + token: text('token').notNull(), // Symbol, z. B. 'WBTC', 'TRX' + instrument: varchar('instrument', { length: 16 }), // null = nicht auf Crypto.com handelbar + eventTs: timestamp('event_ts', { withTimezone: true }).notNull(), + ref: text('ref').notNull(), // Tx-Hash bzw. Post-URL + notionalUsd: doublePrecision('notional_usd'), // nur onchain + consumedAt: timestamp('consumed_at', { withTimezone: true }), // null = noch nicht von der Engine verarbeitet + createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(), + }, + (t) => [uniqueIndex('trump_events_source_ref_token').on(t.source, t.ref, t.token)], +); + +/** Offene Positionen der Trump-Engine (Zeit-Exit, kein Stop). */ +export const trumpPositions = pgTable('trump_positions', { + pair: varchar('pair', { length: 16 }).primaryKey(), + qty: doublePrecision('qty').notNull(), + entryTs: timestamp('entry_ts', { withTimezone: true }).notNull(), + entryPrice: doublePrecision('entry_price').notNull(), + entryCost: doublePrecision('entry_cost').notNull(), + riskAmount: doublePrecision('risk_amount').notNull(), // = entryCost → r = Return auf Einsatz + exitDueTs: timestamp('exit_due_ts', { withTimezone: true }).notNull(), + eventId: integer('event_id').notNull(), +}); + +/** Cursor des On-Chain-Pollers (letzter vollständig gescannter Block). */ +export const trumpSignalState = pgTable('trump_signal_state', { + id: integer('id').primaryKey(), // immer 1 + lastBlock: integer('last_block').notNull(), + updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(), +}); diff --git a/src/server/engine/portfolio.ts b/src/server/engine/portfolio.ts index 7e81c00..c40be8b 100644 --- a/src/server/engine/portfolio.ts +++ b/src/server/engine/portfolio.ts @@ -31,7 +31,7 @@ export interface ClosedTrade { qty: number; pnl: number; r: number; - exitReason: 'trailing_stop' | 'end_of_data' | 'rotation' | 'grid_tp' | 'grid_stop'; + exitReason: 'trailing_stop' | 'end_of_data' | 'rotation' | 'grid_tp' | 'grid_stop' | 'trump_hold'; side: 'long' | 'short'; }