openpondai/agents/price-trigger-bot
OpenTool app
1Branch0Tags
typescript
import {
buildHyperliquidMarketIdentity,
extractHyperliquidOrderIds,
fetchHyperliquidResolvedMarketDescriptor,
fetchHyperliquidTickSize,
formatHyperliquidMarketablePrice,
formatHyperliquidOrderSize,
placeHyperliquidOrder,
resolveHyperliquidLeverageMode,
updateHyperliquidLeverage,
type HyperliquidEnvironment,
type HyperliquidOrderResponse,
} from "opentool/adapters/hyperliquid";
import { store } from "opentool/store";
import type { WalletFullContext } from "opentool/wallet";
import { normalizeSymbol, type PriceTriggerConfig, type PriceTriggerRule } from "../config";
import { readObservedPrice, readOrderSizeDecimals } from "./market";
import type { PriceTriggerTriggeredTarget } from "./evaluate";
export async function executeTriggeredRule(params: {
config: PriceTriggerConfig;
environment: HyperliquidEnvironment;
mids: Record<string, string | number>;
rule: PriceTriggerRule;
walletContext: WalletFullContext;
budgetUsd: number;
}) {
const totalWeight = params.rule.targets.reduce(
(sum, target) => sum + target.weight,
0,
);
const normalizedWeight = totalWeight > 0 ? totalWeight : 1;
const targets: PriceTriggerTriggeredTarget[] = [];
for (const target of params.rule.targets) {
const targetSymbol = normalizeSymbol(target.symbol);
const targetDescriptor = await fetchHyperliquidResolvedMarketDescriptor({
environment: params.environment,
symbol: targetSymbol,
mids: params.mids,
});
const orderSymbol = targetDescriptor.orderSymbol;
const isSpot =
targetDescriptor.kind === "spot" || targetDescriptor.kind === "spotIndex";
const currentPrice = await readObservedPrice({
environment: params.environment,
symbol: targetSymbol,
mids: params.mids,
});
const targetBudgetUsd = (params.budgetUsd * target.weight) / normalizedWeight;
const rawSize = targetBudgetUsd / currentPrice;
const sizeDecimals = await readOrderSizeDecimals({
environment: params.environment,
descriptor: targetDescriptor,
});
const tick = await fetchHyperliquidTickSize({
environment: params.environment,
symbol: orderSymbol,
});
const size = formatHyperliquidOrderSize(rawSize, sizeDecimals);
const price = formatHyperliquidMarketablePrice({
mid: currentPrice,
side: params.rule.actionSide,
slippageBps: params.config.execution?.slippageBps ?? 50,
tick,
szDecimals: sizeDecimals,
marketType: isSpot ? "spot" : "perp",
});
if (size === "0") {
targets.push({
symbol: targetSymbol,
side: params.rule.actionSide,
budgetUsd: targetBudgetUsd,
size,
price,
});
continue;
}
if (
!isSpot &&
typeof params.config.execution?.leverage === "number"
) {
const leverageMode = resolveHyperliquidLeverageMode(orderSymbol);
await updateHyperliquidLeverage({
wallet: params.walletContext,
environment: params.environment,
input: {
symbol: orderSymbol,
leverageMode,
leverage: params.config.execution.leverage,
},
});
}
const orderResponse = await placeHyperliquidOrder({
wallet: params.walletContext,
environment: params.environment,
orders: [
{
symbol: orderSymbol,
side: params.rule.actionSide,
price,
size,
tif: "FrontendMarket",
reduceOnly: false,
},
],
});
const orderIds = extractHyperliquidOrderIds([
orderResponse as unknown as {
response?: {
data?: {
statuses?: Array<Record<string, unknown>>;
};
};
},
]);
const marketIdentity = buildHyperliquidMarketIdentity({
environment: params.environment,
symbol: targetDescriptor.pair ?? orderSymbol,
rawSymbol: orderSymbol,
isSpot,
base: targetDescriptor.base ?? targetSymbol.split(/[:/-]/)[0] ?? null,
quote: targetDescriptor.quote,
});
if (marketIdentity) {
await store({
source: "hyperliquid",
ref:
orderIds.cloids[0] ??
orderIds.oids[0] ??
`price-trigger-bot-${Date.now()}-${targetSymbol}`,
status: "submitted",
walletAddress: params.walletContext.address,
action: "order",
notional: size,
network:
params.environment === "mainnet"
? "hyperliquid"
: "hyperliquid-testnet",
market: marketIdentity,
metadata: {
strategy: "price-trigger-bot",
sourceSymbol: params.rule.sourceSymbol,
threshold: params.rule.threshold,
condition: params.rule.condition,
side: params.rule.actionSide,
symbol: targetSymbol,
price,
size,
budgetUsd: targetBudgetUsd,
cloid: orderIds.cloids[0] ?? null,
orderIds,
orderResponse,
orderResponses: [orderResponse],
},
});
}
targets.push({
symbol: targetSymbol,
side: params.rule.actionSide,
budgetUsd: targetBudgetUsd,
size,
price,
orderIds,
orderResponse: orderResponse as HyperliquidOrderResponse,
});
}
return targets;
}