#!/usr/bin/env node /** * [Purpose] * Fast dev smoke test for autotrade flow: * compile -> validate -> start -> heartbeat -> signal -> worker tick -> stop. */ const appUrl = process.env.AUTOTRADE_APP_URL || "http://127.0.0.1:3001"; const bypassToken = process.env.AUTOTRADE_DEV_BYPASS_TOKEN || "autotrade-dev-bypass"; const workerToken = process.env.AUTOTRADE_WORKER_TOKEN || "autotrade-worker-local"; async function main() { // [Step 1] compile strategy const compile = await callApi("/api/autotrade/strategies/compile", { method: "POST", body: { aiMode: "auto", prompt: "Use ORB and VWAP reversion conservatively, prefer hold on uncertainty.", selectedTechniques: ["orb", "vwap_reversion"], confidenceThreshold: 0.65, }, }); // [Step 2] validate risk const validation = await callApi("/api/autotrade/strategies/validate", { method: "POST", body: { cashBalance: 2_000_000, allocationPercent: 10, allocationAmount: 300_000, dailyLossPercent: 2, dailyLossAmount: 30_000, }, }); if (!validation?.validation?.isValid) { throw new Error("validation failed"); } // [Step 3] start session const start = await callApi("/api/autotrade/sessions/start", { method: "POST", headers: { "x-kis-app-key": "dev-app-key", "x-kis-app-secret": "dev-app-secret", "x-kis-account-no": "12345678-01", "x-kis-trading-env": "mock", }, body: { symbol: "005930", leaderTabId: "autotrade-dev-smoke-tab", effectiveAllocationAmount: validation.validation.effectiveAllocationAmount, effectiveDailyLossLimit: validation.validation.effectiveDailyLossLimit, strategySummary: compile.compiledStrategy.summary, }, }); // [Step 4] send heartbeat await callApi("/api/autotrade/sessions/heartbeat", { method: "POST", body: { sessionId: start.session.sessionId, leaderTabId: "autotrade-dev-smoke-tab", }, }); // [Step 5] generate a signal const signal = await callApi("/api/autotrade/signals/generate", { method: "POST", body: { aiMode: "auto", strategy: compile.compiledStrategy, snapshot: { symbol: "005930", currentPrice: 73_000, changeRate: 0.35, open: 72_800, high: 73_100, low: 72_600, tradeVolume: 120_000, accumulatedVolume: 450_000, recentPrices: [72_600, 72_700, 72_800, 72_900, 73_000, 73_050, 73_100], marketDataLatencySec: 1, }, }, }); // [Step 6] worker tick auth/path check await callApi("/api/autotrade/worker/tick", { method: "POST", headers: { "x-autotrade-worker-token": workerToken, }, body: {}, }); // [Step 7] stop session const stop = await callApi("/api/autotrade/sessions/stop", { method: "POST", body: { sessionId: start.session.sessionId, reason: "manual", }, }); if (stop?.session?.runtimeState !== "STOPPED") { throw new Error("stop failed"); } console.log( "[autotrade-dev-smoke] PASS", JSON.stringify( { appUrl, compileProvider: compile?.compiledStrategy?.provider, signal: signal?.signal?.signal, signalSource: signal?.signal?.source, }, null, 2, ), ); } async function callApi(path, options) { const headers = { "content-type": "application/json", "x-autotrade-dev-bypass": bypassToken, ...(options.headers || {}), }; const response = await fetch(`${appUrl}${path}`, { method: options.method, headers, body: options.body ? JSON.stringify(options.body) : undefined, }); const payload = await response.json().catch(() => null); if (!response.ok) { const message = payload?.message || `${path} failed (${response.status})`; throw new Error(message); } return payload; } main().catch((error) => { const message = error instanceof Error ? error.message : "autotrade dev smoke failed"; console.error(`[autotrade-dev-smoke] FAIL: ${message}`); process.exit(1); });