Fix: SurrealDB v2 compatible record IDs for trade history

This commit is contained in:
2026-03-15 20:14:08 -07:00
parent 04bd2fada6
commit 1999377682

View File

@@ -51,6 +51,34 @@ export class PaperEngine {
return this.accounts.get(strategyName);
}
/**
* Generate a short unique ID safe for SurrealDB v2 record keys.
* Avoids colons so we control the full `table:id` format ourselves.
*/
_genId() {
return `pt_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
}
/**
* Extract the raw record key from a SurrealDB id.
* e.g. "paper_positions:pt_123_abc" -> "pt_123_abc"
* "pt_123_abc" -> "pt_123_abc"
*/
_rawId(id) {
if (!id) return id;
const str = typeof id === 'object' && id.id ? String(id.id) : String(id);
const idx = str.indexOf(':');
return idx >= 0 ? str.slice(idx + 1) : str;
}
/**
* Build the full `table:id` string for use in raw queries.
*/
_recordId(id) {
const raw = this._rawId(id);
return raw.startsWith('paper_positions:') ? raw : `paper_positions:⟨${raw}`;
}
async init() {
try {
const states = await db.query('SELECT * FROM paper_strategy_state ORDER BY timestamp DESC');
@@ -96,11 +124,8 @@ export class PaperEngine {
return null;
}
// FIX: Pre-format the ID exactly as SurrealDB expects it to ensure perfect sync
const tradeId = `paper_positions:pt_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
const trade = {
id: tradeId,
id: this._genId(),
strategy: signal.strategy,
ticker: signal.ticker,
side: signal.side.toLowerCase(),
@@ -127,8 +152,7 @@ export class PaperEngine {
acct.openPositions.set(trade.ticker, list);
try {
// Force SurrealDB to use our exact record link ID
await db.create(tradeId, trade);
await db.create('paper_positions', trade);
await this._saveState(acct);
} catch (e) {
console.error('[Paper] DB write error:', e.message);
@@ -176,7 +200,7 @@ export class PaperEngine {
if (won) acct.wins++;
else acct.losses++;
const recordId = pos.id.includes(':') ? pos.id : `paper_positions:${pos.id}`;
const recordId = this._recordId(pos.id);
try {
const updated = await db.query(
@@ -185,12 +209,13 @@ export class PaperEngine {
);
const rows = updated[0] || [];
if (rows.length === 0) {
await db.create(recordId, { ...pos, id: recordId });
// Record vanished from DB — re-insert the full settled trade
await db.create('paper_positions', { ...pos, id: this._rawId(pos.id) });
}
} catch (e) {
console.error('[Paper] Settle DB error:', e.message);
try {
await db.create(recordId, { ...pos, id: recordId });
await db.create('paper_positions', { ...pos, id: this._rawId(pos.id) });
} catch (e2) {
console.error('[Paper] Settle DB fallback error:', e2.message);
}
@@ -264,7 +289,7 @@ export class PaperEngine {
acct.totalPnL -= pos.cost;
acct.losses++;
const recordId = pos.id.includes(':') ? pos.id : `paper_positions:${pos.id}`;
const recordId = this._recordId(pos.id);
try {
const updated = await db.query(
@@ -273,7 +298,7 @@ export class PaperEngine {
);
const rows = updated[0] || [];
if (rows.length === 0) {
await db.create(recordId, { ...pos, id: recordId });
await db.create('paper_positions', { ...pos, id: this._rawId(pos.id) });
}
} catch (e) {
console.error('[Paper] Force-expire DB error:', e.message);
@@ -302,7 +327,7 @@ export class PaperEngine {
pos.settleTime = Date.now();
try {
const recordId = pos.id.includes(':') ? pos.id : `paper_positions:${pos.id}`;
const recordId = this._recordId(pos.id);
await db.query(
`UPDATE ${recordId} SET settled = true, result = $result, pnl = $pnl, settleTime = $settleTime`,
{ result: 'cancelled', pnl: 0, settleTime: pos.settleTime }