@fullstackcraftllc/floe 0.0.13 → 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +8 -8
  2. package/dist/client/FloeClient.d.ts +5 -1
  3. package/dist/client/FloeClient.js +54 -0
  4. package/dist/client/brokers/IBKRClient.d.ts +324 -0
  5. package/dist/client/brokers/IBKRClient.js +797 -0
  6. package/dist/client/brokers/TradierClient.js +7 -6
  7. package/dist/hedgeflow/charm.d.ts +23 -0
  8. package/dist/hedgeflow/charm.js +113 -0
  9. package/dist/hedgeflow/curve.d.ts +27 -0
  10. package/dist/hedgeflow/curve.js +315 -0
  11. package/dist/hedgeflow/index.d.ts +33 -0
  12. package/dist/hedgeflow/index.js +52 -0
  13. package/dist/hedgeflow/regime.d.ts +7 -0
  14. package/dist/hedgeflow/regime.js +99 -0
  15. package/dist/hedgeflow/types.d.ts +185 -0
  16. package/dist/hedgeflow/types.js +2 -0
  17. package/dist/impliedpdf/adjusted.d.ts +173 -0
  18. package/dist/impliedpdf/adjusted.js +500 -0
  19. package/dist/impliedpdf/index.d.ts +1 -0
  20. package/dist/impliedpdf/index.js +10 -0
  21. package/dist/index.d.ts +8 -2
  22. package/dist/index.js +27 -1
  23. package/dist/iv/index.d.ts +52 -0
  24. package/dist/iv/index.js +287 -0
  25. package/dist/iv/types.d.ts +40 -0
  26. package/dist/iv/types.js +2 -0
  27. package/dist/pressure/grid.d.ts +14 -0
  28. package/dist/pressure/grid.js +220 -0
  29. package/dist/pressure/index.d.ts +5 -0
  30. package/dist/pressure/index.js +22 -0
  31. package/dist/pressure/ivpath.d.ts +31 -0
  32. package/dist/pressure/ivpath.js +304 -0
  33. package/dist/pressure/normalize.d.ts +6 -0
  34. package/dist/pressure/normalize.js +76 -0
  35. package/dist/pressure/regime.d.ts +7 -0
  36. package/dist/pressure/regime.js +99 -0
  37. package/dist/pressure/types.d.ts +182 -0
  38. package/dist/pressure/types.js +2 -0
  39. package/dist/rv/index.d.ts +26 -0
  40. package/dist/rv/index.js +81 -0
  41. package/dist/rv/types.d.ts +32 -0
  42. package/dist/rv/types.js +2 -0
  43. package/dist/utils/indexOptions.js +2 -1
  44. package/package.json +1 -1
