API Reference
Complete documentation for all floe functions and types.
Pricing Functions
blackScholes
Calculates the theoretical price of a European option using the Black-Scholes-Merton model.
import { blackScholes } from "@fullstackcraftllc/floe";
const price = blackScholes({
spot: 100, // Current price of underlying
strike: 105, // Option strike price
timeToExpiry: 0.25, // Time to expiration in years
riskFreeRate: 0.05, // Annual risk-free interest rate
volatility: 0.20, // Annualized volatility (as decimal)
optionType: "call", // "call" or "put"
dividendYield: 0.02 // Optional: continuous dividend yield (as decimal)
});
Implied Volatility
calculateImpliedVolatility
Uses the Black-Scholes model with iterative bisection to compute the implied volatility given an option price.
import { calculateImpliedVolatility } from "@fullstackcraftllc/floe";
const iv = calculateImpliedVolatility(
3.50, // price: observed option price
100, // spot: current underlying price
105, // strike: option strike price
0.05, // riskFreeRate: annual risk-free rate (as decimal)
0.02, // dividendYield: continuous dividend yield (as decimal)
0.25, // timeToExpiry: time to expiration in years
"call" // optionType: "call" or "put"
);
console.log(`Implied Volatility: ${iv.toFixed(2)}%`);
// Note: Returns IV as a percentage (e.g., 20.0 for 20% volatility)
Greeks
calculateGreeks
Calculate all Greeks up to third order for European call and put options. Returns a complete Greeks object containing the option price and all sensitivity measures.
import { calculateGreeks } from "@fullstackcraftllc/floe";
const greeks = calculateGreeks({
spot: 100,
strike: 105,
timeToExpiry: 0.25,
riskFreeRate: 0.05,
volatility: 0.20,
optionType: "call", // "call" or "put"
dividendYield: 0.02 // optional
});
// Access individual Greeks from the returned object:
console.log(`Price: ${greeks.price}`);
console.log(`Delta: ${greeks.delta}`);
console.log(`Gamma: ${greeks.gamma}`);
console.log(`Theta: ${greeks.theta}`); // per day
console.log(`Vega: ${greeks.vega}`); // per 1% volatility change
console.log(`Rho: ${greeks.rho}`); // per 1% rate change
Greeks Interface
The calculateGreeks function returns a Greeks object with all sensitivity measures:
interface Greeks {
price: number; // Option theoretical value
delta: number; // Rate of change of option price with respect to underlying
gamma: number; // Rate of change of delta with respect to underlying
theta: number; // Time decay (per day)
vega: number; // Sensitivity to volatility (per 1% change)
rho: number; // Sensitivity to interest rate (per 1% change)
vanna: number; // Sensitivity of delta to volatility
charm: number; // Delta decay (per day)
volga: number; // Sensitivity of vega to volatility (also known as vomma)
speed: number; // Rate of change of gamma
zomma: number; // Sensitivity of gamma to volatility
color: number; // Gamma decay
ultima: number; // Sensitivity of volga to volatility
}
Time Utilities
getTimeToExpirationInYears
Convert an expiration timestamp to time in years:
import { getTimeToExpirationInYears } from "@fullstackcraftllc/floe";
const expirationTimestamp = Date.now() + 30 * 24 * 60 * 60 * 1000; // 30 days from now
const timeToExpiry = getTimeToExpirationInYears(expirationTimestamp);
// Returns: ~0.0822 (30/365)
getMillisecondsToExpiration
Get milliseconds until expiration:
import { getMillisecondsToExpiration } from "@fullstackcraftllc/floe";
const ms = getMillisecondsToExpiration(expirationTimestamp);
Statistical Utilities
cumulativeNormalDistribution
Standard normal cumulative distribution function (CDF):
import { cumulativeNormalDistribution } from "@fullstackcraftllc/floe";
const prob = cumulativeNormalDistribution(1.96);
// Returns: ~0.975 (97.5% probability)
normalPDF
Standard normal probability density function:
import { normalPDF } from "@fullstackcraftllc/floe";
const density = normalPDF(0);
// Returns: ~0.3989 (peak of the normal curve)
IV Surfaces
getIVSurfaces
Generate implied volatility surfaces for all options across all expirations. Used as input for dealer exposure calculations.
import { getIVSurfaces, OptionChain } from "@fullstackcraftllc/floe";
const chain: OptionChain = {
symbol: 'SPY',
spot: 450.50,
riskFreeRate: 0.05,
dividendYield: 0.02,
options: normalizedOptions
};
const surfaces = getIVSurfaces('blackscholes', 'totalvariance', chain);
// Returns array of IVSurface objects with rawIVs and smoothedIVs
getIVForStrike
Lookup a specific IV from the surface:
import { getIVForStrike } from "@fullstackcraftllc/floe";
const iv = getIVForStrike(surfaces, expirationTimestamp, 'call', 105);
// Returns smoothed IV as percentage (e.g., 23.0 for 23%)
smoothTotalVarianceSmile
Apply total variance smoothing to a volatility smile:
import { smoothTotalVarianceSmile } from "@fullstackcraftllc/floe";
const smoothedIVs = smoothTotalVarianceSmile(
[90, 95, 100, 105, 110], // strikes
[22, 20, 18, 20, 22], // raw IVs as percentages
0.25 // time to expiry in years
);
Dealer Exposures
calculateGammaVannaCharmExposures
Calculate aggregate dealer exposures across an option chain:
import {
calculateGammaVannaCharmExposures,
getIVSurfaces
} from "@fullstackcraftllc/floe";
const ivSurfaces = getIVSurfaces('blackscholes', 'totalvariance', chain);
const exposureVariants = calculateGammaVannaCharmExposures(chain, ivSurfaces);
// exposureVariants contains canonical, stateWeighted, and flowDelta modes.
// Project canonical mode if downstream code expects ExposurePerExpiry.
const exposures = exposureVariants.map(e => ({ spotPrice: e.spotPrice, expiration: e.expiration, ...e.canonical }));
for (const expiry of exposures) {
console.log(`Expiration: ${new Date(expiry.expiration).toDateString()}`);
console.log(` Total Gamma: ${expiry.totalGammaExposure}`);
console.log(` Total Vanna: ${expiry.totalVannaExposure}`);
console.log(` Total Charm: ${expiry.totalCharmExposure}`);
}
calculateSharesNeededToCover
Calculate dealer hedging requirements:
import { calculateSharesNeededToCover } from "@fullstackcraftllc/floe";
const coverage = calculateSharesNeededToCover(
900_000_000, // shares outstanding
-5_000_000, // net exposure
450.50 // spot price
);
console.log(`Action: ${coverage.actionToCover}`);
console.log(`Shares: ${coverage.sharesToCover}`);
console.log(`Implied Move: ${coverage.impliedMoveToCover}%`);
Implied PDF
estimateImpliedProbabilityDistribution
Estimate implied probability distribution for a single expiration:
import { estimateImpliedProbabilityDistribution } from "@fullstackcraftllc/floe";
const result = estimateImpliedProbabilityDistribution("QQQ", 502.50, callOptions);
if (result.success) {
const dist = result.distribution;
console.log(`Mode: ${dist.mostLikelyPrice}`);
console.log(`Expected Move: ${dist.expectedMove}`);
}
estimateImpliedProbabilityDistributions
Process all expirations at once:
import { estimateImpliedProbabilityDistributions } from "@fullstackcraftllc/floe";
const distributions = estimateImpliedProbabilityDistributions("QQQ", 502.50, options);
getProbabilityInRange
Get probability of finishing in a price range:
import { getProbabilityInRange } from "@fullstackcraftllc/floe";
const prob = getProbabilityInRange(distribution, 495, 510);
// Returns probability (e.g., 0.65 for 65%)
getCumulativeProbability
Get cumulative probability up to a price:
import { getCumulativeProbability } from "@fullstackcraftllc/floe";
const prob = getCumulativeProbability(distribution, 500);
getQuantile
Get strike at a probability quantile:
import { getQuantile } from "@fullstackcraftllc/floe";
const p10 = getQuantile(distribution, 0.10); // 10th percentile
const p90 = getQuantile(distribution, 0.90); // 90th percentile
Exposure-Adjusted Implied PDF
estimateExposureAdjustedPDF
Modify the Breeden-Litzenberger implied PDF to account for dealer gamma, vanna, and charm positioning effects:
import {
estimateExposureAdjustedPDF,
calculateGammaVannaCharmExposures,
getIVSurfaces,
} from "@fullstackcraftllc/floe";
const ivSurfaces = getIVSurfaces('blackscholes', 'totalvariance', chain);
const exposureVariants = calculateGammaVannaCharmExposures(chain, ivSurfaces);
const exposures = exposureVariants.map(e => ({ spotPrice: e.spotPrice, expiration: e.expiration, ...e.canonical }));
// Use call options for the same expiration as the exposures
const callOptions = chain.options.filter(
o => o.optionType === 'call' && o.expirationTimestamp === exposures[0].expiration
);
const result = estimateExposureAdjustedPDF(
'SPY',
chain.spot,
callOptions,
exposures[0], // ExposurePerExpiry for the target expiration
{} // Optional: partial ExposureAdjustmentConfig overrides
);
if (result.success) {
console.log(`Baseline mode: $${result.baseline.mostLikelyPrice}`);
console.log(`Adjusted mode: $${result.adjusted.mostLikelyPrice}`);
console.log(`Mean shift: ${result.comparison.meanShift.toFixed(2)}`);
console.log(`Skew change: ${result.comparison.skewChange.toFixed(4)}`);
console.log(`Kurtosis change: ${result.comparison.kurtosisChange.toFixed(4)}`);
}
Preset Configurations
Four regime-specific configurations are exported:
import {
DEFAULT_ADJUSTMENT_CONFIG, // Balanced for normal markets
LOW_VOL_CONFIG, // Strong gamma pinning, moderate vanna
CRISIS_CONFIG, // Amplified vanna cascade, weak pinning
OPEX_CONFIG, // Strong pinning + accelerated charm
} from "@fullstackcraftllc/floe";
// Use a preset
const result = estimateExposureAdjustedPDF('SPY', spot, calls, exposures[0], CRISIS_CONFIG);
getEdgeAtPrice
Compare cumulative probabilities between baseline and adjusted distributions at a given price:
import { getEdgeAtPrice } from "@fullstackcraftllc/floe";
// Positive edge = adjusted PDF assigns more probability below this price
const edge = getEdgeAtPrice(result, 445.0);
console.log(`Edge at $445: ${(edge * 100).toFixed(2)}%`);
getSignificantAdjustmentLevels
Find strikes where the exposure adjustment has the largest effect:
import { getSignificantAdjustmentLevels } from "@fullstackcraftllc/floe";
const levels = getSignificantAdjustmentLevels(result, 0.01); // 1% threshold
for (const level of levels) {
console.log(`$${level.strike}: baseline ${(level.baselineProb * 100).toFixed(1)}% → adjusted ${(level.adjustedProb * 100).toFixed(1)}% (edge: ${(level.edge * 100).toFixed(2)}%)`);
}
Hedge Flow Analysis
Combines dealer gamma and vanna exposures into an actionable price-space response curve, paired with a charm integral that captures time-decay pressure independently.
deriveRegimeParams
Extract market regime parameters from the IV surface:
import { deriveRegimeParams, getIVSurfaces } from "@fullstackcraftllc/floe";
const ivSurfaces = getIVSurfaces('blackscholes', 'totalvariance', chain);
// Use a call surface for regime derivation
const callSurface = ivSurfaces.find(s => s.putCall === 'call');
const regime = deriveRegimeParams(callSurface, chain.spot);
console.log(`Regime: ${regime.regime}`); // 'calm' | 'normal' | 'stressed' | 'crisis'
console.log(`ATM IV: ${(regime.atmIV * 100).toFixed(1)}%`);
console.log(`Spot-Vol Correlation: ${regime.impliedSpotVolCorr.toFixed(3)}`);
console.log(`Vol of Vol: ${regime.impliedVolOfVol.toFixed(4)}`);
console.log(`Expected Daily Vol Move: ${(regime.expectedDailyVolMove * 100).toFixed(2)} pts`);
computeHedgeImpulseCurve
Compute the hedge impulse curve H(S) = GEX(S) - (k/S) × VEX(S) across a price grid:
import {
computeHedgeImpulseCurve,
calculateGammaVannaCharmExposures,
getIVSurfaces,
} from "@fullstackcraftllc/floe";
const ivSurfaces = getIVSurfaces('blackscholes', 'totalvariance', chain);
const exposureVariants = calculateGammaVannaCharmExposures(chain, ivSurfaces);
const exposures = exposureVariants.map(e => ({ spotPrice: e.spotPrice, expiration: e.expiration, ...e.canonical }));
const callSurface = ivSurfaces.find(s => s.putCall === 'call');
const curve = computeHedgeImpulseCurve(
exposures[0], // ExposurePerExpiry for target expiration
callSurface, // IVSurface for regime/k derivation
{ // Optional HedgeImpulseConfig
rangePercent: 3, // ±3% price grid (default)
stepPercent: 0.05, // 0.05% grid step (default)
kernelWidthStrikes: 2, // kernel = 2 × strike spacing (default)
}
);
// Instantaneous reading at current spot
console.log(`Impulse at spot: ${curve.impulseAtSpot.toFixed(0)}`);
console.log(`Slope at spot: ${curve.slopeAtSpot.toFixed(0)}`);
console.log(`Regime: ${curve.regime}`);
console.log(`Spot-vol coupling k: ${curve.spotVolCoupling.toFixed(2)}`);
console.log(`Strike spacing: ${curve.strikeSpacing}`);
console.log(`Kernel width: ${curve.kernelWidth.toFixed(1)} points`);
// Key levels
console.log(`
Zero crossings (flip levels):`);
for (const zc of curve.zeroCrossings) {
console.log(` $${zc.price.toFixed(1)} (${zc.direction})`);
}
console.log(`
Attractors (positive impulse basins):`);
for (const ext of curve.extrema.filter(e => e.type === 'basin')) {
console.log(` $${ext.price.toFixed(1)} (impulse: ${ext.impulse.toFixed(0)})`);
}
console.log(`
Accelerators (negative impulse peaks):`);
for (const ext of curve.extrema.filter(e => e.type === 'peak')) {
console.log(` $${ext.price.toFixed(1)} (impulse: ${ext.impulse.toFixed(0)})`);
}
// Directional asymmetry
const a = curve.asymmetry;
console.log(`
Directional bias: ${a.bias}`);
console.log(`Asymmetry ratio: ${a.asymmetryRatio.toFixed(2)}`);
// Nearest attractors
if (curve.nearestAttractorAbove) {
console.log(`Nearest attractor above: $${curve.nearestAttractorAbove.toFixed(1)}`);
}
if (curve.nearestAttractorBelow) {
console.log(`Nearest attractor below: $${curve.nearestAttractorBelow.toFixed(1)}`);
}
computeCharmIntegral
Compute the cumulative charm exposure from now until expiration:
import { computeCharmIntegral } from "@fullstackcraftllc/floe";
const charm = computeCharmIntegral(
exposures[0], // ExposurePerExpiry
{ timeStepMinutes: 15 } // Optional CharmIntegralConfig
);
console.log(`Minutes remaining: ${charm.minutesRemaining.toFixed(0)}`);
console.log(`Total charm to close: ${charm.totalCharmToClose.toLocaleString()}`);
console.log(`Direction: ${charm.direction}`); // 'buying' | 'selling' | 'neutral'
// Time-bucketed curve for visualization
console.log(`\nCharm curve (${charm.buckets.length} buckets):`);
for (const bucket of charm.buckets.slice(0, 5)) {
console.log(` ${bucket.minutesRemaining.toFixed(0)} min remaining: cumulative ${bucket.cumulativeCEX.toLocaleString()}`);
}
// Per-strike breakdown (top contributors)
console.log(`\nTop charm contributors:`);
for (const sc of charm.strikeContributions.slice(0, 5)) {
console.log(` $${sc.strike}: ${sc.charmExposure.toLocaleString()} (${(sc.fractionOfTotal * 100).toFixed(1)}%)`);
}
analyzeHedgeFlow
Convenience function that computes both the impulse curve and charm integral in a single call:
import { analyzeHedgeFlow } from "@fullstackcraftllc/floe";
const analysis = analyzeHedgeFlow(
exposures[0], // ExposurePerExpiry
callSurface, // IVSurface
{ rangePercent: 3, kernelWidthStrikes: 2 }, // Optional impulse config
{ timeStepMinutes: 15 } // Optional charm config
);
// Access both panels
const { impulseCurve, charmIntegral, regimeParams } = analysis;
console.log(`Regime: ${regimeParams.regime}`);
console.log(`Impulse regime: ${impulseCurve.regime}`);
console.log(`Impulse at spot: ${impulseCurve.impulseAtSpot.toFixed(0)}`);
console.log(`Charm to close: ${charmIntegral.totalCharmToClose.toLocaleString()}`);
console.log(`Charm direction: ${charmIntegral.direction}`);
Model-Free Implied Volatility
Computes implied volatility from the full option chain using the CBOE variance swap methodology. Model-free (no Black-Scholes inversion required) and applicable to any optionable underlying.
computeVarianceSwapIV
Compute model-free implied variance for a single expiration:
import { computeVarianceSwapIV } from "@fullstackcraftllc/floe";
// Pass all options for one expiration (both calls and puts)
const todayOptions = chain.options.filter(
o => o.expirationTimestamp === todayExpiration
);
const result = computeVarianceSwapIV(todayOptions, chain.spot, 0.05);
console.log(`Implied Vol: ${(result.impliedVolatility * 100).toFixed(1)}%`);
console.log(`Forward: $${result.forward.toFixed(2)}`);
console.log(`K₀ (ATM strike): $${result.k0}`);
console.log(`Time to expiry: ${(result.timeToExpiry * 365).toFixed(2)} days`);
console.log(`Strikes contributing: ${result.numStrikes}`);
console.log(`Put contribution: ${result.putContribution.toFixed(6)}`);
console.log(`Call contribution: ${result.callContribution.toFixed(6)}`);
computeImpliedVolatility
Single-term or two-term interpolated implied volatility. Supports the standard CBOE VIX interpolation when two expirations are provided:
import { computeImpliedVolatility } from "@fullstackcraftllc/floe";
// Single-term: IV from one expiration
const singleTerm = computeImpliedVolatility(todayOptions, chain.spot, 0.05);
console.log(`0DTE IV: ${(singleTerm.impliedVolatility * 100).toFixed(1)}%`);
// Two-term interpolation: bracket a target maturity
const tomorrowOptions = chain.options.filter(
o => o.expirationTimestamp === tomorrowExpiration
);
const interpolated = computeImpliedVolatility(
todayOptions, // near-term options
chain.spot,
0.05,
tomorrowOptions, // far-term options
1 // target: 1-day constant maturity
);
console.log(`Interpolated IV: ${(interpolated.impliedVolatility * 100).toFixed(1)}%`);
console.log(`Is interpolated: ${interpolated.isInterpolated}`);
console.log(`Near-term IV: ${(interpolated.nearTerm.impliedVolatility * 100).toFixed(1)}%`);
console.log(`Far-term IV: ${(interpolated.farTerm?.impliedVolatility ?? 0 * 100).toFixed(1)}%`);
Realized Volatility
Computes annualized realized volatility from price observations using quadratic variation. Tick-based, stateless, no windowing — pass all observations and it computes from the full series.
computeRealizedVolatility
import { computeRealizedVolatility, PriceObservation } from "@fullstackcraftllc/floe";
// Accumulate price observations from streaming data
const observations: PriceObservation[] = [
{ price: 600.10, timestamp: 1708099800000 },
{ price: 600.25, timestamp: 1708099860000 },
{ price: 599.80, timestamp: 1708099920000 },
// ... every tick from the session
];
const rv = computeRealizedVolatility(observations);
console.log(`Realized Vol: ${(rv.realizedVolatility * 100).toFixed(1)}%`);
console.log(`Observations: ${rv.numObservations}`);
console.log(`Returns computed: ${rv.numReturns}`);
console.log(`Elapsed: ${rv.elapsedMinutes.toFixed(0)} minutes`);
console.log(`Quadratic variation: ${rv.quadraticVariation.toFixed(8)}`);
OCC Symbol Utilities
buildOCCSymbol
Build an OCC-formatted option symbol:
import { buildOCCSymbol } from "@fullstackcraftllc/floe";
const symbol = buildOCCSymbol({
symbol: 'AAPL',
expiration: '2025-01-17',
optionType: 'call',
strike: 150,
padded: false // optional, default false
});
// Returns: 'AAPL250117C00150000'
parseOCCSymbol
Parse an OCC symbol into components:
import { parseOCCSymbol } from "@fullstackcraftllc/floe";
const parsed = parseOCCSymbol('AAPL250117C00150000');
// Returns: { symbol: 'AAPL', expiration: Date, optionType: 'call', strike: 150 }
generateStrikesAroundSpot
Generate strike prices around a spot price:
import { generateStrikesAroundSpot } from "@fullstackcraftllc/floe";
const strikes = generateStrikesAroundSpot({
spot: 450,
strikesAbove: 10,
strikesBelow: 10,
strikeIncrementInDollars: 5
});
// Returns: [400, 405, 410, ..., 495, 500]
generateOCCSymbolsForStrikes
Generate OCC symbols for specific strikes:
import { generateOCCSymbolsForStrikes } from "@fullstackcraftllc/floe";
const symbols = generateOCCSymbolsForStrikes(
'SPY',
'2025-12-20',
[440, 445, 450, 455, 460],
['call', 'put'] // optional, default both
);
generateOCCSymbolsAroundSpot
Convenience function combining strike generation and OCC symbol creation:
import { generateOCCSymbolsAroundSpot } from "@fullstackcraftllc/floe";
const symbols = generateOCCSymbolsAroundSpot('SPY', '2025-12-20', 600, {
strikesAbove: 10,
strikesBelow: 10,
strikeIncrementInDollars: 1
});
Broker Adapters
createOptionChain
Create an option chain from raw broker data:
import { createOptionChain } from "@fullstackcraftllc/floe";
const chain = createOptionChain(
'SPY', // symbol
450.50, // spot
0.05, // riskFreeRate
0.02, // dividendYield
rawBrokerData, // raw options from broker
'schwab' // broker name for adapter selection
);
getAdapter
Get a specific broker adapter:
import { getAdapter } from "@fullstackcraftllc/floe";
const adapter = getAdapter('schwab');
const normalizedOption = adapter(rawOptionData);
Available Adapters
import {
genericAdapter,
schwabAdapter,
ibkrAdapter,
tdaAdapter,
brokerAdapters
} from "@fullstackcraftllc/floe";
// brokerAdapters is a map: { generic, schwab, ibkr, tda }
Real-Time Market Data (FloeClient)
Supported Brokers
| Broker | Enum Value | Authentication |
|---|---|---|
| Tradier | Broker.TRADIER |
API Token |
| TastyTrade | Broker.TASTYTRADE |
Session Token |
| TradeStation | Broker.TRADESTATION |
OAuth Token |
| Charles Schwab | Broker.SCHWAB |
OAuth Token |
| Interactive Brokers | Broker.IBKR |
OAuth Token |
Basic Usage
import { FloeClient, Broker } from "@fullstackcraftllc/floe";
const client = new FloeClient({ verbose: false });
await client.connect(Broker.TRADIER, 'your-api-token');
client.on('optionUpdate', (option) => {
console.log(`${option.occSymbol}: ${option.bid} / ${option.ask}`);
});
client.on('tickerUpdate', (ticker) => {
console.log(`${ticker.symbol}: ${ticker.spot}`);
});
client.subscribeToOptions(['SPY251220C00600000']);
client.subscribeToTickers(['SPY']);
await client.fetchOpenInterest();
client.disconnect();
Direct Broker Client Access
import { TradierClient, TastyTradeClient, TradeStationClient } from "@fullstackcraftllc/floe";
// Use broker clients directly for advanced scenarios
const tradier = new TradierClient(token, { verbose: true });
Core Types
OptionType
type OptionType = 'call' | 'put';
BlackScholesParams
interface BlackScholesParams {
spot: number;
strike: number;
timeToExpiry: number;
volatility: number;
riskFreeRate: number;
optionType: OptionType;
dividendYield?: number;
}
NormalizedOption
interface NormalizedOption {
occSymbol: string;
underlying: string;
strike: number;
expiration: string;
expirationTimestamp: number;
optionType: OptionType;
bid: number;
bidSize: number;
ask: number;
askSize: number;
mark: number;
last: number;
volume: number;
openInterest: number;
liveOpenInterest?: number;
impliedVolatility: number;
timestamp: number;
}
NormalizedTicker
interface NormalizedTicker {
symbol: string;
spot: number;
bid: number;
bidSize: number;
ask: number;
askSize: number;
last: number;
volume: number;
timestamp: number;
}
OptionChain
interface OptionChain {
symbol: string;
spot: number;
riskFreeRate: number;
dividendYield: number;
options: NormalizedOption[];
}
IVSurface
interface IVSurface {
expirationDate: number;
putCall: OptionType;
strikes: number[];
rawIVs: number[];
smoothedIVs: number[];
}
ExposureVector
interface ExposureVector {
gammaExposure: number;
vannaExposure: number;
charmExposure: number;
netExposure: number;
}
ExposureModeBreakdown
interface ExposureModeBreakdown {
totalGammaExposure: number;
totalVannaExposure: number;
totalCharmExposure: number;
totalNetExposure: number;
strikeOfMaxGamma: number;
strikeOfMinGamma: number;
strikeOfMaxVanna: number;
strikeOfMinVanna: number;
strikeOfMaxCharm: number;
strikeOfMinCharm: number;
strikeOfMaxNet: number;
strikeOfMinNet: number;
strikeExposures: StrikeExposure[];
}
StrikeExposureVariants
interface StrikeExposureVariants {
strikePrice: number;
canonical: ExposureVector;
stateWeighted: ExposureVector;
flowDelta: ExposureVector;
}
ExposureVariantsPerExpiry
interface ExposureVariantsPerExpiry {
spotPrice: number;
expiration: number;
canonical: ExposureModeBreakdown;
stateWeighted: ExposureModeBreakdown;
flowDelta: ExposureModeBreakdown;
strikeExposureVariants: StrikeExposureVariants[];
}
ExposurePerExpiry (Canonical Projection)
interface ExposurePerExpiry {
spotPrice: number;
expiration: number;
totalGammaExposure: number;
totalVannaExposure: number;
totalCharmExposure: number;
totalNetExposure: number;
strikeOfMaxGamma: number;
strikeOfMinGamma: number;
strikeOfMaxVanna: number;
strikeOfMinVanna: number;
strikeOfMaxCharm: number;
strikeOfMinCharm: number;
strikeOfMaxNet: number;
strikeOfMinNet: number;
strikeExposures: StrikeExposure[];
}
ImpliedProbabilityDistribution
interface ImpliedProbabilityDistribution {
symbol: string;
expiryDate: number;
calculationTimestamp: number;
underlyingPrice: number;
strikeProbabilities: StrikeProbability[];
mostLikelyPrice: number;
medianPrice: number;
expectedValue: number;
expectedMove: number;
tailSkew: number;
cumulativeProbabilityAboveSpot: number;
cumulativeProbabilityBelowSpot: number;
}
RegimeParams
type MarketRegime = 'calm' | 'normal' | 'stressed' | 'crisis';
interface RegimeParams {
regime: MarketRegime;
atmIV: number; // decimal (e.g. 0.18 for 18%)
impliedSpotVolCorr: number; // typically [-0.9, -0.5] for indices
impliedVolOfVol: number;
expectedDailyMove: number;
expectedDailyVolMove: number;
}
HedgeImpulseCurve
interface HedgeImpulseCurve {
spot: number;
expiration: number;
computedAt: number;
spotVolCoupling: number; // k coefficient derived from IV skew
kernelWidth: number; // in price units
strikeSpacing: number; // detected modal strike spacing
curve: HedgeImpulsePoint[]; // full price grid
impulseAtSpot: number; // H(S) at current spot
slopeAtSpot: number; // dH/dS at current spot
zeroCrossings: ZeroCrossing[];
extrema: ImpulseExtremum[];
asymmetry: DirectionalAsymmetry;
regime: ImpulseRegime; // 'pinned' | 'expansion' | 'squeeze-up' | 'squeeze-down' | 'neutral'
nearestAttractorAbove: number | null;
nearestAttractorBelow: number | null;
}
interface HedgeImpulsePoint {
price: number;
gamma: number; // kernel-smoothed GEX at this price
vanna: number; // kernel-smoothed VEX at this price
impulse: number; // gamma - (k/S) * vanna
}
interface ZeroCrossing {
price: number;
direction: 'rising' | 'falling';
}
interface ImpulseExtremum {
price: number;
impulse: number;
type: 'basin' | 'peak'; // basin = attractor, peak = accelerator
}
interface DirectionalAsymmetry {
upside: number;
downside: number;
integrationRangePercent: number;
bias: 'up' | 'down' | 'neutral';
asymmetryRatio: number;
}
CharmIntegral
interface CharmIntegral {
spot: number;
expiration: number;
computedAt: number;
minutesRemaining: number;
totalCharmToClose: number;
direction: 'buying' | 'selling' | 'neutral';
buckets: CharmBucket[];
strikeContributions: Array<{
strike: number;
charmExposure: number;
fractionOfTotal: number;
}>;
}
interface CharmBucket {
minutesRemaining: number;
instantaneousCEX: number;
cumulativeCEX: number;
}
VarianceSwapResult
interface VarianceSwapResult {
impliedVolatility: number; // annualized, decimal
annualizedVariance: number;
forward: number; // forward price F
k0: number; // ATM strike
timeToExpiry: number;
expiration: number;
numStrikes: number;
putContribution: number;
callContribution: number;
}
ImpliedVolatilityResult
interface ImpliedVolatilityResult {
impliedVolatility: number;
nearTerm: VarianceSwapResult;
farTerm: VarianceSwapResult | null;
targetDays: number | null;
isInterpolated: boolean;
}
PriceObservation
interface PriceObservation {
price: number;
timestamp: number; // milliseconds
}
RealizedVolatilityResult
interface RealizedVolatilityResult {
realizedVolatility: number; // annualized, decimal
annualizedVariance: number;
quadraticVariation: number; // raw sum of squared log returns
numObservations: number;
numReturns: number;
elapsedMinutes: number;
elapsedYears: number;
firstObservation: number;
lastObservation: number;
}
Vol Response Model
buildVolResponseObservation(current, previous)
Build a single observation for the vol response regression from consecutive IV/RV/spot readings.
import { buildVolResponseObservation } from "@fullstackcraftllc/floe";
const observation = buildVolResponseObservation(
{ iv: 0.22, rv: 0.18, spot: 600.50, timestamp: Date.now() },
{ iv: 0.215, spot: 600.00 }
);
Parameters:
| Name | Type | Description |
|---|---|---|
current |
{ iv: number; rv: number; spot: number; timestamp: number } |
Current tick values (IV and RV as annualized decimals) |
previous |
{ iv: number; spot: number } |
Previous tick values |
Returns: VolResponseObservation
computeVolResponseZScore(observations, config?)
Fit an expanding-window OLS regression on accumulated observations and compute a z-score classifying whether IV is bid or offered relative to the price path.
Regression model: ΔIV(t) ~ α + β₁·return + β₂·|return| + β₃·RV + β₄·IV_level
import { computeVolResponseZScore } from "@fullstackcraftllc/floe";
const result = computeVolResponseZScore(observations, {
minObservations: 30,
volBidThreshold: 1.5,
volOfferedThreshold: -1.5,
});
if (result.isValid) {
console.log("Z-Score:", result.zScore.toFixed(2));
console.log("Signal:", result.signal); // 'vol_bid' | 'vol_offered' | 'neutral'
console.log("R²:", result.rSquared.toFixed(3));
}
Parameters:
| Name | Type | Description |
|---|---|---|
observations |
VolResponseObservation[] |
All accumulated observations for the session |
config |
VolResponseConfig |
Optional configuration overrides |
Returns: VolResponseResult
VolResponseObservation
interface VolResponseObservation {
timestamp: number; // milliseconds
deltaIV: number; // IV(t) - IV(t-1), as decimal
spotReturn: number; // ln(S(t) / S(t-1))
absSpotReturn: number; // |spotReturn|
rvLevel: number; // current RV, annualized decimal
ivLevel: number; // current IV, annualized decimal
}
VolResponseCoefficients
interface VolResponseCoefficients {
intercept: number; // regression intercept
betaReturn: number; // signed spot return (spot-vol correlation)
betaAbsReturn: number; // |return| (vol-of-vol / convexity response)
betaRV: number; // RV level (RV mean-reversion effect)
betaIVLevel: number; // IV level (IV mean-reversion effect)
}
VolResponseConfig
interface VolResponseConfig {
minObservations?: number; // default: 30
volBidThreshold?: number; // default: 1.5
volOfferedThreshold?: number; // default: -1.5
}
VolResponseResult
interface VolResponseResult {
isValid: boolean;
minObservations: number;
numObservations: number;
coefficients: VolResponseCoefficients;
rSquared: number;
residualStdDev: number;
expectedDeltaIV: number;
observedDeltaIV: number;
residual: number;
zScore: number;
signal: 'vol_bid' | 'vol_offered' | 'neutral' | 'insufficient_data';
timestamp: number;
}