1Branch0Tags
GL
glucryptouse HIP-4 series key identity
e149f868 days ago3Commits
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); }