openpondai/agents/pair-trade
OpenTool app
typescript
import { z } from "zod";
export const TEMPLATE_CONFIG_SCHEMA = {
type: "object",
"x-budget": {
modeField: "allocationMode",
defaultMode: "target_notional",
title: "Budget & allocation",
description: "Core exposure settings are shown first.",
modes: {
target_notional: {
fields: ["targetNotionalUsd", "hedgeRatio"],
},
},
},
required: [
"platform",
"allocationMode",
"legAAsset",
"legBAsset",
"legAMarketType",
"legBMarketType",
"legASide",
"targetNotionalUsd",
"hedgeRatio",
"legALeverage",
"legBLeverage",
"legALeverageMode",
"legBLeverageMode",
"slippageBps",
"rebalanceDriftPct",
"environment",
],
properties: {
configVersion: {
type: "number",
title: "Config version",
description: "Internal version for pair-trade config defaults.",
readOnly: true,
"x-hidden": true,
"x-section": "Meta",
"x-order": 1000,
},
platform: {
type: "string",
enum: ["hyperliquid"],
title: "Platform",
description: "Execution venue for long + short legs.",
readOnly: true,
"x-section": "Execution",
"x-order": 1,
},
allocationMode: {
type: "string",
enum: ["target_notional"],
title: "Allocation mode",
description: "Canonical sizing mode for pair-trade templates.",
readOnly: true,
"x-hidden": true,
"x-section": "Meta",
"x-order": 1001,
},
legAAsset: {
type: "string",
title: "Leg A asset",
description: "Primary pair leg asset (example: SOL).",
"x-format": "asset",
"x-section": "Strategy",
"x-order": 1,
},
legBAsset: {
type: "string",
title: "Leg B asset",
description: "Opposite pair leg asset (example: ETH).",
"x-format": "asset",
"x-section": "Strategy",
"x-order": 2,
},
legAMarketType: {
type: "string",
enum: ["spot", "perp"],
title: "Leg A market",
description: "Market type for Leg A.",
"x-enumLabels": ["Spot", "Perp"],
"x-section": "Strategy",
"x-order": 3,
},
legBMarketType: {
type: "string",
enum: ["spot", "perp"],
title: "Leg B market",
description: "Market type for Leg B.",
"x-enumLabels": ["Spot", "Perp"],
"x-section": "Strategy",
"x-order": 4,
},
legASide: {
type: "string",
enum: ["long", "short"],
title: "Leg A side",
description: "Whether Leg A is the long or short side. Leg B uses the opposite side.",
"x-enumLabels": ["Long", "Short"],
"x-section": "Strategy",
"x-order": 5,
},
targetNotionalUsd: {
type: "number",
title: "Leg A notional",
description: "USD notional for Leg A before applying the Leg B size ratio.",
minimum: 1,
"x-unit": "USD",
"x-format": "currency",
"x-step": 1,
"x-section": "Strategy",
"x-order": 6,
},
hedgeRatio: {
type: "number",
title: "Leg B notional size",
description: "Leg B notional exposure as a multiplier of Leg A notional. This is not leverage.",
minimum: 0.01,
"x-unit": "x",
"x-step": 0.01,
"x-section": "Strategy",
"x-order": 7,
},
legALeverage: {
type: "number",
title: "Leg A leverage",
description: "Margin leverage for Leg A when Leg A is a perp market. This changes margin and liquidation buffer, not notional size.",
minimum: 1,
maximum: 40,
"x-unit": "x",
"x-step": 1,
"x-section": "Execution",
"x-order": 3,
},
legBLeverage: {
type: "number",
title: "Leg B leverage",
description: "Margin leverage for Leg B when Leg B is a perp market. This changes margin and liquidation buffer, not notional size.",
minimum: 1,
maximum: 40,
"x-unit": "x",
"x-step": 1,
"x-section": "Execution",
"x-order": 4,
},
legALeverageMode: {
type: "string",
enum: ["cross", "isolated"],
title: "Leg A margin mode",
description: "Margin mode for Leg A when Leg A is a perp market.",
"x-enumLabels": ["Cross", "Isolated"],
"x-section": "Execution",
"x-order": 5,
},
legBLeverageMode: {
type: "string",
enum: ["cross", "isolated"],
title: "Leg B margin mode",
description: "Margin mode for Leg B when Leg B is a perp market.",
"x-enumLabels": ["Cross", "Isolated"],
"x-section": "Execution",
"x-order": 6,
},
slippageBps: {
type: "number",
title: "Slippage",
description: "Maximum slippage for marketable orders.",
minimum: 0,
maximum: 5000,
"x-unit": "bps",
"x-format": "bps",
"x-step": 1,
"x-section": "Risk limits",
"x-order": 1,
},
rebalanceDriftPct: {
type: "number",
title: "Rebalance drift",
description: "Percent of target notional before rebalancing either leg.",
minimum: 0.1,
maximum: 50,
"x-unit": "%",
"x-format": "percent",
"x-step": 0.1,
"x-section": "Risk limits",
"x-order": 2,
},
environment: {
type: "string",
enum: ["mainnet", "testnet"],
title: "Environment",
description: "Hyperliquid environment for execution.",
"x-enumLabels": ["Mainnet", "Testnet"],
"x-section": "Execution",
"x-order": 2,
},
schedule: {
type: ["object", "null"],
title: "Schedule",
description: "Optional cron schedule for advanced recurring maintenance.",
"x-section": "Schedule",
properties: {
cron: {
type: "string",
title: "Cron expression",
description: "Standard cron expression for the run schedule.",
"x-order": 1,
},
enabled: {
type: "boolean",
title: "Enabled",
description: "Enable the scheduled rebalance.",
"x-order": 2,
},
notifyEmail: {
type: "boolean",
title: "Notify email",
description: "Send an email after each scheduled run.",
"x-order": 3,
},
},
},
},
} as const;
export const scheduleSchema = z
.object({
cron: z.string().min(1).optional(),
enabled: z.boolean().optional(),
notifyEmail: z.boolean().optional(),
})
.nullable()
.optional();
export const configSchema = z.object({
platform: z.literal("hyperliquid").optional(),
allocationMode: z.literal("target_notional").optional(),
legAAsset: z.string().min(1).optional(),
legBAsset: z.string().min(1).optional(),
legAMarketType: z.enum(["spot", "perp"]).optional(),
legBMarketType: z.enum(["spot", "perp"]).optional(),
legASide: z.enum(["long", "short"]).optional(),
longAsset: z.string().min(1).optional(),
shortAsset: z.string().min(1).optional(),
longMarketType: z.enum(["spot", "perp"]).optional(),
shortMarketType: z.enum(["spot", "perp"]).optional(),
targetNotionalUsd: z.number().positive().optional(),
hedgeRatio: z.number().positive().optional(),
legALeverage: z.number().positive().optional(),
legBLeverage: z.number().positive().optional(),
legALeverageMode: z.enum(["cross", "isolated"]).optional(),
legBLeverageMode: z.enum(["cross", "isolated"]).optional(),
slippageBps: z.number().int().min(0).max(5000).optional(),
rebalanceDriftPct: z.number().positive().max(100).optional(),
environment: z.enum(["mainnet", "testnet"]).optional(),
schedule: scheduleSchema,
});