openpondai/agents/hip4-edge-bot
OpenTool app
typescript
import type { Hip4EdgeBotConfig, Hip4ModelWindow } from "../config";
import type { Hip4GatewayAnalytics, Hip4GatewayMarket } from "./types";
const YEAR_MS = 365 * 24 * 60 * 60 * 1000;
const VOL_WINDOWS = ["1h", "6h", "24h", "7d"] as const;
type VolWindow = (typeof VOL_WINDOWS)[number];
export type Hip4EdgeCalculation = {
fairYes: Record<VolWindow, number | null> & {
selected: number | null;
min: number | null;
max: number | null;
};
fairNoSelected: number | null;
yesBuyEdge: number | null;
noBuyEdge: number | null;
yesBuyRoi: number | null;
noBuyRoi: number | null;
};
function erf(value: number): number {
const sign = value < 0 ? -1 : 1;
const x = Math.abs(value);
const t = 1 / (1 + 0.3275911 * x);
const a1 = 0.254829592;
const a2 = -0.284496736;
const a3 = 1.421413741;
const a4 = -1.453152027;
const a5 = 1.061405429;
const y =
1 -
(((((a5 * t + a4) * t + a3) * t + a2) * t + a1) *
t *
Math.exp(-x * x));
return sign * y;
}
function normalCdf(value: number): number {
return 0.5 * (1 + erf(value / Math.SQRT2));
}
function clampProbability(value: number): number {
return Math.max(0, Math.min(1, value));
}
function toTimestamp(value: string | null | undefined): number {
if (!value) return Number.NaN;
const timestamp = Date.parse(value);
return Number.isFinite(timestamp) ? timestamp : Number.NaN;
}
function calculateFairYesProbability(params: {
underlyingMid: number | null;
targetPrice: number | null;
expiryMs: number;
nowMs: number;
realizedVolatility: number | null;
}): number | null {
const { underlyingMid, targetPrice, realizedVolatility } = params;
if (
underlyingMid == null ||
targetPrice == null ||
underlyingMid <= 0 ||
targetPrice <= 0 ||
!Number.isFinite(params.expiryMs) ||
!Number.isFinite(params.nowMs)
) {
return null;
}
const yearsToExpiry = Math.max(params.expiryMs - params.nowMs, 0) / YEAR_MS;
if (yearsToExpiry <= 0 || realizedVolatility == null || realizedVolatility <= 0) {
return underlyingMid >= targetPrice ? 1 : 0;
}
const sigmaRootT = realizedVolatility * Math.sqrt(yearsToExpiry);
if (!Number.isFinite(sigmaRootT) || sigmaRootT <= 0) {
return underlyingMid >= targetPrice ? 1 : 0;
}
const d2 =
(Math.log(underlyingMid / targetPrice) -
0.5 * realizedVolatility ** 2 * yearsToExpiry) /
sigmaRootT;
return clampProbability(normalCdf(d2));
}
function resolveSelectedFairYes(params: {
fairYes: Record<VolWindow, number | null>;
selectedModel: Hip4ModelWindow;
}): number | null {
if (params.selectedModel !== "blend") {
return params.fairYes[params.selectedModel];
}
const values = VOL_WINDOWS.map((window) => params.fairYes[window]).filter(
(value): value is number => value != null,
);
if (values.length === 0) return null;
return values.reduce((sum, value) => sum + value, 0) / values.length;
}
function edgeRoi(edge: number | null, price: number | null): number | null {
if (edge == null || price == null || price <= 0) return null;
return edge / price;
}
function readVolatility(
analytics: Hip4GatewayAnalytics,
market: Hip4GatewayMarket,
window: VolWindow,
) {
const marketValue = market.model.realizedVolatility[window];
if (typeof marketValue === "number" && Number.isFinite(marketValue)) {
return marketValue;
}
const analyticsValue = analytics.realizedVolatility[window];
return typeof analyticsValue === "number" && Number.isFinite(analyticsValue)
? analyticsValue
: null;
}
export function calculateHip4Edge(params: {
config: Hip4EdgeBotConfig;
analytics: Hip4GatewayAnalytics;
market: Hip4GatewayMarket;
}): Hip4EdgeCalculation {
const nowMs = toTimestamp(params.analytics.asOf) || Date.now();
const expiryMs = toTimestamp(params.market.expiryAt);
const fairYesBase = Object.fromEntries(
VOL_WINDOWS.map((window) => [
window,
calculateFairYesProbability({
underlyingMid: params.analytics.btcReference.btcMid,
targetPrice: params.market.targetPrice,
expiryMs,
nowMs,
realizedVolatility: readVolatility(params.analytics, params.market, window),
}),
]),
) as Record<VolWindow, number | null>;
const fairYesSelected = resolveSelectedFairYes({
fairYes: fairYesBase,
selectedModel: params.config.model.selectedModel,
});
const fairValues = VOL_WINDOWS.map((window) => fairYesBase[window]).filter(
(value): value is number => value != null,
);
const fairYesMin = fairValues.length > 0 ? Math.min(...fairValues) : null;
const fairYesMax = fairValues.length > 0 ? Math.max(...fairValues) : null;
const fairNoSelected =
fairYesSelected != null ? clampProbability(1 - fairYesSelected) : null;
const yesBuyEdge =
fairYesSelected != null && params.market.yes.ask != null
? fairYesSelected - params.market.yes.ask
: null;
const noBuyEdge =
fairNoSelected != null && params.market.no.ask != null
? fairNoSelected - params.market.no.ask
: null;
return {
fairYes: {
...fairYesBase,
selected: fairYesSelected,
min: fairYesMin,
max: fairYesMax,
},
fairNoSelected,
yesBuyEdge,
noBuyEdge,
yesBuyRoi: edgeRoi(yesBuyEdge, params.market.yes.ask),
noBuyRoi: edgeRoi(noBuyEdge, params.market.no.ask),
};
}