@@ -0,0 +1,500 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getQuantile = exports.getCumulativeProbability = exports.getProbabilityInRange = exports.OPEX_CONFIG = exports.CRISIS_CONFIG = exports.LOW_VOL_CONFIG = exports.DEFAULT_ADJUSTMENT_CONFIG = void 0;
4
+ exports.estimateExposureAdjustedPDF = estimateExposureAdjustedPDF;
5
+ exports.getEdgeAtPrice = getEdgeAtPrice;
6
+ exports.getSignificantAdjustmentLevels = getSignificantAdjustmentLevels;
7
+ const index_1 = require("./index");
8
+ Object.defineProperty(exports, "getProbabilityInRange", { enumerable: true, get: function () { return index_1.getProbabilityInRange; } });
9
+ Object.defineProperty(exports, "getCumulativeProbability", { enumerable: true, get: function () { return index_1.getCumulativeProbability; } });
10
+ Object.defineProperty(exports, "getQuantile", { enumerable: true, get: function () { return index_1.getQuantile; } });
11
+ // ============================================================================
12
+ // Default Configuration
13
+ // ============================================================================
14
+ /**
15
+ * Default configuration tuned for SPX-like indices
16
+ */
17
+ exports.DEFAULT_ADJUSTMENT_CONFIG = {
18
+ gamma: {
19
+ enabled: true,
20
+ attractorStrength: 0.3,
21
+ repellentStrength: 0.3,
22
+ threshold: 1000000, // $1M GEX
23
+ decayRate: 2.0,
24
+ },
25
+ vanna: {
26
+ enabled: true,
27
+ spotVolBeta: -3.0,
28
+ maxTailMultiplier: 2.5,
29
+ feedbackIterations: 3,
30
+ },
31
+ charm: {
32
+ enabled: true,
33
+ timeHorizon: 'daily',
34
+ shiftScale: 1.0,
35
+ },
36
+ };
37
+ /**
38
+ * Configuration for low volatility / grinding markets
39
+ */
40
+ exports.LOW_VOL_CONFIG = {
41
+ gamma: {
42
+ enabled: true,
43
+ attractorStrength: 0.4, // Stronger pinning
44
+ repellentStrength: 0.2,
45
+ threshold: 500000,
46
+ decayRate: 1.5,
47
+ },
48
+ vanna: {
49
+ enabled: true,
50
+ spotVolBeta: -2.0, // Less reactive
51
+ maxTailMultiplier: 1.5,
52
+ feedbackIterations: 2,
53
+ },
54
+ charm: {
55
+ enabled: true,
56
+ timeHorizon: 'daily',
57
+ shiftScale: 1.5, // Charm matters more in calm markets
58
+ },
59
+ };
60
+ /**
61
+ * Configuration for high volatility / crisis markets
62
+ */
63
+ exports.CRISIS_CONFIG = {
64
+ gamma: {
65
+ enabled: true,
66
+ attractorStrength: 0.1, // Pins don't hold
67
+ repellentStrength: 0.5, // -GEX acceleration dominates
68
+ threshold: 2000000,
69
+ decayRate: 3.0,
70
+ },
71
+ vanna: {
72
+ enabled: true,
73
+ spotVolBeta: -5.0, // Highly reactive
74
+ maxTailMultiplier: 3.0,
75
+ feedbackIterations: 5,
76
+ },
77
+ charm: {
78
+ enabled: true,
79
+ timeHorizon: 'intraday',
80
+ shiftScale: 0.5, // Charm less important vs gamma/vanna
81
+ },
82
+ };
83
+ /**
84
+ * Configuration for OPEX week
85
+ */
86
+ exports.OPEX_CONFIG = {
87
+ gamma: {
88
+ enabled: true,
89
+ attractorStrength: 0.5, // Strong pinning into expiry
90
+ repellentStrength: 0.4,
91
+ threshold: 1000000,
92
+ decayRate: 2.5,
93
+ },
94
+ vanna: {
95
+ enabled: true,
96
+ spotVolBeta: -3.0,
97
+ maxTailMultiplier: 2.0,
98
+ feedbackIterations: 3,
99
+ },
100
+ charm: {
101
+ enabled: true,
102
+ timeHorizon: 'intraday', // Charm accelerates into expiry
103
+ shiftScale: 2.0,
104
+ },
105
+ };
106
+ // ============================================================================
107
+ // Core Functions
108
+ // ============================================================================
109
+ /**
110
+ * Estimate an exposure-adjusted implied probability distribution.
111
+ *
112
+ * This function takes the standard market-implied PDF (Breeden-Litzenberger)
113
+ * and adjusts it based on dealer Greek exposures to produce a "mechanically-informed"
114
+ * probability distribution that accounts for:
115
+ *
116
+ * - **Gamma**: Creates "sticky" zones (+GEX) where price pins, and "slippery" zones
117
+ * (-GEX) where price accelerates through
118
+ * - **Vanna**: Fattens tails based on the IV-spot feedback loop (selloffs spike IV,
119
+ * which forces more selling via negative vanna)
120
+ * - **Charm**: Shifts the mean based on predictable delta decay over time
121
+ *
122
+ * @param symbol - Underlying ticker symbol
123
+ * @param underlyingPrice - Current spot price
124
+ * @param callOptions - Call options for a single expiry
125
+ * @param exposures - Exposure metrics from calculateGammaVannaCharmExposures()
126
+ * @param config - Adjustment configuration (uses defaults if not provided)
127
+ * @returns Baseline and adjusted PDFs with comparison metrics
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * // Get exposures first
132
+ * const allExposures = calculateGammaVannaCharmExposures(chain, ivSurfaces);
133
+ * const expiryExposures = allExposures.find(e => e.expiration === targetExpiry);
134
+ *
135
+ * // Calculate adjusted PDF
136
+ * const result = estimateExposureAdjustedPDF(
137
+ * 'SPX',
138
+ * 4520,
139
+ * callOptionsForExpiry,
140
+ * expiryExposures
141
+ * );
142
+ *
143
+ * // Compare probabilities
144
+ * const target = 4400;
145
+ * const baselineProb = getCumulativeProbability(result.baseline, target);
146
+ * const adjustedProb = getCumulativeProbability(result.adjusted, target);
147
+ * console.log(`Market says ${baselineProb}% chance of ${target}`);
148
+ * console.log(`Flow-adjusted: ${adjustedProb}% chance`);
149
+ * ```
150
+ */
151
+ function estimateExposureAdjustedPDF(symbol, underlyingPrice, callOptions, exposures, config = {}) {
152
+ // Merge config with defaults
153
+ const cfg = mergeConfig(config);
154
+ // Step 1: Get baseline PDF using existing Breeden-Litzenberger implementation
155
+ const baselineResult = (0, index_1.estimateImpliedProbabilityDistribution)(symbol, underlyingPrice, callOptions);
156
+ if (!baselineResult.success) {
157
+ return { success: false, error: baselineResult.error };
158
+ }
159
+ const baseline = baselineResult.distribution;
160
+ // Step 2: Calculate gamma modifiers
161
+ const gammaModifiers = cfg.gamma.enabled
162
+ ? calculateGammaModifiers(baseline.strikeProbabilities, exposures, underlyingPrice, cfg.gamma)
163
+ : baseline.strikeProbabilities.map(() => 1.0);
164
+ // Step 3: Calculate vanna modifiers
165
+ const vannaModifiers = cfg.vanna.enabled
166
+ ? calculateVannaModifiers(baseline.strikeProbabilities, exposures, underlyingPrice, cfg.vanna)
167
+ : baseline.strikeProbabilities.map(() => 1.0);
168
+ // Step 4: Calculate charm-induced mean shift
169
+ const charmShift = cfg.charm.enabled
170
+ ? calculateCharmShift(exposures, underlyingPrice, cfg.charm)
171
+ : 0;
172
+ // Step 5: Apply modifiers to create adjusted probabilities
173
+ const adjustedProbabilities = applyModifiers(baseline.strikeProbabilities, gammaModifiers, vannaModifiers, charmShift);
174
+ // Step 6: Normalize to ensure probabilities sum to 1
175
+ const normalizedProbabilities = normalizeProbabilities(adjustedProbabilities);
176
+ // Step 7: Recalculate distribution statistics
177
+ const adjusted = recalculateDistributionStats(baseline, normalizedProbabilities, underlyingPrice);
178
+ // Step 8: Calculate comparison metrics
179
+ const comparison = calculateComparison(baseline, adjusted, gammaModifiers, vannaModifiers, charmShift);
180
+ return {
181
+ baseline,
182
+ adjusted,
183
+ gammaModifiers,
184
+ vannaModifiers,
185
+ charmShift,
186
+ comparison,
187
+ };
188
+ }
189
+ // ============================================================================
190
+ // Modifier Calculations
191
+ // ============================================================================
192
+ /**
193
+ * Calculate gamma-based probability modifiers.
194
+ *
195
+ * Positive GEX creates "attractors" - price tends to pin at these levels.
196
+ * Negative GEX creates "repellents" - price accelerates through these levels.
197
+ *
198
+ * Uses 1/d² decay similar to the "local hedge pressure field" concept.
199
+ */
200
+ function calculateGammaModifiers(strikeProbabilities, exposures, spot, config) {
201
+ const modifiers = [];
202
+ // Find max absolute GEX for normalization
203
+ const maxGex = Math.max(...exposures.strikeExposures.map(e => Math.abs(e.gammaExposure)), 1 // Prevent division by zero
204
+ );
205
+ for (const prob of strikeProbabilities) {
206
+ let modifier = 1.0;
207
+ for (const exposure of exposures.strikeExposures) {
208
+ // Skip insignificant exposures
209
+ if (Math.abs(exposure.gammaExposure) < config.threshold) {
210
+ continue;
211
+ }
212
+ // Distance normalized by spot price
213
+ const distance = Math.abs(prob.strike - exposure.strikePrice) / spot;
214
+ // Influence decays with distance squared (electrostatic/gravitational)
215
+ const influence = 1 / (1 + config.decayRate * distance * distance);
216
+ // Normalized GEX (-1 to 1)
217
+ const normalizedGex = exposure.gammaExposure / maxGex;
218
+ if (exposure.gammaExposure > 0) {
219
+ // Positive GEX = attractor = increase probability density
220
+ modifier *= 1 + config.attractorStrength * normalizedGex * influence;
221
+ }
222
+ else {
223
+ // Negative GEX = repellent = decrease probability density
224
+ modifier *= 1 - config.repellentStrength * Math.abs(normalizedGex) * influence;
225
+ }
226
+ }
227
+ // Clamp to reasonable bounds
228
+ modifiers.push(Math.max(0.1, Math.min(3.0, modifier)));
229
+ }
230
+ return modifiers;
231
+ }
232
+ /**
233
+ * Calculate vanna-based probability modifiers.
234
+ *
235
+ * Vanna creates feedback loops:
236
+ * - Spot drops → IV spikes → negative vanna forces selling → spot drops more
237
+ *
238
+ * This fattens the left tail beyond what the market-implied PDF shows.
239
+ */
240
+ function calculateVannaModifiers(strikeProbabilities, exposures, spot, config) {
241
+ const modifiers = [];
242
+ // Sum vanna below and above spot
243
+ const vannaBelow = exposures.strikeExposures
244
+ .filter(e => e.strikePrice < spot)
245
+ .reduce((sum, e) => sum + e.vannaExposure, 0);
246
+ const vannaAbove = exposures.strikeExposures
247
+ .filter(e => e.strikePrice > spot)
248
+ .reduce((sum, e) => sum + e.vannaExposure, 0);
249
+ for (const prob of strikeProbabilities) {
250
+ let modifier = 1.0;
251
+ // Percentage move to this strike
252
+ const movePercent = (prob.strike - spot) / spot;
253
+ if (movePercent < 0) {
254
+ // Downside move: estimate IV spike
255
+ const ivSpike = -movePercent * Math.abs(config.spotVolBeta);
256
+ // Vanna flow from IV change
257
+ // Negative vanna * positive IV change = selling pressure
258
+ const vannaFlow = vannaBelow * ivSpike;
259
+ if (vannaFlow < 0) {
260
+ // Selling pressure increases left tail probability
261
+ let cumulativeEffect = 0;
262
+ let currentFlow = Math.abs(vannaFlow);
263
+ // Iterate feedback loop (each iteration dampens ~50%)
264
+ for (let i = 0; i < config.feedbackIterations; i++) {
265
+ cumulativeEffect += currentFlow;
266
+ currentFlow *= 0.5;
267
+ }
268
+ // Scale to modifier (normalize by spot * $1M for comparability)
269
+ const effectScale = cumulativeEffect / (spot * 1000000);
270
+ modifier = 1 + Math.min(config.maxTailMultiplier - 1, effectScale);
271
+ }
272
+ }
273
+ else if (movePercent > 0) {
274
+ // Upside move: IV typically compresses (asymmetrically less than down moves)
275
+ const ivCompress = movePercent * Math.abs(config.spotVolBeta) * 0.5;
276
+ // Positive vanna + IV compression = less buying pressure on upside
277
+ const vannaFlow = vannaAbove * (-ivCompress);
278
+ if (vannaFlow > 0) {
279
+ const effectScale = vannaFlow / (spot * 1000000);
280
+ modifier = Math.max(0.5, 1 - effectScale * 0.5);
281
+ }
282
+ }
283
+ modifiers.push(modifier);
284
+ }
285
+ return modifiers;
286
+ }
287
+ /**
288
+ * Calculate charm-induced mean shift.
289
+ *
290
+ * Net charm represents guaranteed delta decay. Negative net charm means
291
+ * dealers will be net sellers over time (downward pressure).
292
+ */
293
+ function calculateCharmShift(exposures, spot, config) {
294
+ const timeMultiplier = {
295
+ 'intraday': 0.25,
296
+ 'daily': 1.0,
297
+ 'weekly': 5.0,
298
+ }[config.timeHorizon];
299
+ // Estimate price impact of charm flow
300
+ // Heuristic: $1B of flow ≈ 0.1% price impact for large indices
301
+ const flowImpactPerBillion = 0.001 * spot;
302
+ const priceShift = (exposures.totalCharmExposure / 1000000000) * flowImpactPerBillion * timeMultiplier;
303
+ return priceShift * config.shiftScale;
304
+ }
305
+ // ============================================================================
306
+ // PDF Operations
307
+ // ============================================================================
308
+ /**
309
+ * Apply gamma, vanna, and charm modifiers to probability distribution
310
+ */
311
+ function applyModifiers(strikeProbabilities, gammaModifiers, vannaModifiers, charmShift) {
312
+ return strikeProbabilities.map((sp, i) => ({
313
+ strike: sp.strike + charmShift,
314
+ probability: sp.probability * gammaModifiers[i] * vannaModifiers[i],
315
+ }));
316
+ }
317
+ /**
318
+ * Normalize probabilities to sum to 1
319
+ */
320
+ function normalizeProbabilities(strikeProbabilities) {
321
+ const sum = strikeProbabilities.reduce((acc, sp) => acc + sp.probability, 0);
322
+ if (sum < 1e-9) {
323
+ // If all probabilities are essentially zero, return uniform
324
+ const uniform = 1 / strikeProbabilities.length;
325
+ return strikeProbabilities.map(sp => ({ ...sp, probability: uniform }));
326
+ }
327
+ return strikeProbabilities.map(sp => ({
328
+ strike: sp.strike,
329
+ probability: sp.probability / sum,
330
+ }));
331
+ }
332
+ /**
333
+ * Recalculate distribution statistics from adjusted probabilities
334
+ */
335
+ function recalculateDistributionStats(baseline, adjustedProbabilities, underlyingPrice) {
336
+ // Most likely price (mode)
337
+ let mostLikelyPrice = adjustedProbabilities[0].strike;
338
+ let maxProb = 0;
339
+ for (const sp of adjustedProbabilities) {
340
+ if (sp.probability > maxProb) {
341
+ maxProb = sp.probability;
342
+ mostLikelyPrice = sp.strike;
343
+ }
344
+ }
345
+ // Median (50th percentile)
346
+ let cumulative = 0;
347
+ let medianPrice = adjustedProbabilities[Math.floor(adjustedProbabilities.length / 2)].strike;
348
+ for (const sp of adjustedProbabilities) {
349
+ cumulative += sp.probability;
350
+ if (cumulative >= 0.5) {
351
+ medianPrice = sp.strike;
352
+ break;
353
+ }
354
+ }
355
+ // Expected value (mean)
356
+ let mean = 0;
357
+ for (const sp of adjustedProbabilities) {
358
+ mean += sp.strike * sp.probability;
359
+ }
360
+ // Variance and expected move (std dev)
361
+ let variance = 0;
362
+ for (const sp of adjustedProbabilities) {
363
+ const diff = sp.strike - mean;
364
+ variance += diff * diff * sp.probability;
365
+ }
366
+ const expectedMove = Math.sqrt(variance);
367
+ // Tail skew
368
+ let leftTail = 0;
369
+ let rightTail = 0;
370
+ for (const sp of adjustedProbabilities) {
371
+ if (sp.strike < mean) {
372
+ leftTail += sp.probability;
373
+ }
374
+ else {
375
+ rightTail += sp.probability;
376
+ }
377
+ }
378
+ const tailSkew = rightTail / Math.max(leftTail, 1e-9);
379
+ // Cumulative above/below spot
380
+ let cumulativeBelowSpot = 0;
381
+ let cumulativeAboveSpot = 0;
382
+ for (const sp of adjustedProbabilities) {
383
+ if (sp.strike < underlyingPrice) {
384
+ cumulativeBelowSpot += sp.probability;
385
+ }
386
+ else if (sp.strike > underlyingPrice) {
387
+ cumulativeAboveSpot += sp.probability;
388
+ }
389
+ }
390
+ return {
391
+ symbol: baseline.symbol,
392
+ expiryDate: baseline.expiryDate,
393
+ calculationTimestamp: Date.now(),
394
+ underlyingPrice,
395
+ strikeProbabilities: adjustedProbabilities,
396
+ mostLikelyPrice,
397
+ medianPrice,
398
+ expectedValue: mean,
399
+ expectedMove,
400
+ tailSkew,
401
+ cumulativeProbabilityAboveSpot: cumulativeAboveSpot,
402
+ cumulativeProbabilityBelowSpot: cumulativeBelowSpot,
403
+ };
404
+ }
405
+ /**
406
+ * Calculate comparison metrics between baseline and adjusted distributions
407
+ */
408
+ function calculateComparison(baseline, adjusted, gammaModifiers, vannaModifiers, charmShift) {
409
+ // Tail percentiles
410
+ const baseline5 = (0, index_1.getQuantile)(baseline, 0.05);
411
+ const baseline95 = (0, index_1.getQuantile)(baseline, 0.95);
412
+ const adjusted5 = (0, index_1.getQuantile)(adjusted, 0.05);
413
+ const adjusted95 = (0, index_1.getQuantile)(adjusted, 0.95);
414
+ // Determine dominant factor
415
+ const gammaEffect = Math.max(...gammaModifiers) - Math.min(...gammaModifiers);
416
+ const vannaEffect = Math.max(...vannaModifiers) - Math.min(...vannaModifiers);
417
+ const charmEffect = Math.abs(charmShift) / baseline.underlyingPrice;
418
+ let dominantFactor = 'none';
419
+ const maxEffect = Math.max(gammaEffect, vannaEffect, charmEffect);
420
+ if (maxEffect > 0.01) {
421
+ if (gammaEffect === maxEffect)
422
+ dominantFactor = 'gamma';
423
+ else if (vannaEffect === maxEffect)
424
+ dominantFactor = 'vanna';
425
+ else
426
+ dominantFactor = 'charm';
427
+ }
428
+ return {
429
+ meanShift: adjusted.expectedValue - baseline.expectedValue,
430
+ meanShiftPercent: ((adjusted.expectedValue - baseline.expectedValue) / baseline.underlyingPrice) * 100,
431
+ stdDevChange: adjusted.expectedMove - baseline.expectedMove,
432
+ tailSkewChange: adjusted.tailSkew - baseline.tailSkew,
433
+ leftTail: {
434
+ baseline: baseline5,
435
+ adjusted: adjusted5,
436
+ ratio: adjusted5 / baseline5,
437
+ },
438
+ rightTail: {
439
+ baseline: baseline95,
440
+ adjusted: adjusted95,
441
+ ratio: adjusted95 / baseline95,
442
+ },
443
+ dominantFactor,
444
+ };
445
+ }
446
+ // ============================================================================
447
+ // Utility Functions
448
+ // ============================================================================
449
+ /**
450
+ * Merge partial config with defaults
451
+ */
452
+ function mergeConfig(partial) {
453
+ return {
454
+ gamma: { ...exports.DEFAULT_ADJUSTMENT_CONFIG.gamma, ...partial.gamma },
455
+ vanna: { ...exports.DEFAULT_ADJUSTMENT_CONFIG.vanna, ...partial.vanna },
456
+ charm: { ...exports.DEFAULT_ADJUSTMENT_CONFIG.charm, ...partial.charm },
457
+ };
458
+ }
459
+ /**
460
+ * Get the "edge" - the difference between market-implied and flow-adjusted
461
+ * probability of reaching a price level.
462
+ *
463
+ * Positive edge means the market is underpricing the probability of reaching that level.
464
+ * Negative edge means the market is overpricing it.
465
+ *
466
+ * @example
467
+ * ```typescript
468
+ * const result = estimateExposureAdjustedPDF(...);
469
+ * const edge = getEdgeAtPrice(result, 4400);
470
+ * console.log(`Edge at 4400: ${(edge * 100).toFixed(2)}%`);
471
+ * // Output: "Edge at 4400: 2.35%"
472
+ * // Meaning: flow mechanics suggest 2.35% higher probability than market prices
473
+ * ```
474
+ */
475
+ function getEdgeAtPrice(result, price) {
476
+ const baselineProb = (0, index_1.getCumulativeProbability)(result.baseline, price);
477
+ const adjustedProb = (0, index_1.getCumulativeProbability)(result.adjusted, price);
478
+ return adjustedProb - baselineProb;
479
+ }
480
+ /**
481
+ * Get price levels where the adjustment is most significant
482
+ */
483
+ function getSignificantAdjustmentLevels(result, threshold = 0.01) {
484
+ const levels = [];
485
+ for (let i = 0; i < result.baseline.strikeProbabilities.length; i++) {
486
+ const baselineProb = result.baseline.strikeProbabilities[i].probability;
487
+ const adjustedProb = result.adjusted.strikeProbabilities[i]?.probability ?? 0;
488
+ const edge = adjustedProb - baselineProb;
489
+ if (Math.abs(edge) >= threshold) {
490
+ levels.push({
491
+ strike: result.baseline.strikeProbabilities[i].strike,
492
+ baselineProb,
493
+ adjustedProb,
494
+ edge,
495
+ });
496
+ }
497
+ }
498
+ // Sort by absolute edge descending
499
+ return levels.sort((a, b) => Math.abs(b.edge) - Math.abs(a.edge));
500
+ }
@@ -146,3 +146,4 @@ export declare function getCumulativeProbability(distribution: ImpliedProbabilit
146
146
  * ```
147
147
  */
148
148
  export declare function getQuantile(distribution: ImpliedProbabilityDistribution, probability: number): number;
149
+ export { estimateExposureAdjustedPDF, getEdgeAtPrice, getSignificantAdjustmentLevels, DEFAULT_ADJUSTMENT_CONFIG, LOW_VOL_CONFIG, CRISIS_CONFIG, OPEX_CONFIG, type ExposureAdjustmentConfig, type AdjustedPDFResult, type PDFComparison, } from './adjusted';
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OPEX_CONFIG = exports.CRISIS_CONFIG = exports.LOW_VOL_CONFIG = exports.DEFAULT_ADJUSTMENT_CONFIG = exports.getSignificantAdjustmentLevels = exports.getEdgeAtPrice = exports.estimateExposureAdjustedPDF = void 0;
3
4
  exports.estimateImpliedProbabilityDistribution = estimateImpliedProbabilityDistribution;
4
5
  exports.estimateImpliedProbabilityDistributions = estimateImpliedProbabilityDistributions;
5
6
  exports.getProbabilityInRange = getProbabilityInRange;
@@ -276,3 +277,12 @@ function getQuantile(distribution, probability) {
276
277
  }
277
278
  return distribution.strikeProbabilities[distribution.strikeProbabilities.length - 1]?.strike ?? 0;
278
279
  }
280
+ // Re-export exposure-adjusted PDF functionality
281
+ var adjusted_1 = require("./adjusted");
282
+ Object.defineProperty(exports, "estimateExposureAdjustedPDF", { enumerable: true, get: function () { return adjusted_1.estimateExposureAdjustedPDF; } });
283
+ Object.defineProperty(exports, "getEdgeAtPrice", { enumerable: true, get: function () { return adjusted_1.getEdgeAtPrice; } });
284
+ Object.defineProperty(exports, "getSignificantAdjustmentLevels", { enumerable: true, get: function () { return adjusted_1.getSignificantAdjustmentLevels; } });
285
+ Object.defineProperty(exports, "DEFAULT_ADJUSTMENT_CONFIG", { enumerable: true, get: function () { return adjusted_1.DEFAULT_ADJUSTMENT_CONFIG; } });
286
+ Object.defineProperty(exports, "LOW_VOL_CONFIG", { enumerable: true, get: function () { return adjusted_1.LOW_VOL_CONFIG; } });
287
+ Object.defineProperty(exports, "CRISIS_CONFIG", { enumerable: true, get: function () { return adjusted_1.CRISIS_CONFIG; } });
288
+ Object.defineProperty(exports, "OPEX_CONFIG", { enumerable: true, get: function () { return adjusted_1.OPEX_CONFIG; } });
package/dist/index.d.ts CHANGED
@@ -12,11 +12,17 @@ export { calculateGammaVannaCharmExposures, calculateSharesNeededToCover, } from
12
12
  export { cumulativeNormalDistribution, normalPDF, } from './utils/statistics';
13
13
  export { buildOCCSymbol, parseOCCSymbol, generateStrikesAroundSpot, generateOCCSymbolsForStrikes, generateOCCSymbolsAroundSpot, } from './utils/occ';
14
14
  export type { OCCSymbolParams, ParsedOCCSymbol, StrikeGenerationParams, } from './utils/occ';
15
- export { estimateImpliedProbabilityDistribution, estimateImpliedProbabilityDistributions, getProbabilityInRange, getCumulativeProbability, getQuantile, } from './impliedpdf';
16
- export type { StrikeProbability, ImpliedProbabilityDistribution, ImpliedPDFResult, } from './impliedpdf';
15
+ export { estimateImpliedProbabilityDistribution, estimateImpliedProbabilityDistributions, getProbabilityInRange, getCumulativeProbability, getQuantile, estimateExposureAdjustedPDF, getEdgeAtPrice, getSignificantAdjustmentLevels, DEFAULT_ADJUSTMENT_CONFIG, LOW_VOL_CONFIG, CRISIS_CONFIG, OPEX_CONFIG, } from './impliedpdf';
16
+ export type { StrikeProbability, ImpliedProbabilityDistribution, ImpliedPDFResult, ExposureAdjustmentConfig, AdjustedPDFResult, PDFComparison, } from './impliedpdf';
17
+ export { deriveRegimeParams, interpolateIVAtStrike, computeHedgeImpulseCurve, computeCharmIntegral, analyzeHedgeFlow, } from './hedgeflow';
18
+ export type { MarketRegime, RegimeParams, HedgeImpulseConfig, HedgeImpulsePoint, HedgeImpulseCurve, ZeroCrossing, ImpulseExtremum, DirectionalAsymmetry, ImpulseRegime, CharmIntegralConfig, CharmBucket, CharmIntegral, HedgeFlowAnalysis, } from './hedgeflow';
17
19
  export { FloeClient, Broker } from './client/FloeClient';
18
20
  export { TradierClient } from './client/brokers/TradierClient';
19
21
  export { TastyTradeClient } from './client/brokers/TastyTradeClient';
20
22
  export { TradeStationClient } from './client/brokers/TradeStationClient';
21
23
  export type { AggressorSide, IntradayTrade } from './client/brokers/TradierClient';
22
24
  export { genericAdapter, schwabAdapter, ibkrAdapter, tdaAdapter, brokerAdapters, getAdapter, createOptionChain, } from './adapters';
25
+ export { computeVarianceSwapIV, computeImpliedVolatility, } from './iv';
26
+ export type { VarianceSwapResult, ImpliedVolatilityResult, } from './iv';
27
+ export { computeRealizedVolatility, } from './rv';
28
+ export type { PriceObservation, RealizedVolatilityResult, } from './rv';
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
20
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = exports.TradeStationClient = exports.TastyTradeClient = exports.TradierClient = exports.Broker = exports.FloeClient = exports.getQuantile = exports.getCumulativeProbability = exports.getProbabilityInRange = exports.estimateImpliedProbabilityDistributions = exports.estimateImpliedProbabilityDistribution = exports.generateOCCSymbolsAroundSpot = exports.generateOCCSymbolsForStrikes = exports.generateStrikesAroundSpot = exports.parseOCCSymbol = exports.buildOCCSymbol = exports.normalPDF = exports.cumulativeNormalDistribution = exports.calculateSharesNeededToCover = exports.calculateGammaVannaCharmExposures = exports.smoothTotalVarianceSmile = exports.getIVForStrike = exports.getIVSurfaces = exports.getTimeToExpirationInYears = exports.getMillisecondsToExpiration = exports.calculateImpliedVolatility = exports.calculateGreeks = exports.blackScholes = void 0;
23
+ exports.computeRealizedVolatility = exports.computeImpliedVolatility = exports.computeVarianceSwapIV = exports.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = exports.TradeStationClient = exports.TastyTradeClient = exports.TradierClient = exports.Broker = exports.FloeClient = exports.analyzeHedgeFlow = exports.computeCharmIntegral = exports.computeHedgeImpulseCurve = exports.interpolateIVAtStrike = exports.deriveRegimeParams = exports.OPEX_CONFIG = exports.CRISIS_CONFIG = exports.LOW_VOL_CONFIG = exports.DEFAULT_ADJUSTMENT_CONFIG = exports.getSignificantAdjustmentLevels = exports.getEdgeAtPrice = exports.estimateExposureAdjustedPDF = exports.getQuantile = exports.getCumulativeProbability = exports.getProbabilityInRange = exports.estimateImpliedProbabilityDistributions = exports.estimateImpliedProbabilityDistribution = exports.generateOCCSymbolsAroundSpot = exports.generateOCCSymbolsForStrikes = exports.generateStrikesAroundSpot = exports.parseOCCSymbol = exports.buildOCCSymbol = exports.normalPDF = exports.cumulativeNormalDistribution = exports.calculateSharesNeededToCover = exports.calculateGammaVannaCharmExposures = exports.smoothTotalVarianceSmile = exports.getIVForStrike = exports.getIVSurfaces = exports.getTimeToExpirationInYears = exports.getMillisecondsToExpiration = exports.calculateImpliedVolatility = exports.calculateGreeks = exports.blackScholes = void 0;
24
24
  // Core types
25
25
  __exportStar(require("./types"), exports);
26
26
  // Black-Scholes pricing and Greeks
@@ -59,6 +59,25 @@ Object.defineProperty(exports, "estimateImpliedProbabilityDistributions", { enum
59
59
  Object.defineProperty(exports, "getProbabilityInRange", { enumerable: true, get: function () { return impliedpdf_1.getProbabilityInRange; } });
60
60
  Object.defineProperty(exports, "getCumulativeProbability", { enumerable: true, get: function () { return impliedpdf_1.getCumulativeProbability; } });
61
61
  Object.defineProperty(exports, "getQuantile", { enumerable: true, get: function () { return impliedpdf_1.getQuantile; } });
62
+ // Exposure-adjusted PDF
63
+ Object.defineProperty(exports, "estimateExposureAdjustedPDF", { enumerable: true, get: function () { return impliedpdf_1.estimateExposureAdjustedPDF; } });
64
+ Object.defineProperty(exports, "getEdgeAtPrice", { enumerable: true, get: function () { return impliedpdf_1.getEdgeAtPrice; } });
65
+ Object.defineProperty(exports, "getSignificantAdjustmentLevels", { enumerable: true, get: function () { return impliedpdf_1.getSignificantAdjustmentLevels; } });
66
+ Object.defineProperty(exports, "DEFAULT_ADJUSTMENT_CONFIG", { enumerable: true, get: function () { return impliedpdf_1.DEFAULT_ADJUSTMENT_CONFIG; } });
67
+ Object.defineProperty(exports, "LOW_VOL_CONFIG", { enumerable: true, get: function () { return impliedpdf_1.LOW_VOL_CONFIG; } });
68
+ Object.defineProperty(exports, "CRISIS_CONFIG", { enumerable: true, get: function () { return impliedpdf_1.CRISIS_CONFIG; } });
69
+ Object.defineProperty(exports, "OPEX_CONFIG", { enumerable: true, get: function () { return impliedpdf_1.OPEX_CONFIG; } });
70
+ // Hedge flow analysis (impulse curve, charm integral, regime derivation)
71
+ var hedgeflow_1 = require("./hedgeflow");
72
+ // Regime derivation from IV surface
73
+ Object.defineProperty(exports, "deriveRegimeParams", { enumerable: true, get: function () { return hedgeflow_1.deriveRegimeParams; } });
74
+ Object.defineProperty(exports, "interpolateIVAtStrike", { enumerable: true, get: function () { return hedgeflow_1.interpolateIVAtStrike; } });
75
+ // Hedge impulse curve
76
+ Object.defineProperty(exports, "computeHedgeImpulseCurve", { enumerable: true, get: function () { return hedgeflow_1.computeHedgeImpulseCurve; } });
77
+ // Charm integral
78
+ Object.defineProperty(exports, "computeCharmIntegral", { enumerable: true, get: function () { return hedgeflow_1.computeCharmIntegral; } });
79
+ // Combined analysis
80
+ Object.defineProperty(exports, "analyzeHedgeFlow", { enumerable: true, get: function () { return hedgeflow_1.analyzeHedgeFlow; } });
62
81
  // Client
63
82
  var FloeClient_1 = require("./client/FloeClient");
64
83
  Object.defineProperty(exports, "FloeClient", { enumerable: true, get: function () { return FloeClient_1.FloeClient; } });
@@ -78,3 +97,10 @@ Object.defineProperty(exports, "tdaAdapter", { enumerable: true, get: function (
78
97
  Object.defineProperty(exports, "brokerAdapters", { enumerable: true, get: function () { return adapters_1.brokerAdapters; } });
79
98
  Object.defineProperty(exports, "getAdapter", { enumerable: true, get: function () { return adapters_1.getAdapter; } });
80
99
  Object.defineProperty(exports, "createOptionChain", { enumerable: true, get: function () { return adapters_1.createOptionChain; } });
100
+ // Model-free implied volatility (variance swap / VIX methodology)
101
+ var iv_1 = require("./iv");
102
+ Object.defineProperty(exports, "computeVarianceSwapIV", { enumerable: true, get: function () { return iv_1.computeVarianceSwapIV; } });
103
+ Object.defineProperty(exports, "computeImpliedVolatility", { enumerable: true, get: function () { return iv_1.computeImpliedVolatility; } });
104
+ // Realized volatility (tick-based quadratic variation)
105
+ var rv_1 = require("./rv");
106
+ Object.defineProperty(exports, "computeRealizedVolatility", { enumerable: true, get: function () { return rv_1.computeRealizedVolatility; } });
@@ -0,0 +1,52 @@
1
+ import { NormalizedOption } from '../types';
2
+ import { VarianceSwapResult, ImpliedVolatilityResult } from './types';
3
+ export type { VarianceSwapResult, ImpliedVolatilityResult } from './types';
4
+ /**
5
+ * Compute the model-free implied variance for a single expiration
6
+ * using the CBOE variance swap methodology.
7
+ *
8
+ * This implements the formula:
9
+ *
10
+ * σ² = (2/T) × Σᵢ (ΔKᵢ/Kᵢ²) × e^(rT) × Q(Kᵢ) - (1/T) × (F/K₀ - 1)²
11
+ *
12
+ * Where:
13
+ * - K₀ is the strike where |call_mid - put_mid| is minimized
14
+ * - F = K₀ + e^(rT) × (call(K₀) - put(K₀)) is the forward price
15
+ * - Q(Kᵢ) = put mid for Kᵢ < K₀, call mid for Kᵢ > K₀,
16
+ * average of call and put mids at K₀
17
+ * - ΔKᵢ = (Kᵢ₊₁ - Kᵢ₋₁) / 2 (actual strike spacing, not hardcoded)
18
+ *
19
+ * Two consecutive zero-bid options terminate the summation in each
20
+ * direction (puts walking down, calls walking up), per CBOE rules.
21
+ *
22
+ * @param options - All options for one expiration (calls and puts)
23
+ * @param spot - Current underlying price
24
+ * @param riskFreeRate - Annual risk-free rate as decimal (e.g. 0.05)
25
+ * @returns Variance swap result with annualized IV
26
+ */
27
+ export declare function computeVarianceSwapIV(options: NormalizedOption[], spot: number, riskFreeRate: number): VarianceSwapResult;
28
+ /**
29
+ * Compute implied volatility from option prices using the CBOE
30
+ * variance swap methodology.
31
+ *
32
+ * If only nearTermOptions are provided, computes the single-expiration
33
+ * model-free implied volatility directly.
34
+ *
35
+ * If farTermOptions are also provided, performs CBOE VIX-style
36
+ * interpolation between the two terms to produce a constant-maturity
37
+ * measure at `targetDays` (defaults to the far term's DTE).
38
+ *
39
+ * The interpolation formula (in variance space):
40
+ *
41
+ * VIX = 100 × √{ [T₁σ₁² × (N₂ - N_target)/(N₂ - N₁)
42
+ * + T₂σ₂² × (N_target - N₁)/(N₂ - N₁)]
43
+ * × N_365 / N_target }
44
+ *
45
+ * @param nearTermOptions - Options for the near-term expiration
46
+ * @param spot - Current underlying price
47
+ * @param riskFreeRate - Annual risk-free rate as decimal
48
+ * @param farTermOptions - Options for the far-term expiration (optional)
49
+ * @param targetDays - Target constant maturity in days for interpolation
50
+ * (defaults to far term DTE if far term provided)
51
+ */
52
+ export declare function computeImpliedVolatility(nearTermOptions: NormalizedOption[], spot: number, riskFreeRate: number, farTermOptions?: NormalizedOption[] | null, targetDays?: number): ImpliedVolatilityResult;