openpondai/agents/twap-bot
OpenTool app
typescript
import {
DEFAULT_AMOUNT_USD,
DEFAULT_ASSET,
DEFAULT_MARKET_SYMBOL,
DEFAULT_SCHEDULE_CRON,
TEMPLATE_CONFIG_DEFAULTS,
TEMPLATE_CONFIG_ENV_VAR,
TEMPLATE_CONFIG_VERSION,
} from "./defaults";
import { configSchema, TEMPLATE_CONFIG_SCHEMA } from "./schema";
import type { TwapBotConfig } from "./types";
function parseJson(value: string | null | undefined) {
if (!value) return null;
try {
return JSON.parse(value) as unknown;
} catch {
return null;
}
}
export function normalizeBaseAsset(value: string | null | undefined) {
const trimmed = value?.trim().toUpperCase() ?? "";
if (!trimmed) return "";
const withoutDex = trimmed.includes(":")
? trimmed.split(":").slice(1).join(":")
: trimmed;
const base = withoutDex.split(/[-/]/)[0] ?? withoutDex;
return base.trim().toUpperCase();
}
export function normalizeMarketSymbol(value: string | null | undefined) {
const trimmed = value?.trim().toUpperCase() ?? "";
if (!trimmed) return "";
if (trimmed.includes("/")) return trimmed;
if (trimmed.includes("-")) {
const [base, ...rest] = trimmed.split("-");
const quote = rest.join("-").trim();
return base && quote ? `${base}/${quote}` : trimmed;
}
return trimmed;
}
export const TWAP_BOT_TEMPLATE_CONFIG = {
version: TEMPLATE_CONFIG_VERSION,
schema: TEMPLATE_CONFIG_SCHEMA,
defaults: TEMPLATE_CONFIG_DEFAULTS,
envVar: TEMPLATE_CONFIG_ENV_VAR,
};
export function readConfig(): TwapBotConfig {
const parsed = configSchema.safeParse(
parseJson(process.env[TEMPLATE_CONFIG_ENV_VAR]),
);
const input = parsed.success ? parsed.data : {};
const marketSymbol =
normalizeMarketSymbol(input.marketSymbol) ||
DEFAULT_MARKET_SYMBOL;
const asset = normalizeBaseAsset(marketSymbol) || DEFAULT_ASSET;
return {
configVersion: TEMPLATE_CONFIG_VERSION,
platform: "hyperliquid",
signalType: "twap",
allocationMode: "fixed",
asset,
marketSymbol,
amountUsd: input.amountUsd ?? DEFAULT_AMOUNT_USD,
schedule: {
cron: input.schedule?.cron ?? DEFAULT_SCHEDULE_CRON,
enabled:
input.schedule?.enabled ?? TEMPLATE_CONFIG_DEFAULTS.schedule.enabled,
notifyEmail:
input.schedule?.notifyEmail ??
TEMPLATE_CONFIG_DEFAULTS.schedule.notifyEmail,
},
twap: {
side: input.twap?.side ?? TEMPLATE_CONFIG_DEFAULTS.twap.side,
minutes: input.twap?.minutes ?? TEMPLATE_CONFIG_DEFAULTS.twap.minutes,
randomize:
input.twap?.randomize ?? TEMPLATE_CONFIG_DEFAULTS.twap.randomize,
},
execution: {
enabled:
input.execution?.enabled ?? TEMPLATE_CONFIG_DEFAULTS.execution!.enabled,
environment:
input.execution?.environment ??
TEMPLATE_CONFIG_DEFAULTS.execution!.environment,
...(typeof input.execution?.leverage === "number"
? { leverage: input.execution.leverage }
: {}),
},
};
}
export function resolveScheduleConfig(config: TwapBotConfig) {
return config.schedule;
}
export function resolveProfileAssets(config: TwapBotConfig) {
const marketSymbol = (config.marketSymbol || config.asset).trim();
if (!marketSymbol) {
return [];
}
return [
{
venue: "hyperliquid" as const,
chain:
config.execution?.environment === "testnet"
? ("hyperliquid-testnet" as const)
: ("hyperliquid" as const),
assetSymbols: [marketSymbol],
...(typeof config.execution?.leverage === "number"
? { leverage: config.execution.leverage }
: {}),
},
];
}