import BN from "bn.js"; import { safeToNumber } from "../math.js"; import { OutcomeResult } from "./types.js"; /** * Calculate the expected outcome based on current price and strikes * * For High Pool: * - Outcome 0: price < strikes[0] * - Outcome 1: price >= strikes[0] AND price < strikes[1] * - Outcome 2: price >= strikes[1] AND price < strikes[2] * - Outcome 3: price >= strikes[2] * * For Low Pool: * - Strikes are in DESCENDING order: [strike0=$160, strike1=$150, strike2=$140] * - Outcome 0: price >= strikes[0] (price >= $160, no strikes hit) * - Outcome 1: price < strikes[0] AND price >= strikes[1] (below $160, above $150) * - Outcome 2: price < strikes[1] AND price >= strikes[2] (below $150, above $140) * - Outcome 3: price < strikes[2] (below $140, all strikes hit) * * CRITICAL: Low Pool strikes must be checked from lowest to highest (s2 → s1 → s0) * using independent if statements to allow cumulative outcome assignment. * * @param priceInCents - Current price in cents * @param strikes - Strike prices in cents [strike0, strike1, strike2] * @returns Outcome for both High and Low pools * * @example * ```typescript * // High Pool strikes: [140, 150, 160] (ascending) * const result = calculateOutcome(15500, [14000, 15000, 16000]); * // result.highOutcome = 2 (price >= 150 but < 160) * * // Low Pool strikes: [160, 150, 140] (descending) * const result = calculateOutcome(15500, [16000, 15000, 14000]); * // result.lowOutcome = 1 (price < 160 but >= 150) * ``` */ export function calculateOutcome( priceInCents: number, strikes: [BN, BN, BN] | [number, number, number] ): OutcomeResult { // Safely convert BN strikes to numbers // Strike prices in cents are always safe (< 2^53) const s0 = typeof strikes[0] === "number" ? strikes[0] : safeToNumber(strikes[0], "strike0"); const s1 = typeof strikes[1] === "number" ? strikes[1] : safeToNumber(strikes[1], "strike1"); const s2 = typeof strikes[2] === "number" ? strikes[2] : safeToNumber(strikes[2], "strike2"); // High pool: outcome increases when price goes UP let highOutcome = 0; if (priceInCents >= s2) { highOutcome = 3; } else if (priceInCents >= s1) { highOutcome = 2; } else if (priceInCents >= s0) { highOutcome = 1; } // Low pool: outcome increases when price goes DOWN // Check from most extreme (s2, lowest) to least extreme (s0, highest) let lowOutcome = 0; if (priceInCents < s2) { lowOutcome = 3; // Dropped below all strikes (lowest) } else if (priceInCents < s1) { lowOutcome = 2; // Dropped below middle strike } else if (priceInCents < s0) { lowOutcome = 1; // Dropped below highest strike } return { highOutcome, lowOutcome, priceInCents, }; } /** * Check if a new outcome should be marked * * @param currentOutcome - Current winning_outcome from pool * @param newOutcome - Calculated outcome from price * @returns true if newOutcome > currentOutcome (should call mark_extreme) */ export function shouldMarkExtreme(currentOutcome: number, newOutcome: number): boolean { return newOutcome > currentOutcome; }