openpondai/agents/price-trigger-bot
OpenTool app
1Branch0Tags
typescript
import {
fetchHyperliquidAllMids,
type HyperliquidEnvironment,
} from "opentool/adapters/hyperliquid";
import { store } from "opentool/store";
import { wallet } from "opentool/wallet";
import type { WalletFullContext } from "opentool/wallet";
import type { PriceTriggerConfig } from "../config";
import { evaluateRule, resolveBudgetPerRule, type PriceTriggerRuleResult } from "./evaluate";
import { executeTriggeredRule } from "./execute";
import { readObservedPrice } from "./market";
import { readLatestState, type PriceTriggerRuleState } from "./state";
export type PriceTriggerBotResult = {
ok: boolean;
simulated: boolean;
environment: HyperliquidEnvironment;
amountUsd: number;
maxPerRunUsd: number;
triggeredRules: number;
results: PriceTriggerRuleResult[];
state: Record<string, PriceTriggerRuleState>;
};
export async function runPriceTriggerBot(
config: PriceTriggerConfig,
): Promise<PriceTriggerBotResult> {
const environment = config.execution?.environment ?? "mainnet";
const mids = await fetchHyperliquidAllMids(environment);
const previousState = await readLatestState();
const evaluatedRules = await Promise.all(
config.rules.map(async (rule) => {
const currentPrice = await readObservedPrice({
environment,
symbol: rule.sourceSymbol,
mids,
});
const prior = previousState[rule.id] ?? null;
return {
alreadyExecuted: Boolean(prior?.executedAt),
currentPrice,
previousState: prior,
rule,
triggered:
!prior?.executedAt &&
evaluateRule({
rule,
currentPrice,
previousState: prior,
}),
};
}),
);
const triggeredRules = evaluatedRules.filter((entry) => entry.triggered);
const budgetPerRule = resolveBudgetPerRule(config, triggeredRules.length);
const walletContext =
config.execution?.enabled === true ? ((await wallet()) as WalletFullContext) : null;
const results: PriceTriggerRuleResult[] = [];
const nextState: Record<string, PriceTriggerRuleState> = {};
for (const entry of evaluatedRules) {
let executedAt = entry.previousState?.executedAt ?? null;
const targets =
entry.triggered && walletContext && config.execution?.enabled
? await executeTriggeredRule({
config,
environment,
mids,
rule: entry.rule,
walletContext,
budgetUsd: budgetPerRule,
})
: [];
const executedNow = targets.some(
(target) => target.size !== "0" && target.orderResponse != null,
);
if (executedNow && !executedAt) {
executedAt = new Date().toISOString();
}
nextState[entry.rule.id] = {
lastObservedPrice: entry.currentPrice,
...(executedAt ? { executedAt } : {}),
};
results.push({
id: entry.rule.id,
sourceSymbol: entry.rule.sourceSymbol,
condition: entry.rule.condition,
threshold: entry.rule.threshold,
currentPrice: entry.currentPrice,
lastObservedPrice: entry.previousState?.lastObservedPrice ?? null,
alreadyExecuted: entry.alreadyExecuted,
executedAt,
triggered: entry.triggered,
actionSide: entry.rule.actionSide,
targets,
});
}
const result: PriceTriggerBotResult = {
ok: true,
simulated: !config.execution?.enabled,
environment,
amountUsd: config.amountUsd,
maxPerRunUsd: config.maxPerRunUsd,
triggeredRules: triggeredRules.length,
results,
state: nextState,
};
await store({
source: "price-trigger-bot",
ref: `price-trigger-bot-${Date.now()}`,
status: "info",
action: "signal",
metadata: result,
});
return result;
}