openpondai/agents/price-trigger-bot
OpenTool app
1Branch0Tags
typescript
import {
fetchHyperliquidDexMetaAndAssetCtxs,
fetchHyperliquidPerpMarketInfo,
fetchHyperliquidResolvedMarketDescriptor,
fetchHyperliquidSizeDecimals,
fetchHyperliquidSpotMarketInfo,
normalizeHyperliquidMetaSymbol,
type HyperliquidEnvironment,
type HyperliquidResolvedMarketDescriptor,
} from "opentool/adapters/hyperliquid";
import { normalizeSymbol } from "../config";
export function isSpotSymbol(value: string) {
return value.includes("/") || value.includes(":");
}
export async function readObservedPrice(params: {
environment: HyperliquidEnvironment;
symbol: string;
mids: Record<string, string | number>;
}) {
const symbol = normalizeSymbol(params.symbol);
const descriptor = await fetchHyperliquidResolvedMarketDescriptor({
environment: params.environment,
symbol,
mids: params.mids,
});
const midRaw = params.mids[descriptor.marketDataCoin];
const midPrice =
typeof midRaw === "number"
? midRaw
: typeof midRaw === "string"
? Number.parseFloat(midRaw)
: Number.NaN;
if (Number.isFinite(midPrice) && midPrice > 0) {
return midPrice;
}
if (descriptor.kind === "spot" || descriptor.kind === "spotIndex") {
const marketInfo = await fetchHyperliquidSpotMarketInfo({
environment: params.environment,
base: descriptor.base ?? symbol,
quote: descriptor.quote ?? "USDC",
mids: params.mids,
});
return marketInfo.price;
}
if (descriptor.dex) {
const data = (await fetchHyperliquidDexMetaAndAssetCtxs(
params.environment,
descriptor.dex,
)) as [{ universe?: Array<{ name?: string }> }, Array<Record<string, unknown>>];
const universe = Array.isArray(data?.[0]?.universe) ? data[0].universe : [];
const contexts = Array.isArray(data?.[1]) ? data[1] : [];
const target = normalizeHyperliquidMetaSymbol(descriptor.orderSymbol).toUpperCase();
const idx = universe.findIndex(
(entry) => normalizeHyperliquidMetaSymbol(entry?.name ?? "").toUpperCase() === target,
);
const ctx = idx >= 0 ? contexts[idx] : null;
const fallbackRaw = ctx?.markPx ?? ctx?.midPx ?? ctx?.oraclePx;
const fallbackPrice =
typeof fallbackRaw === "number"
? fallbackRaw
: typeof fallbackRaw === "string"
? Number.parseFloat(fallbackRaw)
: Number.NaN;
if (Number.isFinite(fallbackPrice) && fallbackPrice > 0) {
return fallbackPrice;
}
} else {
const marketInfo = await fetchHyperliquidPerpMarketInfo({
environment: params.environment,
symbol: descriptor.orderSymbol,
});
return marketInfo.price;
}
throw new Error(`Unable to resolve a live price for ${params.symbol}.`);
}
export async function readOrderSizeDecimals(params: {
environment: HyperliquidEnvironment;
descriptor: HyperliquidResolvedMarketDescriptor;
}) {
if (!params.descriptor.dex) {
return fetchHyperliquidSizeDecimals({
environment: params.environment,
symbol: params.descriptor.orderSymbol,
});
}
const data = (await fetchHyperliquidDexMetaAndAssetCtxs(
params.environment,
params.descriptor.dex,
)) as [{ universe?: Array<{ name?: string; szDecimals?: number | string }> }, Array<Record<string, unknown>>];
const universe = Array.isArray(data?.[0]?.universe) ? data[0].universe : [];
const target = normalizeHyperliquidMetaSymbol(params.descriptor.orderSymbol).toUpperCase();
const entry =
universe.find(
(item) => normalizeHyperliquidMetaSymbol(item?.name ?? "").toUpperCase() === target,
) ?? null;
const rawDecimals = entry?.szDecimals;
const sizeDecimals =
typeof rawDecimals === "number"
? rawDecimals
: typeof rawDecimals === "string"
? Number.parseFloat(rawDecimals)
: Number.NaN;
if (!Number.isFinite(sizeDecimals)) {
throw new Error(`No size decimals found for ${params.descriptor.orderSymbol}.`);
}
return sizeDecimals;
}