1Branch0Tags
GL
glucryptoAdopt opentool 0.19.8
e1b57112 months ago13Commits
typescript
import { store } from "opentool/store"; import type { ToolProfile } from "opentool"; import { HyperliquidApiError } from "opentool/adapters/hyperliquid"; import { buildBacktestDecisionSeriesInput, backtestDecisionRequestSchema, resolveBacktestMode, } from "opentool/backtest"; import { readConfig, resolveProfileAssets, resolveScheduleConfig, RSI_SIGNAL_BOT_TEMPLATE_CONFIG, } from "../src/config"; import { buildBacktestDecisionSeries, runSignalBot, } from "../src/rsi-signal-bot"; const config = readConfig(); export const schema = backtestDecisionRequestSchema.partial(); type BacktestAwareToolProfile = ToolProfile & { backtest?: { mode: "decisions"; }; }; export const profile: BacktestAwareToolProfile = { description: "Hyperliquid RSI signal bot.", category: "strategy", backtest: { mode: "decisions", }, templatePreview: { subtitle: "Hyperliquid RSI execution with schedule and risk controls.", description: `Scans a selected asset on a fixed schedule and evaluates RSI thresholds. Combines RSI signals with risk checks before any order decision. Places standardized Hyperliquid orders using fixed USD budget controls. Includes allocation and exposure settings to keep runs bounded. Supports backtest decision series generation for RSI strategy validation.`, }, schedule: resolveScheduleConfig(config), assets: resolveProfileAssets(config), templateConfig: RSI_SIGNAL_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 } : {}), }; } 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 = parsed.data; const decisionSeries = await buildBacktestDecisionSeries({ config: snapshot, ...buildBacktestDecisionSeriesInput(backtest), }); return Response.json({ ok: true, mode: normalizedMode, backtest: decisionSeries, }); } 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" }, }); } } try { const result = await runSignalBot(snapshot); await store({ source: "rsi-signal-bot", ref: `rsi-signal-bot-${Date.now()}`, status: result.ok ? "info" : "failed", action: "signal", metadata: result, }); return Response.json(result); } catch (error) { const metadata = buildFailureMetadata(error); await store({ source: "rsi-signal-bot", ref: `rsi-signal-bot-${Date.now()}`, status: "failed", action: "signal", metadata, }); return new Response(JSON.stringify(metadata), { status: 400, headers: { "content-type": "application/json" }, }); } }