/** Cloudflare Worker entry point for the vinext-starter template. */ import { handleImageOptimization, DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES } from "vinext/server/image-optimization"; import handler from "vinext/server/app-router-entry"; import { buildEastmoneyEmtGatewayStatus } from "./eastmoney-emt-gateway"; import { buildCnBrokerSelectionSnapshot } from "./cn-broker-selection"; import { cnTradingSessionSnapshot } from "./cn-trading-session"; import { buildCnResearchEvidenceSnapshot } from "./cn-research-evidence"; import { buildCnAutoOptimizerStatus } from "./cn-auto-optimizer"; interface Env { ASSETS: Fetcher; DB?: D1Database; MINUTE_ARCHIVE?: R2Bucket; MINUTE_ARCHIVE_ADMIN_TOKEN?: string; CN_MINUTE_WAREHOUSE?: DurableObjectNamespace; CN_MINUTE_ARCHIVE?: DurableObjectNamespace; ALPACA_API_KEY_ID?: string; ALPACA_API_SECRET_KEY?: string; PAPER_ORDER_ROUTING_ENABLED?: string; PAPER_ORDER_CONFIRMATION_ID?: string; LIVE_TRADING_ENABLED?: string; LIVE_TRADING_CONFIRMATION_ID?: string; ALPACA_LIVE_API_KEY_ID?: string; ALPACA_LIVE_API_SECRET_KEY?: string; LIVE_TRADING_ENGINE?: DurableObjectNamespace; EASTMONEY_EMT_TRIAL_APPROVED?: string; EASTMONEY_EMT_CLIENT_ID?: string; EASTMONEY_EMT_SOFTWARE_KEY?: string; EASTMONEY_EMT_TRADER_SERVER?: string; EASTMONEY_EMT_ACCOUNT_ID?: string; EASTMONEY_EMT_PASSWORD?: string; EASTMONEY_EMT_WINDOWS_RELAY_URL?: string; IMAGES: { input(stream: ReadableStream): { transform(options: Record): { output(options: { format: string; quality: number }): Promise<{ response(): Response }>; }; }; }; } export { SimulationEngine } from "./simulation-engine"; export { LiveTradingEngine } from "./live-trading-engine"; export { CnMinuteWarehouse } from "./cn-minute-warehouse"; export { CnMinuteArchiveShard } from "./cn-minute-warehouse"; interface ExecutionContext { waitUntil(promise: Promise): void; passThroughOnException(): void; } function liveEngineStatus() { const now = new Date().toISOString(); const session = cnTradingSessionSnapshot(now); return { version: "cn-three-sleeve-risk-adjusted-v10", mode: "LOCKED" as const, runtimeArmed: false, liveCredentialsConfigured: false, marketCredentialsConfigured: true, autonomousLoop: "CN_SESSION_AWARE_DURABLE_OBJECT_ALARM", cycleSeconds: 2, initializedAt: null, lastCycleAt: null, lastSuccessAt: null, lastError: null, haltReason: "CN_BROKER_ADAPTER_REQUIRED", killLatched: false, lastDecision: session.strategyEvaluationEnabled ? "交易日连续竞价时段:A股、ETF与可转债三策略镜像自动评估;真实券商委托保持锁定" : `当前${session.phase}:自动等待下一交易窗口;真实券商委托保持锁定`, ordersSubmitted: 0, ordersToday: 0, account: { connected: false, status: "BROKER_NOT_CONNECTED", currency: "CNY", positionCount: 0, grossExposurePct: 0, dailyReturn: 0, drawdown: 0, sinceActivationReturn: 0 }, clock: { isOpen: session.strategyEvaluationEnabled, timestamp: now, nextOpen: session.nextTransitionAt, phase: session.phase, isTradingDay: session.isTradingDay, entriesAllowed: session.entriesAllowed, exitsAllowed: session.exitsAllowed, flattenOnly: session.flattenOnly, calendarValidThrough: session.calendarValidThrough, }, riskLimits: { maxSymbolWeight: 0.12, maxGrossWeight: 0.25, dailyLossLimit: 0.0035, maxDrawdown: 0.015, maxOrdersPerDay: 6, maxOrdersPerSymbolDay: 2, orderTimeoutSeconds: 15 }, recentEvents: [], publicControlSurface: "READ_ONLY" as const, }; } // Image security config. SVG sources with .svg extension auto-skip the // optimization endpoint on the client side (served directly, no proxy). // To route SVGs through the optimizer (with security headers), set // dangerouslyAllowSVG: true in next.config.js and uncomment below: // const imageConfig: ImageConfig = { dangerouslyAllowSVG: true }; const worker = { async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise { const url = new URL(request.url); if (url.pathname.startsWith("/api/internal/minute-archive/")) { const expected = env.MINUTE_ARCHIVE_ADMIN_TOKEN; const supplied = request.headers.get("Authorization"); if (!expected || supplied !== `Bearer ${expected}`) { return Response.json({ error: "not found" }, { status: 404 }); } if (!env.MINUTE_ARCHIVE) { return Response.json({ error: "minute archive unavailable" }, { status: 503 }); } const key = url.searchParams.get("key") ?? ""; if (!/^(raw|manifests|system)\//.test(key) || key.includes("..")) { return Response.json({ error: "invalid object key" }, { status: 400 }); } if (url.pathname === "/api/internal/minute-archive/object") { if (request.method === "HEAD") { const object = await env.MINUTE_ARCHIVE.head(key); if (!object) return new Response(null, { status: 404 }); const headers = new Headers({ "Content-Length": String(object.size), ETag: object.httpEtag, "X-R2-Uploaded": object.uploaded.toISOString(), }); for (const [name, value] of Object.entries(object.customMetadata ?? {})) headers.set(`X-R2-Meta-${name}`, value); return new Response(null, { status: 200, headers }); } if (request.method === "GET") { const object = await env.MINUTE_ARCHIVE.get(key, { range: request.headers }); if (!object) return new Response(null, { status: 404 }); const headers = new Headers({ ETag: object.httpEtag, "Accept-Ranges": "bytes" }); object.writeHttpMetadata(headers); if ("range" in object && object.range) { const range = object.range as { offset: number; length: number }; headers.set("Content-Range", `bytes ${range.offset}-${range.offset + range.length - 1}/${object.size}`); headers.set("Content-Length", String(range.length)); return new Response(object.body, { status: 206, headers }); } headers.set("Content-Length", String(object.size)); return new Response(object.body, { headers }); } if (request.method === "PUT") { if (!request.body) return Response.json({ error: "missing body" }, { status: 400 }); const stored = await env.MINUTE_ARCHIVE.put(key, request.body, { httpMetadata: { contentType: request.headers.get("Content-Type") ?? "application/octet-stream" }, }); return Response.json({ key: stored.key, size: stored.size, etag: stored.httpEtag }); } if (request.method === "DELETE") { await env.MINUTE_ARCHIVE.delete(key); return new Response(null, { status: 204 }); } } if (url.pathname === "/api/internal/minute-archive/multipart/init" && request.method === "POST") { const payload = await request.json() as { contentType?: string; metadata?: Record }; const upload = await env.MINUTE_ARCHIVE.createMultipartUpload(key, { httpMetadata: { contentType: payload.contentType ?? "application/octet-stream" }, customMetadata: payload.metadata ?? {}, }); return Response.json({ key: upload.key, uploadId: upload.uploadId }); } if (url.pathname === "/api/internal/minute-archive/multipart/part" && request.method === "PUT") { const uploadId = url.searchParams.get("uploadId") ?? ""; const partNumber = Number(url.searchParams.get("partNumber")); if (!uploadId || !Number.isInteger(partNumber) || partNumber < 1 || partNumber > 10_000 || !request.body) { return Response.json({ error: "invalid multipart part" }, { status: 400 }); } const upload = env.MINUTE_ARCHIVE.resumeMultipartUpload(key, uploadId); const part = await upload.uploadPart(partNumber, request.body); return Response.json({ partNumber: part.partNumber, etag: part.etag }); } if (url.pathname === "/api/internal/minute-archive/multipart/complete" && request.method === "POST") { const payload = await request.json() as { uploadId?: string; parts?: Array<{ partNumber: number; etag: string }> }; if (!payload.uploadId || !payload.parts?.length) return Response.json({ error: "invalid completion payload" }, { status: 400 }); const upload = env.MINUTE_ARCHIVE.resumeMultipartUpload(key, payload.uploadId); const object = await upload.complete(payload.parts); return Response.json({ key: object.key, size: object.size, etag: object.httpEtag }); } if (url.pathname === "/api/internal/minute-archive/multipart/abort" && request.method === "POST") { const payload = await request.json() as { uploadId?: string }; if (!payload.uploadId) return Response.json({ error: "invalid abort payload" }, { status: 400 }); await env.MINUTE_ARCHIVE.resumeMultipartUpload(key, payload.uploadId).abort(); return Response.json({ aborted: true }); } return Response.json({ error: "unsupported archive operation" }, { status: 405 }); } if (url.pathname === "/_health") { const runtimeEnv = env as unknown as Record; return Response.json({ ok: true, service: "hunter-quant-portal", simulationEngine: Boolean(runtimeEnv.SIMULATION_ENGINE), liveTradingEngine: Boolean(runtimeEnv.LIVE_TRADING_ENGINE), minuteWarehouse: Boolean(runtimeEnv.CN_MINUTE_WAREHOUSE), minuteArchive: Boolean(runtimeEnv.MINUTE_ARCHIVE), minuteArchiveShards: Boolean(runtimeEnv.CN_MINUTE_ARCHIVE), minuteCatalog: Boolean(runtimeEnv.DB), marketDataConfigured: Boolean(runtimeEnv.ALPACA_API_KEY_ID && runtimeEnv.ALPACA_API_SECRET_KEY), assets: Boolean(runtimeEnv.ASSETS), }); } if (url.pathname === "/api/simulation/stream") { const runtimeEnv = env as unknown as { SIMULATION_ENGINE?: DurableObjectNamespace }; if (!runtimeEnv.SIMULATION_ENGINE) { return Response.json({ error: "simulation engine unavailable" }, { status: 503 }); } const id = runtimeEnv.SIMULATION_ENGINE.idFromName("quantpilot-primary"); const upstreamRequest = new Request("https://simulation-engine/stream", request); return runtimeEnv.SIMULATION_ENGINE.get(id).fetch(upstreamRequest); } if (url.pathname === "/api/universe") { if (request.method !== "GET") return Response.json({ error: "read-only endpoint" }, { status: 405 }); const runtimeEnv = env as unknown as { SIMULATION_ENGINE?: DurableObjectNamespace }; if (!runtimeEnv.SIMULATION_ENGINE) return Response.json({ error: "universe registry unavailable" }, { status: 503 }); const id = runtimeEnv.SIMULATION_ENGINE.idFromName("quantpilot-primary"); const upstreamUrl = new URL("https://simulation-engine/universe"); upstreamUrl.search = url.search; return runtimeEnv.SIMULATION_ENGINE.get(id).fetch(new Request(upstreamUrl, request)); } if (url.pathname === "/api/forward" || url.pathname === "/api/forward/history") { if (request.method !== "GET") return Response.json({ error: "read-only endpoint" }, { status: 405 }); const runtimeEnv = env as unknown as { SIMULATION_ENGINE?: DurableObjectNamespace }; if (!runtimeEnv.SIMULATION_ENGINE) return Response.json({ error: "forward shadow engine unavailable" }, { status: 503 }); const id = runtimeEnv.SIMULATION_ENGINE.idFromName("quantpilot-primary"); const upstreamUrl = new URL(`https://simulation-engine${url.pathname.replace("/api", "")}`); upstreamUrl.search = url.search; return runtimeEnv.SIMULATION_ENGINE.get(id).fetch(new Request(upstreamUrl, request)); } if (url.pathname === "/api/warehouse/status") { if (request.method !== "GET") return Response.json({ error: "read-only endpoint" }, { status: 405 }); const runtimeEnv = env as unknown as { CN_MINUTE_WAREHOUSE?: DurableObjectNamespace }; if (!runtimeEnv.CN_MINUTE_WAREHOUSE) return Response.json({ error: "minute warehouse unavailable" }, { status: 503 }); const id = runtimeEnv.CN_MINUTE_WAREHOUSE.idFromName("cn-minute-warehouse-primary"); return runtimeEnv.CN_MINUTE_WAREHOUSE.get(id).fetch(new Request("https://minute-warehouse/status", request)); } if (url.pathname === "/api/data-lake/status") { if (request.method !== "GET") return Response.json({ error: "read-only endpoint" }, { status: 405 }); if (!env.MINUTE_ARCHIVE) return Response.json({ error: "minute data lake unavailable", realOrders: 0 }, { status: 503 }); const object = await env.MINUTE_ARCHIVE.get("system/minute-data-lake-progress.json"); if (!object) { return Response.json({ schemaVersion: "quant-atlas-r2-minute-lake-progress-v1", generatedAt: new Date().toISOString(), phase: "initializing", private: true, deletionReady: false, realOrders: 0, }, { headers: { "Cache-Control": "no-store", "Access-Control-Allow-Origin": "*" } }); } const progress = await object.json>(); const deletionReady = progress.phase === "verified-final" && progress.deletionReady === true; return Response.json({ ...progress, deletionReady, realOrders: 0 }, { headers: { "Cache-Control": "no-store", "Access-Control-Allow-Origin": "*" }, }); } if (url.pathname === "/api/execution/status") { if (request.method !== "GET") return Response.json({ error: "read-only endpoint" }, { status: 405 }); const liveEngine = liveEngineStatus(); const eastmoneyGateway = buildEastmoneyEmtGatewayStatus(env as unknown as Record); const brokerSelection = buildCnBrokerSelectionSnapshot(); const researchEvidence = buildCnResearchEvidenceSnapshot(); const autoOptimizer = buildCnAutoOptimizerStatus(); return Response.json({ generatedAt: new Date().toISOString(), publicControlSurface: "READ_ONLY", targetMarket: { country: "中国", venue: "沪深北A股、沪深场内基金与沪深可转债", currency: "CNY", settlement: "A股个股T+1,次日起按趋势、利润保护与风险动态退出;经规则核验的ETF与可转债采用T+0日内策略", symbols: ["510900", "511010", "518880"], realBrokerStatus: "LOCKED", programTradingReportRequired: true, }, paper: { checkedAt: new Date().toISOString(), connected: false, mode: "retired-us-reference", reason: "实盘策略已切换至A股场内T+0基金,美股Paper账户不再参与该页面", account: null, clock: null, positions: [], orders: [] }, paperRouter: { status: "LOCKED", orderRouteExposed: false, checks: { credentialsConfigured: false, runtimeArmed: false, confirmationConfigured: false }, }, liveGateway: { status: "LOCKED", orderRouteExposed: false, checks: { explicitRuntimeArm: false, actionConfirmation: false, liveBrokerCredentials: false }, }, liveEngine, eastmoneyGateway, brokerSelection, researchEvidence, autoOptimizer, automatedExecution: { mode: "SHADOW_AUTOMATIC__LIVE_LOCKED", scheduler: "DURABLE_OBJECT_ALARM", unattended: true, session: cnTradingSessionSnapshot(), policy: { evaluateDuring: "交易日 09:30–11:30、13:00–15:00", newEntriesDuring: "10:00–11:30、13:00–14:35", flattenOnlyDuring: "14:55–15:00", openingAuctionOrders: false, lunchOrders: false, holidayOrders: false, disconnectBehavior: "FAIL_CLOSED", }, realBrokerOrdersEnabled: false, realOrdersSubmitted: 0, }, safety: { publicMutationEndpoints: 0, liveOrdersSubmittedByPortal: liveEngine.ordersSubmitted, paperOrdersSubmittedByPortal: 0, allowedSymbols: ["510900", "511010", "518880"], orderType: "cn-three-sleeve-limit-only-shadow", maxPaperOrderNotional: 0, maxLiveOrderEquityFraction: 0.12, maxLiveGrossEquityFraction: 0.25, }, }, { headers: { "Cache-Control": "no-store", "Access-Control-Allow-Origin": "*" } }); } if ( url.pathname.startsWith("/assets/") || url.pathname.startsWith("/artifacts/") || url.pathname.startsWith("/_vinext_fonts/") || ["/favicon.svg", "/og.png", "/file.svg", "/globe.svg", "/window.svg"].includes(url.pathname) ) { return env.ASSETS.fetch(request); } if (url.pathname === "/_vinext/image") { const allowedWidths = [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES]; return handleImageOptimization(request, { fetchAsset: (path) => env.ASSETS.fetch(new Request(new URL(path, request.url))), transformImage: async (body, { width, format, quality }) => { const result = await env.IMAGES.input(body).transform(width > 0 ? { width } : {}).output({ format, quality }); return result.response(); }, }, allowedWidths); } try { return await handler.fetch(request, env, ctx); } catch (error) { return Response.json({ error: "portal-worker-failed", detail: error instanceof Error ? error.message : String(error), }, { status: 500 }); } }, }; export default worker;