openpondai/agents/rsi-signal-bot
OpenTool app
typescript
import { store } from "opentool/store";
import type { ToolProfile } from "opentool";
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,
};
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 message = error instanceof Error ? error.message : "unknown";
await store({
source: "rsi-signal-bot",
ref: `rsi-signal-bot-${Date.now()}`,
status: "failed",
action: "signal",
metadata: { ok: false, error: message },
});
return new Response(JSON.stringify({ ok: false, error: message }), {
status: 400,
headers: { "content-type": "application/json" },
});
}
}