openpondai/agents/dca-agent
dca-agent
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 {
DCA_AGENT_TEMPLATE_CONFIG,
readConfig,
resolveProfileAssets,
resolveScheduleConfig,
} from "../src/config";
import { buildBacktestDecisionSeries, runDcaAgent } from "../src/dca-agent";
const config = readConfig();
export const schema = backtestDecisionRequestSchema.partial();
type BacktestAwareToolProfile = ToolProfile & {
backtest?: {
mode: "decisions";
};
};
export const profile: BacktestAwareToolProfile = {
description: "Hyperliquid DCA agent.",
category: "strategy",
backtest: {
mode: "decisions",
},
templatePreview: {
subtitle: "Automated fixed-amount DCA into selected Hyperliquid assets.",
description: `Runs on a recurring schedule and buys with a fixed USD amount per cycle.
Supports multiple configured assets with shared budget and allocation controls.
Uses standardized order sizing and execution utilities from OpenTool.
Tracks decision outcomes and execution status through structured strategy events.
Built for steady accumulation and schedule-aware backtest validation.`,
},
schedule: resolveScheduleConfig(config),
assets: resolveProfileAssets(config),
templateConfig: DCA_AGENT_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 {
const result = await runDcaAgent(snapshot);
await store({
source: "dca-agent",
ref: `dca-agent-${Date.now()}`,
status: result.ok ? "info" : "failed",
action: "dca",
metadata: result,
});
return Response.json(result);
} catch (error) {
const metadata = buildFailureMetadata(error);
await store({
source: "dca-agent",
ref: `dca-agent-${Date.now()}`,
status: "failed",
action: "dca",
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();
}