openpondai/agents/hip4-edge-bot
OpenTool app
typescript
import type { Hip4EdgeBotConfig, Hip4EdgeSide } from "../config";
import { calculateHip4Edge } from "./calculate";
import type {
Hip4EdgeBotState,
Hip4EdgeCandidate,
Hip4GatewayAnalytics,
Hip4GatewayMarket,
} from "./types";
function isFinitePositive(value: number | null | undefined): value is number {
return typeof value === "number" && Number.isFinite(value) && value > 0;
}
function normalizeIdentity(value: string | null | undefined) {
return value?.trim().toLowerCase() ?? "";
}
function matchesConfiguredSeries(config: Hip4EdgeBotConfig, market: Hip4GatewayMarket) {
const configuredSeries = normalizeIdentity(config.model.seriesKey);
if (!configuredSeries) return true;
return normalizeIdentity(market.seriesKey) === configuredSeries;
}
function buildStateKey(market: Hip4GatewayMarket, side: Hip4EdgeSide) {
const series = market.seriesKey ?? "unknown-series";
const round = market.roundKey ?? `${market.outcomeId}:${market.expiryAt ?? "unknown"}`;
return `${series}:${round}:${side}`;
}
function buildCandidate(params: {
config: Hip4EdgeBotConfig;
analytics: Hip4GatewayAnalytics;
market: Hip4GatewayMarket;
side: Hip4EdgeSide;
state: Hip4EdgeBotState;
}): Hip4EdgeCandidate | null {
const book = params.side === "yes" ? params.market.yes : params.market.no;
const calculation = calculateHip4Edge({
config: params.config,
analytics: params.analytics,
market: params.market,
});
const edge =
params.side === "yes"
? calculation.yesBuyEdge
: calculation.noBuyEdge;
const roi =
params.side === "yes"
? calculation.yesBuyRoi
: calculation.noBuyRoi;
const fairProbability =
params.side === "yes"
? calculation.fairYes.selected
: calculation.fairNoSelected;
const ask = book.ask;
const askDepthUsd = book.askDepthUsd ?? book.topAskDepthUsd ?? null;
const stateKey = buildStateKey(params.market, params.side);
if (params.config.risk.oneTradePerRoundSide && params.state[stateKey]) return null;
if (!isFinitePositive(ask) || ask > params.config.risk.maxAsk) return null;
if (!isFinitePositive(edge) || edge < params.config.risk.minEdgePct / 100) return null;
if (!isFinitePositive(roi) || roi < params.config.risk.minRoiPct / 100) return null;
if (!isFinitePositive(fairProbability)) return null;
if (askDepthUsd != null && askDepthUsd < params.config.risk.minAskDepthUsd) return null;
if (
typeof params.config.risk.maxHoursToExpiry === "number" &&
isFinitePositive(params.market.distance.hoursToExpiry) &&
params.market.distance.hoursToExpiry > params.config.risk.maxHoursToExpiry
) {
return null;
}
return {
side: params.side,
market: params.market,
seriesKey: params.market.seriesKey,
roundKey: params.market.roundKey,
marketDataCoin: book.marketDataCoin,
ask,
fairProbability,
edge,
roi,
calculation,
askDepthUsd,
stateKey,
};
}
export function selectHip4EdgeCandidates(params: {
config: Hip4EdgeBotConfig;
analytics: Hip4GatewayAnalytics;
markets: Hip4GatewayMarket[];
state: Hip4EdgeBotState;
}) {
const allowedSides = new Set(params.config.risk.allowedSides);
const candidates: Hip4EdgeCandidate[] = [];
for (const market of params.markets) {
if (!matchesConfiguredSeries(params.config, market)) {
continue;
}
if (allowedSides.has("yes")) {
const candidate = buildCandidate({
config: params.config,
analytics: params.analytics,
market,
side: "yes",
state: params.state,
});
if (candidate) candidates.push(candidate);
}
if (allowedSides.has("no")) {
const candidate = buildCandidate({
config: params.config,
analytics: params.analytics,
market,
side: "no",
state: params.state,
});
if (candidate) candidates.push(candidate);
}
}
return candidates
.sort((left, right) => {
if (right.edge !== left.edge) return right.edge - left.edge;
return right.roi - left.roi;
})
.slice(0, params.config.risk.maxTradesPerRun);
}