import {z} from 'zod' export type ClientDefinedOutcomeExpiryPeriodType = 'MONTHS' | 'WEEKS'; export type ClientDefinedOutcomeExpiry = {expiry_period_type: ClientDefinedOutcomeExpiryPeriodType, expiry_period_value: number}; export type OptionExerciseStyle = 'EUROPEAN' | 'AMERICAN'; export type ClientDefinedOutcomeOptionType = 'PUT' | 'CALL'; export type ClientDefinedOutcomeRoots = 'SPY' | 'QQQ' | 'IWM' | 'EFA' | 'XIU' | 'TLT' | 'HYG' | 'EEM' | 'LQD' | 'VOO' | 'IVV' | 'SPX' | 'VTWO' | 'BITO' | 'IBIT' | 'MSFT' | 'AMZN' | 'TSLA' | 'META' | 'AAPL' | 'NVDA' | 'GOOGL' | 'GOOG' | 'GLD'; export type ClientDefinedOutcomeStrikeType = 'DELTA' | 'PCT_SPOT' | 'INCOME'; export type ClientDefinedOutcomeTradeType = 'BUY' | 'SELL'; export type ClientDefinedOutcomeOption = {exercise_style: OptionExerciseStyle, exposure?: number | undefined, option_type: ClientDefinedOutcomeOptionType, root: ClientDefinedOutcomeRoots, strike_type: ClientDefinedOutcomeStrikeType, strike_value: number, trade_type: ClientDefinedOutcomeTradeType}; export type ClientDefinedOutcomeParticipationRateStrategyOption = {exercise_style: OptionExerciseStyle, exposure?: number | undefined, option_type: ClientDefinedOutcomeOptionType, root: ClientDefinedOutcomeRoots, strike_type: ClientDefinedOutcomeStrikeType, strike_value: number, trade_type: ClientDefinedOutcomeTradeType}; export type DefinedOutcomeSolverRequest = {expiry: ClientDefinedOutcomeExpiry, options: Array, root: ClientDefinedOutcomeRoots, variable_leg_params?: ClientDefinedOutcomeOption | undefined, variable_leg_strategy?: ({max_participation: number, option: ClientDefinedOutcomeParticipationRateStrategyOption, strategy: string} | {min_cap: number, strategy: string} | {strategy: string} | null) | undefined}; export type CreateApiHelperSolverDefinedOutcomeArgs = {body: DefinedOutcomeSolverRequest}; export type CreateApiHelperSolverDefinedOutcomeOptions = {baseUrl?: string | undefined, fetch?: typeof fetch | undefined}; export const optionPriceMetrics = z.object({bid_ask_spread: z.number(), delta: z.number(), dividend_yield: z.number(), gamma: z.number(), interest_rate: z.number(), price: z.number(), spot: z.number(), theta: z.number(), vega: z.number(), vol: z.number()}); export const optionExerciseStyle = z.enum(["EUROPEAN", "AMERICAN"]); export const clientDefinedOutcomeOptionType = z.enum(["PUT", "CALL"]); export const clientDefinedOutcomeRoots = z.enum(["SPY", "QQQ", "IWM", "EFA", "XIU", "TLT", "HYG", "EEM", "LQD", "VOO", "IVV", "SPX", "VTWO", "BITO", "IBIT", "MSFT", "AMZN", "TSLA", "META", "AAPL", "NVDA", "GOOGL", "GOOG", "GLD"]); export const clientDefinedOutcomeStrikeType = z.enum(["DELTA", "PCT_SPOT", "INCOME"]); export const clientDefinedOutcomeTradeType = z.enum(["BUY", "SELL"]); export const definedOutcomeSolverResponse = z.object({option_solutions: z.array(z.union([optionPriceMetrics, z.unknown()])).optional(), variable_leg_cash_solution: z.number().nullable().optional(), variable_leg_solution: z.union([z.object({exercise_style: optionExerciseStyle, exposure: z.number().gte(0).optional(), option_type: clientDefinedOutcomeOptionType, root: clientDefinedOutcomeRoots, strike_type: clientDefinedOutcomeStrikeType, strike_value: z.number().gte(0), trade_type: clientDefinedOutcomeTradeType, price: z.number()}), z.object({price: z.number()})]).optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Solve for the options prices and variable leg of a Defined Outcome Options Index * * POST /helper/solver/defined_outcome */ export const createApiHelperSolverDefinedOutcome = async (args: CreateApiHelperSolverDefinedOutcomeArgs, options?: CreateApiHelperSolverDefinedOutcomeOptions) => { const {baseUrl = "https://api.merqube.com", fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") const url = `${baseUrl.replace(/\/+$/, '')}/helper/solver/defined_outcome${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(definedOutcomeSolverResponse, await res.json()) };