openpondai/agents/signal-bot
OpenTool app
typescript
import { z } from "zod";
import { LIMITS } from "./defaults";
export const indicatorSchema = z.enum([
"rsi",
"macd",
"bb",
"sma",
"ema",
"ma-cross",
"donchian",
]);
export const TEMPLATE_CONFIG_SCHEMA = {
type: "object",
"x-budget": {
modeField: "allocationMode",
defaultMode: "fixed",
title: "Budget & allocation",
description: "Directional signal starters use fixed USD sizing.",
modes: {
fixed: {
fields: ["indicators", "amountUsd"],
},
},
},
properties: {
configVersion: {
type: "number",
title: "Config version",
description: "Internal version for signal-bot defaults.",
readOnly: true,
"x-hidden": true,
"x-section": "Meta",
"x-order": 1000,
},
platform: {
type: "string",
enum: ["hyperliquid"],
title: "Platform",
description: "Execution venue for this strategy.",
readOnly: true,
"x-hidden": true,
"x-section": "Meta",
"x-order": 1001,
},
signalType: {
type: "string",
enum: ["price"],
title: "Strategy type",
description: "Internal strategy identifier.",
"x-enumLabels": ["Price"],
readOnly: true,
"x-hidden": true,
"x-section": "Meta",
"x-order": 1002,
},
asset: {
type: "string",
title: "Asset",
description:
"Default Hyperliquid perp symbol. Use execution symbol for spot pairs (BASE-QUOTE or BASE/QUOTE) or HIP-3 symbols (dex:BASE).",
"x-format": "asset",
"x-section": "Strategy",
"x-order": 1,
},
indicators: {
type: "array",
title: "Indicators",
description: "Indicators to evaluate for price-based signals.",
items: {
type: "string",
enum: ["rsi", "macd", "bb", "sma", "ema", "ma-cross", "donchian"],
"x-enumLabels": [
"RSI",
"MACD",
"Bollinger Bands",
"SMA",
"EMA",
"MA Cross",
"Donchian",
],
},
"x-section": "Strategy",
"x-order": 2,
},
allocationMode: {
type: "string",
enum: ["fixed"],
title: "Allocation mode",
description: "Position sizing method.",
"x-enumLabels": ["Fixed USD"],
readOnly: true,
"x-hidden": true,
"x-section": "Risk",
"x-order": 1,
},
amountUsd: {
type: "number",
title: "Fixed amount",
description: "USD notional per run when allocation mode is fixed.",
minimum: 1,
"x-unit": "USD",
"x-format": "currency",
"x-step": 1,
"x-section": "Risk",
"x-order": 2,
},
schedule: {
type: "object",
title: "Schedule",
description: "Run toggles and notifications.",
"x-section": "Schedule",
properties: {
cron: {
type: "string",
title: "Cron expression",
description: "Cron expression for automated runs.",
"x-order": 1,
},
enabled: {
type: "boolean",
title: "Enabled",
description: "Enable scheduled runs.",
"x-order": 2,
},
notifyEmail: {
type: "boolean",
title: "Notify email",
description: "Send an email after scheduled runs.",
"x-order": 3,
},
},
},
resolution: {
type: "string",
enum: ["1", "5", "15", "30", "60", "240", "1D", "1W"],
title: "Resolution",
description: "Bar timeframe for price data.",
"x-section": "Price model",
"x-order": 1,
},
countBack: {
type: "number",
title: "Bars to fetch",
description: "Number of historical bars for indicator calculations.",
minimum: 50,
maximum: 5000,
"x-step": 1,
"x-section": "Price model",
"x-order": 2,
},
price: {
type: "object",
title: "Price settings",
description: "Indicator-specific tuning.",
"x-section": "Price model",
properties: {
rsiPreset: {
type: "string",
title: "RSI preset",
description: "Preset thresholds for RSI.",
"x-visibleIf": { field: "indicators", contains: "rsi" },
"x-order": 10,
},
rsi: {
type: "object",
title: "RSI thresholds",
description: "Custom RSI overbought/oversold values.",
properties: {
overbought: {
type: "number",
title: "RSI overbought",
description: "Overbought threshold.",
minimum: 1,
maximum: 100,
"x-visibleIf": { field: "indicators", contains: "rsi" },
"x-order": 11,
},
oversold: {
type: "number",
title: "RSI oversold",
description: "Oversold threshold.",
minimum: 1,
maximum: 100,
"x-visibleIf": { field: "indicators", contains: "rsi" },
"x-order": 12,
},
},
},
movingAverage: {
type: "object",
title: "Moving average",
description: "Configuration for SMA/EMA checks.",
properties: {
type: {
type: "string",
enum: ["sma", "ema"],
title: "Average type",
description: "Moving average type.",
"x-visibleIf": { field: "indicators", containsAny: ["sma", "ema"] },
"x-order": 20,
},
period: {
type: "number",
title: "Average period",
description: "Lookback period for moving average.",
minimum: 2,
maximum: 240,
"x-step": 1,
"x-visibleIf": { field: "indicators", containsAny: ["sma", "ema"] },
"x-order": 21,
},
},
},
maCross: {
type: "object",
title: "MA cross",
description: "Fast/slow moving average cross settings.",
properties: {
type: {
type: "string",
enum: ["sma", "ema"],
title: "Cross type",
description: "Moving average type used in cross logic.",
"x-visibleIf": { field: "indicators", contains: "ma-cross" },
"x-order": 30,
},
fastPeriod: {
type: "number",
title: "Fast period",
description: "Fast moving average lookback.",
minimum: 2,
maximum: 239,
"x-step": 1,
"x-visibleIf": { field: "indicators", contains: "ma-cross" },
"x-order": 31,
},
slowPeriod: {
type: "number",
title: "Slow period",
description: "Slow moving average lookback.",
minimum: 3,
maximum: 240,
"x-step": 1,
"x-visibleIf": { field: "indicators", contains: "ma-cross" },
"x-order": 32,
},
},
},
bollinger: {
type: "object",
title: "Bollinger bands",
description: "Period and standard deviation settings.",
properties: {
period: {
type: "number",
title: "BB period",
description: "Lookback period for Bollinger bands.",
minimum: 5,
maximum: 240,
"x-step": 1,
"x-visibleIf": { field: "indicators", contains: "bb" },
"x-order": 40,
},
stdDev: {
type: "number",
title: "BB std dev",
description: "Standard deviation multiplier.",
minimum: 0.5,
maximum: 5,
"x-step": 0.1,
"x-visibleIf": { field: "indicators", contains: "bb" },
"x-order": 41,
},
},
},
donchian: {
type: "object",
title: "Donchian",
description: "Breakout channel lookback settings.",
properties: {
period: {
type: "number",
title: "Donchian period",
description: "Bars used to build the channel.",
minimum: 2,
maximum: 240,
"x-step": 1,
"x-visibleIf": { field: "indicators", contains: "donchian" },
"x-order": 60,
},
},
},
},
},
execution: {
type: "object",
title: "Execution",
description: "Live trading controls and routing.",
"x-section": "Execution",
properties: {
enabled: {
type: "boolean",
title: "Execution enabled",
description: "Submit live orders when signals trigger.",
"x-order": 1,
},
environment: {
type: "string",
enum: ["testnet", "mainnet"],
title: "Environment",
description: "Execution environment for Hyperliquid.",
"x-enumLabels": ["Testnet", "Mainnet"],
"x-order": 2,
},
symbol: {
type: "string",
title: "Execution symbol",
description:
"Optional exact Hyperliquid symbol override. Use BASE-QUOTE or BASE/QUOTE for spot, bare BASE for perps, or dex:BASE for HIP-3.",
"x-order": 3,
},
mode: {
type: "string",
enum: ["long-only", "long-short"],
title: "Execution mode",
description: "Allow long-only or long-short positioning.",
"x-enumLabels": ["Long only", "Long + short"],
"x-order": 4,
},
size: {
type: "number",
title: "Explicit size",
description: "Optional explicit position size override.",
minimum: 0,
"x-step": 0.0001,
"x-order": 5,
},
indicator: {
type: "string",
enum: ["rsi", "macd", "bb", "sma", "ema", "ma-cross", "donchian"],
title: "Execution indicator",
description: "Indicator used to decide trade direction.",
"x-order": 6,
},
leverage: {
type: "number",
title: "Leverage",
description: "Target leverage for perpetual execution.",
minimum: 1,
maximum: 50,
"x-unit": "x",
"x-step": 1,
"x-order": 7,
},
slippageBps: {
type: "number",
title: "Execution slippage",
description: "Max slippage for signal-driven orders.",
minimum: 0,
maximum: 500,
"x-unit": "bps",
"x-format": "bps",
"x-step": 1,
"x-order": 8,
},
},
},
},
} as const;
export const configSchema = z
.object({
platform: z.literal("hyperliquid").optional(),
asset: z.string().min(1).optional(),
signalType: z.literal("price").optional(),
indicators: z.array(indicatorSchema).optional(),
allocationMode: z.literal("fixed").optional(),
amountUsd: z.number().positive().optional(),
schedule: z
.object({
cron: z.string().min(1).optional(),
enabled: z.boolean().optional(),
notifyEmail: z.boolean().optional(),
})
.optional(),
resolution: z.enum(["1", "5", "15", "30", "60", "240", "1D", "1W"]).optional(),
countBack: z.number().int().min(50).max(5000).optional(),
price: z
.object({
rsiPreset: z.string().optional(),
rsi: z
.object({
overbought: z.number().optional(),
oversold: z.number().optional(),
})
.optional(),
movingAverage: z
.object({
type: z.enum(["sma", "ema"]).optional(),
period: z
.number()
.int()
.min(LIMITS.movingAverage.min)
.max(LIMITS.movingAverage.max)
.optional(),
})
.optional(),
maCross: z
.object({
type: z.enum(["sma", "ema"]).optional(),
fastPeriod: z
.number()
.int()
.min(LIMITS.maCross.fastMin)
.max(LIMITS.maCross.fastMax)
.optional(),
slowPeriod: z
.number()
.int()
.min(LIMITS.maCross.slowMin)
.max(LIMITS.maCross.slowMax)
.optional(),
})
.optional(),
bollinger: z
.object({
period: z
.number()
.int()
.min(LIMITS.bollinger.periodMin)
.max(LIMITS.bollinger.periodMax)
.optional(),
stdDev: z
.number()
.min(LIMITS.bollinger.stdDevMin)
.max(LIMITS.bollinger.stdDevMax)
.optional(),
})
.optional(),
donchian: z
.object({
period: z
.number()
.int()
.min(LIMITS.donchian.min)
.max(LIMITS.donchian.max)
.optional(),
})
.optional(),
})
.optional(),
execution: z
.object({
enabled: z.boolean().optional(),
environment: z.enum(["testnet", "mainnet"]).optional(),
symbol: z.string().optional(),
mode: z.enum(["long-only", "long-short"]).optional(),
size: z.number().positive().optional(),
leverage: z.number().positive().optional(),
slippageBps: z.number().min(0).max(500).optional(),
indicator: indicatorSchema.optional(),
})
.optional(),
})
.partial();