1Branch0Tags
GL
glucryptoReserve only trade targets in price trigger profil...
4a63a6312 days ago20Commits
typescript
import { store } from "opentool/store"; import type { ToolProfile } from "opentool"; import { HyperliquidApiError } from "opentool/adapters/hyperliquid"; import { backtestDecisionRequestSchema, buildBacktestDecisionSeriesInput, resolveBacktestMode, } from "opentool/backtest"; import { PRICE_TRIGGER_BOT_TEMPLATE_CONFIG, readConfig, resolveProfileAssets, resolveScheduleConfig, } from "../src/config"; import { buildBacktestDecisionSeries, runPriceTriggerBot, } from "../src/price-trigger-bot"; const config = readConfig(); export const schema = backtestDecisionRequestSchema.partial(); type BacktestAwareToolProfile = ToolProfile & { backtest?: { mode: "decisions"; }; }; export const profile: BacktestAwareToolProfile = { description: "Hyperliquid price trigger bot.", category: "strategy", backtest: { mode: "decisions", }, templatePreview: { subtitle: "Rule-based Hyperliquid price trigger strategy with multi-asset action baskets.", description: `Evaluates explicit above, below, and crossing rules on a fixed schedule. Each rule can watch one market and execute one or more weighted trade targets. Supports compact fixed-amount sizing with optional live Hyperliquid execution. Designed for threshold-driven rule automation rather than indicator-based signals.`, }, schedule: resolveScheduleConfig(config), assets: resolveProfileAssets(config), templateConfig: PRICE_TRIGGER_BOT_TEMPLATE_CONFIG, }; function buildFailureMetadata(error: unknown) { const message = error instanceof Error ? error.message : "unknown"; return { ok: false, error: message, errorType: error instanceof Error ? error.name : typeof error, ...(error instanceof HyperliquidApiError ? { errorResponse: error.response } : {}), }; } async function executeLive() { const snapshot = readConfig(); try { return Response.json(await runPriceTriggerBot(snapshot)); } catch (error) { const metadata = buildFailureMetadata(error); await store({ source: "price-trigger-bot", ref: `price-trigger-bot-${Date.now()}`, status: "failed", action: "signal", metadata, }); return new Response(JSON.stringify(metadata), { status: 400, headers: { "content-type": "application/json" }, }); } } export async function POST(req: Request) { const snapshot = readConfig(); const payload = await req.json().catch(() => null); const mode = payload && typeof payload === "object" && !Array.isArray(payload) ? (payload as { mode?: unknown }).mode : null; const normalizedMode = resolveBacktestMode(mode); if (normalizedMode === "backtest_decisions") { try { const parsed = backtestDecisionRequestSchema.safeParse(payload); if (!parsed.success) { return new Response( JSON.stringify({ ok: false, error: parsed.error.issues[0]?.message ?? "invalid backtest request payload", }), { status: 400, headers: { "content-type": "application/json" }, }, ); } const backtest = await buildBacktestDecisionSeries({ config: snapshot, ...buildBacktestDecisionSeriesInput(parsed.data), }); return Response.json({ ok: true, mode: normalizedMode, backtest, }); } catch (error) { const message = error instanceof Error ? error.message : "unknown"; return new Response(JSON.stringify({ ok: false, error: message }), { status: 400, headers: { "content-type": "application/json" }, }); } } return executeLive(); }