feat: GridBot als zweite Paper-Engine — No-Stop-XRP-Grid live

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-10 07:29:42 +00:00
parent f754b91acd
commit 021049b259
12 changed files with 1267 additions and 17 deletions

View File

@@ -29,6 +29,7 @@ export const positions = pgTable('positions', {
export const paperTrades = pgTable('paper_trades', {
id: serial('id').primaryKey(),
bot: text('bot').notNull().default('trend'), // 'trend' | 'grid'
pair: varchar('pair', { length: 16 }).notNull(),
side: text('side').notNull(),
entryTs: timestamp('entry_ts', { withTimezone: true }).notNull(),
@@ -69,10 +70,38 @@ export const botState = pgTable('bot_state', {
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 equitySnapshots = pgTable(
'equity_snapshots',
{
bot: text('bot').notNull().default('trend'),
ts: timestamp('ts', { withTimezone: true }).notNull(), // 4h-Bucket
equity: doublePrecision('equity').notNull(),
cash: doublePrecision('cash').notNull(),
},
(t) => [primaryKey({ columns: [t.bot, t.ts] })],
);
/** Aktives Grid je Pair (No-Stop-GridBot). */
export const gridState = pgTable('grid_state', {
pair: varchar('pair', { length: 16 }).primaryKey(),
center: doublePrecision('center').notNull(),
spacing: doublePrecision('spacing').notNull(),
lowerBound: doublePrecision('lower_bound').notNull(),
upperBound: doublePrecision('upper_bound').notNull(),
budgetPerLevel: doublePrecision('budget_per_level').notNull(),
activatedTs: timestamp('activated_ts', { withTimezone: true }).notNull(),
});
/** Offene Grid-Lots (Inventar). */
export const gridLots = pgTable('grid_lots', {
id: serial('id').primaryKey(),
pair: varchar('pair', { length: 16 }).notNull(),
levelIdx: integer('level_idx').notNull(),
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(),
});
export const backtestRuns = pgTable('backtest_runs', {