openpondai/agents/price-trigger-bot
OpenTool app
1Branch0Tags
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();
}