openpondai/agents/hip4-edge-bot
OpenTool app
typescript
import { buildHyperliquidProfileAssets } from "opentool/adapters/hyperliquid";
import {
DEFAULT_AMOUNT_USD,
DEFAULT_SERIES_PERIOD,
DEFAULT_SERIES_SETTLEMENT_SLOT,
DEFAULT_UNDERLYING,
TEMPLATE_CONFIG_DEFAULTS,
TEMPLATE_CONFIG_ENV_VAR,
TEMPLATE_CONFIG_VERSION,
} from "./defaults";
import { configSchema, TEMPLATE_CONFIG_SCHEMA } from "./schema";
import type {
Hip4EdgeBotConfig,
Hip4EdgeSide,
Hip4ModelWindow,
} from "./types";
function parseJson(value: string | null | undefined) {
if (!value) return null;
try {
return JSON.parse(value) as unknown;
} catch {
return null;
}
}
function clamp(value: number, min: number, max: number) {
return Math.min(max, Math.max(min, value));
}
export function normalizeUnderlying(value: string | null | undefined) {
const trimmed = value?.trim().toUpperCase() ?? "";
return trimmed || DEFAULT_UNDERLYING;
}
function normalizeKeyPart(value: string) {
return value
.trim()
.toLowerCase()
.replace(/[^a-z0-9._-]+/g, "-")
.replace(/^-+|-+$/g, "");
}
function normalizeOptionalSeriesKey(value: string | null | undefined) {
const trimmed = value?.trim();
return trimmed || null;
}
function buildDefaultSeriesKey(params: {
environment: "mainnet" | "testnet";
underlying: string;
}) {
return [
"hip4",
normalizeKeyPart(params.environment),
normalizeKeyPart(params.underlying),
"price-binary",
"above",
DEFAULT_SERIES_PERIOD,
`settles-${DEFAULT_SERIES_SETTLEMENT_SLOT}`,
].join(":");
}
function normalizeAllowedSides(value: Hip4EdgeSide[] | undefined) {
const sides = new Set<Hip4EdgeSide>();
for (const side of value ?? TEMPLATE_CONFIG_DEFAULTS.risk.allowedSides) {
if (side === "yes" || side === "no") sides.add(side);
}
return sides.size > 0 ? Array.from(sides) : TEMPLATE_CONFIG_DEFAULTS.risk.allowedSides;
}
export const HIP4_EDGE_BOT_TEMPLATE_CONFIG = {
version: TEMPLATE_CONFIG_VERSION,
schema: TEMPLATE_CONFIG_SCHEMA,
defaults: TEMPLATE_CONFIG_DEFAULTS,
envVar: TEMPLATE_CONFIG_ENV_VAR,
};
export function readConfig(): Hip4EdgeBotConfig {
const parsed = configSchema.safeParse(parseJson(process.env[TEMPLATE_CONFIG_ENV_VAR]));
const input = parsed.success ? parsed.data : {};
const amountUsd = input.amountUsd ?? DEFAULT_AMOUNT_USD;
const maxPerRunUsd = input.maxPerRunUsd ?? amountUsd;
const underlying = normalizeUnderlying(input.model?.underlying);
const environment =
input.execution?.environment ?? TEMPLATE_CONFIG_DEFAULTS.execution.environment;
const seriesKey =
normalizeOptionalSeriesKey(input.model?.seriesKey) ??
buildDefaultSeriesKey({ environment, underlying });
return {
configVersion: TEMPLATE_CONFIG_VERSION,
platform: "hyperliquid",
signalType: "hip4-edge",
allocationMode: "fixed",
amountUsd,
maxPerRunUsd,
schedule: {
cron: input.schedule?.cron ?? TEMPLATE_CONFIG_DEFAULTS.schedule.cron,
enabled: input.schedule?.enabled ?? TEMPLATE_CONFIG_DEFAULTS.schedule.enabled,
notifyEmail:
input.schedule?.notifyEmail ?? TEMPLATE_CONFIG_DEFAULTS.schedule.notifyEmail,
},
model: {
underlying,
seriesKey,
selectedModel:
(input.model?.selectedModel as Hip4ModelWindow | undefined) ??
TEMPLATE_CONFIG_DEFAULTS.model.selectedModel,
depthUsd: Math.max(
input.model?.depthUsd ?? TEMPLATE_CONFIG_DEFAULTS.model.depthUsd,
amountUsd,
),
includeExpired:
input.model?.includeExpired ?? TEMPLATE_CONFIG_DEFAULTS.model.includeExpired,
},
risk: {
minEdgePct: input.risk?.minEdgePct ?? TEMPLATE_CONFIG_DEFAULTS.risk.minEdgePct,
minRoiPct: input.risk?.minRoiPct ?? TEMPLATE_CONFIG_DEFAULTS.risk.minRoiPct,
maxAsk: clamp(input.risk?.maxAsk ?? TEMPLATE_CONFIG_DEFAULTS.risk.maxAsk, 0.01, 1),
minAskDepthUsd:
input.risk?.minAskDepthUsd ?? TEMPLATE_CONFIG_DEFAULTS.risk.minAskDepthUsd,
...(typeof input.risk?.maxHoursToExpiry === "number"
? { maxHoursToExpiry: input.risk.maxHoursToExpiry }
: {}),
allowedSides: normalizeAllowedSides(input.risk?.allowedSides),
maxTradesPerRun:
input.risk?.maxTradesPerRun ?? TEMPLATE_CONFIG_DEFAULTS.risk.maxTradesPerRun,
oneTradePerRoundSide:
input.risk?.oneTradePerRoundSide ??
TEMPLATE_CONFIG_DEFAULTS.risk.oneTradePerRoundSide,
},
execution: {
enabled: input.execution?.enabled ?? TEMPLATE_CONFIG_DEFAULTS.execution.enabled,
environment,
slippageBps:
input.execution?.slippageBps ?? TEMPLATE_CONFIG_DEFAULTS.execution.slippageBps,
},
};
}
export function resolveScheduleConfig(config: Hip4EdgeBotConfig) {
return config.schedule;
}
export function resolveProfileAssets(config: Hip4EdgeBotConfig) {
return buildHyperliquidProfileAssets({
environment: config.execution.environment,
assets: [{ assetSymbols: [config.model.underlying] }],
});
}