@fullstackcraftllc/floe 0.0.15 → 0.0.17

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.
@@ -1,17 +1,22 @@
1
- import { OptionChain, ExposurePerExpiry, IVSurface } from '../types';
1
+ import { OptionChain, ExposureVariantsPerExpiry, ExposureCalculationOptions, IVSurface } from '../types';
2
2
  /**
3
- * Calculate Gamma, Vanna, and Charm exposures for an option chain
3
+ * Calculate canonical, state-weighted, and flow-delta exposure variants.
4
4
  *
5
- * @param chain - Option chain with market context (spot, rates, options)
6
- * @param ivSurfaces - IV surfaces for all expirations
7
- * @returns Array of exposure metrics per expiration
5
+ * canonical:
6
+ * - GEX: dollars per 1% underlying move
7
+ * - VEX: dollars per 1 vol-point move
8
+ * - CEX: dollars per 1 day of time decay
8
9
  *
9
- * @example
10
- * ```typescript
11
- * const exposures = calculateGammaVannaCharmExposures(chain, ivSurfaces);
12
- * ```
10
+ * stateWeighted:
11
+ * - Gamma: same as canonical (spot is already the state variable)
12
+ * - Vanna: canonical vanna weighted by strike IV level
13
+ * - Charm: canonical charm weighted by days-to-expiration
14
+ *
15
+ * flowDelta:
16
+ * - Canonical exposure formulas using OI deltas:
17
+ * (liveOpenInterest - openInterest)
13
18
  */
14
- export declare function calculateGammaVannaCharmExposures(chain: OptionChain, ivSurfaces: IVSurface[]): ExposurePerExpiry[];
19
+ export declare function calculateGammaVannaCharmExposures(chain: OptionChain, ivSurfaces: IVSurface[], options?: ExposureCalculationOptions): ExposureVariantsPerExpiry[];
15
20
  /**
16
21
  * Calculate shares needed to cover net exposure
17
22
  *
@@ -6,70 +6,62 @@ const types_1 = require("../types");
6
6
  const blackscholes_1 = require("../blackscholes");
7
7
  const volatility_1 = require("../volatility");
8
8
  /**
9
- * Calculate Gamma, Vanna, and Charm exposures for an option chain
9
+ * Calculate canonical, state-weighted, and flow-delta exposure variants.
10
10
  *
11
- * @param chain - Option chain with market context (spot, rates, options)
12
- * @param ivSurfaces - IV surfaces for all expirations
13
- * @returns Array of exposure metrics per expiration
11
+ * canonical:
12
+ * - GEX: dollars per 1% underlying move
13
+ * - VEX: dollars per 1 vol-point move
14
+ * - CEX: dollars per 1 day of time decay
14
15
  *
15
- * @example
16
- * ```typescript
17
- * const exposures = calculateGammaVannaCharmExposures(chain, ivSurfaces);
18
- * ```
16
+ * stateWeighted:
17
+ * - Gamma: same as canonical (spot is already the state variable)
18
+ * - Vanna: canonical vanna weighted by strike IV level
19
+ * - Charm: canonical charm weighted by days-to-expiration
20
+ *
21
+ * flowDelta:
22
+ * - Canonical exposure formulas using OI deltas:
23
+ * (liveOpenInterest - openInterest)
19
24
  */
20
- function calculateGammaVannaCharmExposures(chain, ivSurfaces) {
21
- const { spot, riskFreeRate, dividendYield, options } = chain;
25
+ function calculateGammaVannaCharmExposures(chain, ivSurfaces, options = {}) {
26
+ const { spot, riskFreeRate, dividendYield, options: chainOptions } = chain;
27
+ const asOfTimestamp = options.asOfTimestamp ?? Date.now();
22
28
  const exposureRows = [];
23
- // Get unique expirations from options
24
29
  const expirationsSet = new Set();
25
- for (const option of options) {
30
+ for (const option of chainOptions) {
26
31
  expirationsSet.add(option.expirationTimestamp);
27
32
  }
28
33
  const expirations = Array.from(expirationsSet).sort((a, b) => a - b);
29
- // Loop through all expirations
34
+ const putOptionsByKey = new Map();
35
+ for (const option of chainOptions) {
36
+ if (option.optionType === 'put') {
37
+ putOptionsByKey.set(getOptionKey(option.expirationTimestamp, option.strike), option);
38
+ }
39
+ }
30
40
  for (const expiration of expirations) {
31
- // Skip any expiration that is in the past
32
- if (expiration < Date.now()) {
41
+ if (expiration < asOfTimestamp) {
42
+ continue;
43
+ }
44
+ const timeToExpirationInYears = (expiration - asOfTimestamp) / types_1.MILLISECONDS_PER_YEAR;
45
+ if (timeToExpirationInYears <= 0) {
33
46
  continue;
34
47
  }
35
- // Reset totals for this expiration
36
- let totalGammaExposure = 0.0;
37
- let totalVannaExposure = 0.0;
38
- let totalCharmExposure = 0.0;
39
- let strikeOfMaxGamma = 0.0;
40
- let strikeOfMinGamma = 0.0;
41
- let strikeOfMaxVanna = 0.0;
42
- let strikeOfMinVanna = 0.0;
43
- let strikeOfMaxCharm = 0.0;
44
- let strikeOfMinCharm = 0.0;
45
- let strikeOfMaxNet = 0.0;
46
- let strikeOfMinNet = 0.0;
47
- const strikeExposures = [];
48
- // Process all call options first
49
- for (const callOption of options) {
50
- // Check if this option is at the expiration we are looking at
51
- if (callOption.expirationTimestamp !== expiration || callOption.optionType === 'put') {
48
+ const timeToExpirationInDays = Math.max(timeToExpirationInYears * types_1.DAYS_PER_YEAR, 0);
49
+ const strikeExposureVariants = [];
50
+ for (const callOption of chainOptions) {
51
+ if (callOption.expirationTimestamp !== expiration || callOption.optionType !== 'call') {
52
52
  continue;
53
53
  }
54
- // Get the corresponding put option
55
- const putOption = options.find((opt) => opt.expirationTimestamp === expiration &&
56
- opt.optionType === 'put' &&
57
- opt.strike === callOption.strike);
54
+ const putOption = putOptionsByKey.get(getOptionKey(expiration, callOption.strike));
58
55
  if (!putOption) {
59
- continue; // Skip if no matching put
56
+ continue;
60
57
  }
61
- // Get IV for this strike and expiry from the surface
62
- const callIVAtStrike = (0, volatility_1.getIVForStrike)(ivSurfaces, expiration, 'call', callOption.strike);
63
- const putIVAtStrike = (0, volatility_1.getIVForStrike)(ivSurfaces, expiration, 'put', putOption.strike);
64
- // Get time to expiration in years
65
- const timeToExpirationInYears = (0, blackscholes_1.getTimeToExpirationInYears)(expiration);
66
- // Calculate Greeks for both call and put
67
- // Rates are already decimals in OptionChain, IV from surface is percentage
58
+ const callIVAtStrike = resolveIVPercent((0, volatility_1.getIVForStrike)(ivSurfaces, expiration, 'call', callOption.strike), callOption.impliedVolatility);
59
+ const putIVAtStrike = resolveIVPercent((0, volatility_1.getIVForStrike)(ivSurfaces, expiration, 'put', putOption.strike), putOption.impliedVolatility);
68
60
  const callGreeks = (0, blackscholes_1.calculateGreeks)({
69
61
  spot,
70
62
  strike: callOption.strike,
71
63
  timeToExpiry: timeToExpirationInYears,
72
- volatility: callIVAtStrike / 100.0, // Convert from percentage to decimal
64
+ volatility: callIVAtStrike / 100.0,
73
65
  riskFreeRate,
74
66
  dividendYield,
75
67
  optionType: 'call',
@@ -83,74 +75,33 @@ function calculateGammaVannaCharmExposures(chain, ivSurfaces) {
83
75
  dividendYield,
84
76
  optionType: 'put',
85
77
  });
86
- // Calculate exposures from dealer perspective
87
- // Dealer is short calls (negative gamma) and long puts (positive gamma)
88
- // Multiply by 100 for contract size, multiply by spot for dollar exposure
89
- // Multiply by 0.01 for 1% move sensitivity
90
- // Gamma: second order with respect to price twice
91
- const gammaExposureForStrike = -callOption.openInterest * callGreeks.gamma * (spot * 100.0) * spot * 0.01 +
92
- putOption.openInterest * putGreeks.gamma * (spot * 100.0) * spot * 0.01;
93
- // Vanna: second order with respect to price and volatility
94
- const vannaExposureForStrike = -callOption.openInterest * callGreeks.vanna * (spot * 100.0) * callIVAtStrike * 0.01 +
95
- putOption.openInterest * putGreeks.vanna * (spot * 100.0) * putIVAtStrike * 0.01;
96
- // Charm: second order with respect to price and time
97
- // Already normalized per day in calculateGreeks
98
- const charmExposureForStrike = -callOption.openInterest * callGreeks.charm * (spot * 100.0) * types_1.DAYS_PER_YEAR * timeToExpirationInYears +
99
- putOption.openInterest * putGreeks.charm * (spot * 100.0) * types_1.DAYS_PER_YEAR * timeToExpirationInYears;
100
- // NaN checks
101
- const gammaExposure = isNaN(gammaExposureForStrike) ? 0.0 : gammaExposureForStrike;
102
- const vannaExposure = isNaN(vannaExposureForStrike) ? 0.0 : vannaExposureForStrike;
103
- const charmExposure = isNaN(charmExposureForStrike) ? 0.0 : charmExposureForStrike;
104
- // Add to totals
105
- totalGammaExposure += gammaExposure;
106
- totalVannaExposure += vannaExposure;
107
- totalCharmExposure += charmExposure;
108
- // Add to strike exposures
109
- strikeExposures.push({
78
+ const callOpenInterest = sanitizeFinite(callOption.openInterest);
79
+ const putOpenInterest = sanitizeFinite(putOption.openInterest);
80
+ const canonical = calculateCanonicalVector(spot, callOpenInterest, putOpenInterest, callGreeks.gamma, putGreeks.gamma, callGreeks.vanna, putGreeks.vanna, callGreeks.charm, putGreeks.charm);
81
+ const stateWeighted = calculateStateWeightedVector(spot, callOpenInterest, putOpenInterest, callGreeks.vanna, putGreeks.vanna, callGreeks.charm, putGreeks.charm, callIVAtStrike, putIVAtStrike, timeToExpirationInDays, canonical.gammaExposure);
82
+ const callFlowDelta = resolveFlowDeltaOpenInterest(callOption.openInterest, callOption.liveOpenInterest);
83
+ const putFlowDelta = resolveFlowDeltaOpenInterest(putOption.openInterest, putOption.liveOpenInterest);
84
+ const flowDelta = calculateCanonicalVector(spot, callFlowDelta, putFlowDelta, callGreeks.gamma, putGreeks.gamma, callGreeks.vanna, putGreeks.vanna, callGreeks.charm, putGreeks.charm);
85
+ strikeExposureVariants.push({
110
86
  strikePrice: callOption.strike,
111
- gammaExposure,
112
- vannaExposure,
113
- charmExposure,
114
- netExposure: gammaExposure + vannaExposure + charmExposure,
87
+ canonical,
88
+ stateWeighted,
89
+ flowDelta,
115
90
  });
116
91
  }
117
- if (strikeExposures.length === 0) {
118
- continue; // No options for this expiration
92
+ if (strikeExposureVariants.length === 0) {
93
+ continue;
119
94
  }
120
- // Sort by gamma exposure and find extremes
121
- strikeExposures.sort((a, b) => b.gammaExposure - a.gammaExposure);
122
- strikeOfMaxGamma = strikeExposures[0].strikePrice;
123
- strikeOfMinGamma = strikeExposures[strikeExposures.length - 1].strikePrice;
124
- // Sort by vanna exposure and find extremes
125
- strikeExposures.sort((a, b) => b.vannaExposure - a.vannaExposure);
126
- strikeOfMaxVanna = strikeExposures[0].strikePrice;
127
- strikeOfMinVanna = strikeExposures[strikeExposures.length - 1].strikePrice;
128
- // Sort by charm exposure and find extremes
129
- strikeExposures.sort((a, b) => b.charmExposure - a.charmExposure);
130
- strikeOfMaxCharm = strikeExposures[0].strikePrice;
131
- strikeOfMinCharm = strikeExposures[strikeExposures.length - 1].strikePrice;
132
- // Sort by net exposure and find extremes
133
- strikeExposures.sort((a, b) => b.netExposure - a.netExposure);
134
- strikeOfMaxNet = strikeExposures[0].strikePrice;
135
- strikeOfMinNet = strikeExposures[strikeExposures.length - 1].strikePrice;
136
- const totalNetExposure = totalGammaExposure + totalVannaExposure + totalCharmExposure;
137
- // Add exposure row
95
+ const canonical = buildModeBreakdown(strikeExposureVariants, 'canonical');
96
+ const stateWeighted = buildModeBreakdown(strikeExposureVariants, 'stateWeighted');
97
+ const flowDelta = buildModeBreakdown(strikeExposureVariants, 'flowDelta');
138
98
  exposureRows.push({
139
99
  spotPrice: spot,
140
100
  expiration,
141
- totalGammaExposure,
142
- totalVannaExposure,
143
- totalCharmExposure,
144
- totalNetExposure,
145
- strikeOfMaxGamma,
146
- strikeOfMinGamma,
147
- strikeOfMaxVanna,
148
- strikeOfMinVanna,
149
- strikeOfMaxCharm,
150
- strikeOfMinCharm,
151
- strikeOfMaxNet,
152
- strikeOfMinNet,
153
- strikeExposures
101
+ canonical,
102
+ stateWeighted,
103
+ flowDelta,
104
+ strikeExposureVariants,
154
105
  });
155
106
  }
156
107
  return exposureRows;
@@ -210,3 +161,110 @@ function calculateSharesNeededToCover(sharesOutstanding, totalNetExposure, under
210
161
  resultingSpotToCover: resultingPrice,
211
162
  };
212
163
  }
164
+ function getOptionKey(expiration, strike) {
165
+ return `${expiration}:${strike}`;
166
+ }
167
+ function calculateCanonicalVector(spot, callPosition, putPosition, callGamma, putGamma, callVanna, putVanna, callCharm, putCharm) {
168
+ const gammaExposure = -callPosition * callGamma * (spot * 100.0) * spot * 0.01 +
169
+ putPosition * putGamma * (spot * 100.0) * spot * 0.01;
170
+ const vannaExposure = -callPosition * callVanna * (spot * 100.0) * 0.01 +
171
+ putPosition * putVanna * (spot * 100.0) * 0.01;
172
+ const charmExposure = -callPosition * callCharm * (spot * 100.0) +
173
+ putPosition * putCharm * (spot * 100.0);
174
+ return sanitizeVector({
175
+ gammaExposure,
176
+ vannaExposure,
177
+ charmExposure,
178
+ netExposure: gammaExposure + vannaExposure + charmExposure,
179
+ });
180
+ }
181
+ function calculateStateWeightedVector(spot, callPosition, putPosition, callVanna, putVanna, callCharm, putCharm, callIVPercent, putIVPercent, timeToExpirationInDays, canonicalGammaExposure) {
182
+ const callIVLevel = Math.max(callIVPercent * 0.01, 0);
183
+ const putIVLevel = Math.max(putIVPercent * 0.01, 0);
184
+ // Gamma already uses instantaneous price scaling in canonical GEX.
185
+ const gammaExposure = canonicalGammaExposure;
186
+ const vannaExposure = -callPosition * callVanna * (spot * 100.0) * 0.01 * callIVLevel +
187
+ putPosition * putVanna * (spot * 100.0) * 0.01 * putIVLevel;
188
+ const canonicalCharmComponent = -callPosition * callCharm * (spot * 100.0) +
189
+ putPosition * putCharm * (spot * 100.0);
190
+ const charmExposure = canonicalCharmComponent * Math.max(timeToExpirationInDays, 0);
191
+ return sanitizeVector({
192
+ gammaExposure,
193
+ vannaExposure,
194
+ charmExposure,
195
+ netExposure: gammaExposure + vannaExposure + charmExposure,
196
+ });
197
+ }
198
+ function resolveFlowDeltaOpenInterest(openInterest, liveOpenInterest) {
199
+ if (typeof liveOpenInterest !== 'number' || !isFinite(liveOpenInterest)) {
200
+ return 0;
201
+ }
202
+ return sanitizeFinite(liveOpenInterest - openInterest);
203
+ }
204
+ function resolveIVPercent(ivFromSurface, optionImpliedVolatilityDecimal) {
205
+ if (isFinite(ivFromSurface) && ivFromSurface > 0) {
206
+ return ivFromSurface;
207
+ }
208
+ const fallback = optionImpliedVolatilityDecimal * 100.0;
209
+ if (isFinite(fallback) && fallback > 0) {
210
+ return fallback;
211
+ }
212
+ return 0;
213
+ }
214
+ function buildModeBreakdown(strikeExposureVariants, mode) {
215
+ const strikeExposures = strikeExposureVariants.map((strike) => ({
216
+ strikePrice: strike.strikePrice,
217
+ ...strike[mode],
218
+ }));
219
+ if (strikeExposures.length === 0) {
220
+ return {
221
+ totalGammaExposure: 0,
222
+ totalVannaExposure: 0,
223
+ totalCharmExposure: 0,
224
+ totalNetExposure: 0,
225
+ strikeOfMaxGamma: 0,
226
+ strikeOfMinGamma: 0,
227
+ strikeOfMaxVanna: 0,
228
+ strikeOfMinVanna: 0,
229
+ strikeOfMaxCharm: 0,
230
+ strikeOfMinCharm: 0,
231
+ strikeOfMaxNet: 0,
232
+ strikeOfMinNet: 0,
233
+ strikeExposures: [],
234
+ };
235
+ }
236
+ const totalGammaExposure = strikeExposures.reduce((sum, s) => sum + s.gammaExposure, 0);
237
+ const totalVannaExposure = strikeExposures.reduce((sum, s) => sum + s.vannaExposure, 0);
238
+ const totalCharmExposure = strikeExposures.reduce((sum, s) => sum + s.charmExposure, 0);
239
+ const totalNetExposure = totalGammaExposure + totalVannaExposure + totalCharmExposure;
240
+ const byGamma = [...strikeExposures].sort((a, b) => b.gammaExposure - a.gammaExposure);
241
+ const byVanna = [...strikeExposures].sort((a, b) => b.vannaExposure - a.vannaExposure);
242
+ const byCharm = [...strikeExposures].sort((a, b) => b.charmExposure - a.charmExposure);
243
+ const byNet = [...strikeExposures].sort((a, b) => b.netExposure - a.netExposure);
244
+ return {
245
+ totalGammaExposure: sanitizeFinite(totalGammaExposure),
246
+ totalVannaExposure: sanitizeFinite(totalVannaExposure),
247
+ totalCharmExposure: sanitizeFinite(totalCharmExposure),
248
+ totalNetExposure: sanitizeFinite(totalNetExposure),
249
+ strikeOfMaxGamma: byGamma[0].strikePrice,
250
+ strikeOfMinGamma: byGamma[byGamma.length - 1].strikePrice,
251
+ strikeOfMaxVanna: byVanna[0].strikePrice,
252
+ strikeOfMinVanna: byVanna[byVanna.length - 1].strikePrice,
253
+ strikeOfMaxCharm: byCharm[0].strikePrice,
254
+ strikeOfMinCharm: byCharm[byCharm.length - 1].strikePrice,
255
+ strikeOfMaxNet: byNet[0].strikePrice,
256
+ strikeOfMinNet: byNet[byNet.length - 1].strikePrice,
257
+ strikeExposures: byNet,
258
+ };
259
+ }
260
+ function sanitizeVector(vector) {
261
+ return {
262
+ gammaExposure: sanitizeFinite(vector.gammaExposure),
263
+ vannaExposure: sanitizeFinite(vector.vannaExposure),
264
+ charmExposure: sanitizeFinite(vector.charmExposure),
265
+ netExposure: sanitizeFinite(vector.netExposure),
266
+ };
267
+ }
268
+ function sanitizeFinite(value) {
269
+ return isFinite(value) && !isNaN(value) ? value : 0;
270
+ }
@@ -1,9 +1,11 @@
1
1
  import { ExposurePerExpiry, IVSurface } from '../types';
2
2
  import { HedgeImpulseConfig, CharmIntegralConfig, HedgeFlowAnalysis } from './types';
3
3
  export type { MarketRegime, RegimeParams, HedgeImpulseConfig, HedgeImpulsePoint, HedgeImpulseCurve, ZeroCrossing, ImpulseExtremum, DirectionalAsymmetry, ImpulseRegime, CharmIntegralConfig, CharmBucket, CharmIntegral, HedgeFlowAnalysis, } from './types';
4
+ export type { HedgeContractEstimates, PressureZone, RegimeEdge, PressureLevel, PressureCloudConfig, PressureCloud, } from './pressurecloud';
4
5
  export { deriveRegimeParams, interpolateIVAtStrike } from './regime';
5
6
  export { computeHedgeImpulseCurve } from './curve';
6
7
  export { computeCharmIntegral } from './charm';
8
+ export { computePressureCloud } from './pressurecloud';
7
9
  /**
8
10
  * Compute a complete hedge flow analysis for a single expiration.
9
11
  *
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.computeCharmIntegral = exports.computeHedgeImpulseCurve = exports.interpolateIVAtStrike = exports.deriveRegimeParams = void 0;
3
+ exports.computePressureCloud = exports.computeCharmIntegral = exports.computeHedgeImpulseCurve = exports.interpolateIVAtStrike = exports.deriveRegimeParams = void 0;
4
4
  exports.analyzeHedgeFlow = analyzeHedgeFlow;
5
5
  const regime_1 = require("./regime");
6
6
  const curve_1 = require("./curve");
@@ -14,6 +14,9 @@ var curve_2 = require("./curve");
14
14
  Object.defineProperty(exports, "computeHedgeImpulseCurve", { enumerable: true, get: function () { return curve_2.computeHedgeImpulseCurve; } });
15
15
  var charm_2 = require("./charm");
16
16
  Object.defineProperty(exports, "computeCharmIntegral", { enumerable: true, get: function () { return charm_2.computeCharmIntegral; } });
17
+ // Re-export pressure cloud
18
+ var pressurecloud_1 = require("./pressurecloud");
19
+ Object.defineProperty(exports, "computePressureCloud", { enumerable: true, get: function () { return pressurecloud_1.computePressureCloud; } });
17
20
  /**
18
21
  * Compute a complete hedge flow analysis for a single expiration.
19
22
  *
@@ -0,0 +1,166 @@
1
+ import { HedgeImpulseCurve } from './types';
2
+ import { RegimeParams } from './types';
3
+ /**
4
+ * Expected dealer hedge volume in futures contracts for a 1-point spot move.
5
+ * Positive = dealers buying, negative = dealers selling.
6
+ */
7
+ export interface HedgeContractEstimates {
8
+ /** E-mini Nasdaq 100 (multiplier: 20) */
9
+ nq: number;
10
+ /** Micro E-mini Nasdaq 100 (multiplier: 2) */
11
+ mnq: number;
12
+ /** E-mini S&P 500 (multiplier: 50) */
13
+ es: number;
14
+ /** Micro E-mini S&P 500 (multiplier: 5) */
15
+ mes: number;
16
+ }
17
+ /**
18
+ * A zone where price is likely to stabilize (positive impulse)
19
+ * or accelerate (negative impulse).
20
+ */
21
+ export interface PressureZone {
22
+ /** Center price of the zone (peak of the impulse) */
23
+ center: number;
24
+ /** Lower bound of the zone (25th percentile of the peak width) */
25
+ lower: number;
26
+ /** Upper bound of the zone (75th percentile of the peak width) */
27
+ upper: number;
28
+ /** Normalized strength 0-1 (relative to the strongest zone found) */
29
+ strength: number;
30
+ /** Whether this zone is above or below current spot */
31
+ side: 'above-spot' | 'below-spot';
32
+ /**
33
+ * Trade type this zone favors:
34
+ * - Stability zones below spot → long (buy the bounce)
35
+ * - Stability zones above spot → short (sell the rejection)
36
+ * - Acceleration zones below spot → short (momentum downside)
37
+ * - Acceleration zones above spot → long (momentum upside / squeeze)
38
+ */
39
+ tradeType: 'long' | 'short';
40
+ /**
41
+ * Hedge execution type:
42
+ * - passive: dealers post resting limit orders (positive gamma → absorption)
43
+ * - aggressive: dealers send market orders (negative gamma → sweeping)
44
+ */
45
+ hedgeType: 'passive' | 'aggressive';
46
+ }
47
+ /**
48
+ * A regime boundary where behavior flips between mean-reverting and trend-amplifying.
49
+ */
50
+ export interface RegimeEdge {
51
+ /** Price at which the impulse curve crosses zero */
52
+ price: number;
53
+ /** Direction of the transition relative to a downward price move */
54
+ transitionType: 'stable-to-unstable' | 'unstable-to-stable';
55
+ }
56
+ /**
57
+ * Per-price-level detail for the full pressure overlay.
58
+ */
59
+ export interface PressureLevel {
60
+ /** Price level */
61
+ price: number;
62
+ /**
63
+ * Stability score: positive means mean-reverting pressure at this level.
64
+ * Higher = stronger buffering. Weighted by proximity to spot.
65
+ */
66
+ stabilityScore: number;
67
+ /**
68
+ * Acceleration score: positive means trend-amplifying pressure at this level.
69
+ * Higher = stronger momentum fuel. Weighted by proximity to spot.
70
+ */
71
+ accelerationScore: number;
72
+ /**
73
+ * Expected signed hedge contracts (positive = dealers buy, negative = dealers sell)
74
+ * for a 1-point spot move toward this level. Units depend on product.
75
+ * @deprecated Use hedgeContracts instead for multi-product estimates.
76
+ */
77
+ expectedHedgeContracts: number;
78
+ /** Multi-product hedge contract estimates */
79
+ hedgeContracts: HedgeContractEstimates;
80
+ /**
81
+ * Whether dealers would hedge passively (limit orders / absorption)
82
+ * or aggressively (market orders / sweeping) at this level.
83
+ */
84
+ hedgeType: 'passive' | 'aggressive';
85
+ }
86
+ /**
87
+ * Configuration for pressure cloud computation.
88
+ */
89
+ export interface PressureCloudConfig {
90
+ /**
91
+ * Product multiplier for contract conversion.
92
+ * NQ = 20, MNQ = 2, ES = 50, MES = 5, SPY = 100 (shares).
93
+ * Default: 20 (NQ)
94
+ */
95
+ contractMultiplier?: number;
96
+ /** Product type hint for primary contract display. Default: 'NQ' */
97
+ product?: 'NQ' | 'MNQ' | 'ES' | 'MES' | 'SPY';
98
+ /**
99
+ * How many expected-daily-moves to consider "reachable".
100
+ * Levels beyond this get heavily penalized. Default: 2.0
101
+ */
102
+ reachabilityMultiple?: number;
103
+ /**
104
+ * Minimum impulse magnitude (as fraction of mean abs impulse) to
105
+ * qualify as a zone. Default: 0.15
106
+ */
107
+ zoneThreshold?: number;
108
+ }
109
+ /**
110
+ * Complete pressure cloud analysis combining stability and acceleration zones.
111
+ */
112
+ export interface PressureCloud {
113
+ /** Current spot price */
114
+ spot: number;
115
+ /** Expiration timestamp */
116
+ expiration: number;
117
+ /** Timestamp when this cloud was computed */
118
+ computedAt: number;
119
+ /**
120
+ * Stability zones: levels where positive dealer impulse creates
121
+ * mean-reverting pressure. Price decelerates into these zones.
122
+ * Trade approach: reversal / bounce entries.
123
+ */
124
+ stabilityZones: PressureZone[];
125
+ /**
126
+ * Acceleration zones: levels where negative dealer impulse creates
127
+ * trend-amplifying pressure. Price accelerates through these zones.
128
+ * Trade approach: momentum / breakout continuation.
129
+ */
130
+ accelerationZones: PressureZone[];
131
+ /**
132
+ * Regime edges: prices where the impulse curve crosses zero,
133
+ * marking transitions between mean-reverting and trend-amplifying behavior.
134
+ */
135
+ regimeEdges: RegimeEdge[];
136
+ /**
137
+ * Per-price-level detail for the full chart overlay.
138
+ * Every point on the impulse curve grid, enriched with scores and contract estimates.
139
+ */
140
+ priceLevels: PressureLevel[];
141
+ }
142
+ /**
143
+ * Compute a pressure cloud from an existing hedge impulse curve.
144
+ *
145
+ * The pressure cloud translates the raw impulse curve into actionable
146
+ * trading zones:
147
+ *
148
+ * - **Stability zones** (positive impulse peaks, weighted by proximity):
149
+ * Where dealer hedging creates counter-trend flow. These are bounce/rejection
150
+ * targets — price decelerates into them and often stalls or reverses.
151
+ *
152
+ * - **Acceleration zones** (negative impulse troughs, weighted by proximity):
153
+ * Where dealer hedging creates with-trend flow. If price reaches these levels,
154
+ * dealers amplify the move. These are momentum/breakout zones — price
155
+ * accelerates through them.
156
+ *
157
+ * - **Regime edges** (zero crossings):
158
+ * Where behavior flips. Critical for understanding "if price breaks past X,
159
+ * the character of the tape changes."
160
+ *
161
+ * @param impulseCurve - A previously computed hedge impulse curve
162
+ * @param regimeParams - Regime parameters (for expected daily move / reachability)
163
+ * @param config - Optional tuning parameters
164
+ * @returns Complete pressure cloud analysis
165
+ */
166
+ export declare function computePressureCloud(impulseCurve: HedgeImpulseCurve, regimeParams: RegimeParams, config?: PressureCloudConfig): PressureCloud;
@@ -0,0 +1,243 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.computePressureCloud = computePressureCloud;
4
+ /** Product multipliers for contract conversion */
5
+ const PRODUCT_MULTIPLIERS = {
6
+ NQ: 20,
7
+ MNQ: 2,
8
+ ES: 50,
9
+ MES: 5,
10
+ };
11
+ /**
12
+ * Convert a dollar impulse value to hedge contracts for a given product multiplier.
13
+ * contracts = impulse / (multiplier * spot * 0.01)
14
+ */
15
+ function impulseToContracts(impulse, multiplier, spot) {
16
+ const denominator = multiplier * spot * 0.01;
17
+ return denominator > 0 ? sanitize(impulse / denominator) : 0;
18
+ }
19
+ /**
20
+ * Compute multi-product hedge contract estimates from a dollar impulse value.
21
+ */
22
+ function computeHedgeContractEstimates(impulse, spot) {
23
+ return {
24
+ nq: impulseToContracts(impulse, PRODUCT_MULTIPLIERS.NQ, spot),
25
+ mnq: impulseToContracts(impulse, PRODUCT_MULTIPLIERS.MNQ, spot),
26
+ es: impulseToContracts(impulse, PRODUCT_MULTIPLIERS.ES, spot),
27
+ mes: impulseToContracts(impulse, PRODUCT_MULTIPLIERS.MES, spot),
28
+ };
29
+ }
30
+ // ============================================================================
31
+ // Implementation
32
+ // ============================================================================
33
+ /**
34
+ * Compute a pressure cloud from an existing hedge impulse curve.
35
+ *
36
+ * The pressure cloud translates the raw impulse curve into actionable
37
+ * trading zones:
38
+ *
39
+ * - **Stability zones** (positive impulse peaks, weighted by proximity):
40
+ * Where dealer hedging creates counter-trend flow. These are bounce/rejection
41
+ * targets — price decelerates into them and often stalls or reverses.
42
+ *
43
+ * - **Acceleration zones** (negative impulse troughs, weighted by proximity):
44
+ * Where dealer hedging creates with-trend flow. If price reaches these levels,
45
+ * dealers amplify the move. These are momentum/breakout zones — price
46
+ * accelerates through them.
47
+ *
48
+ * - **Regime edges** (zero crossings):
49
+ * Where behavior flips. Critical for understanding "if price breaks past X,
50
+ * the character of the tape changes."
51
+ *
52
+ * @param impulseCurve - A previously computed hedge impulse curve
53
+ * @param regimeParams - Regime parameters (for expected daily move / reachability)
54
+ * @param config - Optional tuning parameters
55
+ * @returns Complete pressure cloud analysis
56
+ */
57
+ function computePressureCloud(impulseCurve, regimeParams, config = {}) {
58
+ const { contractMultiplier = 20, // NQ default
59
+ reachabilityMultiple = 2.0, zoneThreshold = 0.15, } = config;
60
+ const { spot, curve, extrema, zeroCrossings } = impulseCurve;
61
+ const expectedMove = regimeParams.expectedDailySpotMove * spot;
62
+ // Compute reachability-weighted scores for each price level
63
+ const priceLevels = computePriceLevels(curve, spot, expectedMove, reachabilityMultiple, contractMultiplier);
64
+ // Extract stability zones from positive impulse peaks
65
+ const stabilityZones = extractStabilityZones(extrema, curve, spot, expectedMove, reachabilityMultiple, zoneThreshold);
66
+ // Extract acceleration zones from negative impulse troughs
67
+ const accelerationZones = extractAccelerationZones(extrema, curve, spot, expectedMove, reachabilityMultiple, zoneThreshold);
68
+ // Convert zero crossings to regime edges
69
+ const regimeEdges = convertZeroCrossingsToEdges(zeroCrossings, spot);
70
+ return {
71
+ spot,
72
+ expiration: impulseCurve.expiration,
73
+ computedAt: Date.now(),
74
+ stabilityZones,
75
+ accelerationZones,
76
+ regimeEdges,
77
+ priceLevels,
78
+ };
79
+ }
80
+ /**
81
+ * Compute per-price-level detail from the impulse curve.
82
+ */
83
+ function computePriceLevels(curve, spot, expectedMove, reachabilityMultiple, contractMultiplier) {
84
+ const reachRange = expectedMove * reachabilityMultiple;
85
+ return curve.map((point) => {
86
+ const distance = Math.abs(point.price - spot);
87
+ const proximity = Math.exp(-((distance / reachRange) ** 2));
88
+ // Stability: positive impulse weighted by proximity
89
+ const stabilityScore = point.impulse > 0
90
+ ? point.impulse * proximity
91
+ : 0;
92
+ // Acceleration: negative impulse magnitude weighted by proximity
93
+ const accelerationScore = point.impulse < 0
94
+ ? Math.abs(point.impulse) * proximity
95
+ : 0;
96
+ // Convert dollar impulse to signed contract count.
97
+ // impulse is $ of hedging per 1% move. For contracts per 1 point:
98
+ // contracts = impulse / (contractMultiplier * spot * 0.01)
99
+ const contractDenominator = contractMultiplier * spot * 0.01;
100
+ const expectedHedgeContracts = contractDenominator > 0
101
+ ? point.impulse / contractDenominator
102
+ : 0;
103
+ return {
104
+ price: point.price,
105
+ stabilityScore,
106
+ accelerationScore,
107
+ expectedHedgeContracts: sanitize(expectedHedgeContracts),
108
+ hedgeContracts: computeHedgeContractEstimates(point.impulse, spot),
109
+ hedgeType: point.impulse >= 0 ? 'passive' : 'aggressive',
110
+ };
111
+ });
112
+ }
113
+ /**
114
+ * Extract stability zones from positive impulse peaks (basins).
115
+ */
116
+ function extractStabilityZones(extrema, curve, spot, expectedMove, reachabilityMultiple, zoneThreshold) {
117
+ const basins = extrema.filter((e) => e.type === 'basin');
118
+ if (basins.length === 0)
119
+ return [];
120
+ const reachRange = expectedMove * reachabilityMultiple;
121
+ const maxImpulse = Math.max(...basins.map((b) => Math.abs(b.impulse)), 1e-10);
122
+ // Filter by threshold
123
+ const significant = basins.filter((b) => Math.abs(b.impulse) / maxImpulse >= zoneThreshold);
124
+ // Build zones
125
+ const zones = significant.map((basin) => {
126
+ const proximity = Math.exp(-((Math.abs(basin.price - spot) / reachRange) ** 2));
127
+ const rawStrength = (Math.abs(basin.impulse) / maxImpulse) * proximity;
128
+ // Find zone width: where impulse drops to 50% of peak
129
+ const halfPeak = basin.impulse * 0.5;
130
+ const { lower, upper } = findZoneBounds(curve, basin.price, halfPeak);
131
+ const side = basin.price >= spot ? 'above-spot' : 'below-spot';
132
+ const tradeType = side === 'below-spot' ? 'long' : 'short';
133
+ return {
134
+ center: basin.price,
135
+ lower,
136
+ upper,
137
+ strength: Math.min(1, rawStrength),
138
+ side,
139
+ tradeType,
140
+ hedgeType: 'passive',
141
+ };
142
+ });
143
+ // Sort by strength descending
144
+ return zones.sort((a, b) => b.strength - a.strength);
145
+ }
146
+ /**
147
+ * Extract acceleration zones from negative impulse troughs (peaks in the type system).
148
+ */
149
+ function extractAccelerationZones(extrema, curve, spot, expectedMove, reachabilityMultiple, zoneThreshold) {
150
+ const peaks = extrema.filter((e) => e.type === 'peak');
151
+ if (peaks.length === 0)
152
+ return [];
153
+ const reachRange = expectedMove * reachabilityMultiple;
154
+ const maxImpulse = Math.max(...peaks.map((p) => Math.abs(p.impulse)), 1e-10);
155
+ const significant = peaks.filter((p) => Math.abs(p.impulse) / maxImpulse >= zoneThreshold);
156
+ const zones = significant.map((peak) => {
157
+ const proximity = Math.exp(-((Math.abs(peak.price - spot) / reachRange) ** 2));
158
+ const rawStrength = (Math.abs(peak.impulse) / maxImpulse) * proximity;
159
+ const halfTrough = peak.impulse * 0.5;
160
+ const { lower, upper } = findZoneBounds(curve, peak.price, halfTrough);
161
+ const side = peak.price >= spot ? 'above-spot' : 'below-spot';
162
+ // Acceleration below spot → momentum short (waterfall)
163
+ // Acceleration above spot → momentum long (squeeze)
164
+ const tradeType = side === 'below-spot' ? 'short' : 'long';
165
+ return {
166
+ center: peak.price,
167
+ lower,
168
+ upper,
169
+ strength: Math.min(1, rawStrength),
170
+ side,
171
+ tradeType,
172
+ hedgeType: 'aggressive',
173
+ };
174
+ });
175
+ return zones.sort((a, b) => b.strength - a.strength);
176
+ }
177
+ /**
178
+ * Find the price bounds where impulse drops to the given threshold
179
+ * around a peak/trough center price.
180
+ */
181
+ function findZoneBounds(curve, centerPrice, thresholdImpulse) {
182
+ // Find the center index
183
+ let centerIdx = 0;
184
+ let minDist = Infinity;
185
+ for (let i = 0; i < curve.length; i++) {
186
+ const d = Math.abs(curve[i].price - centerPrice);
187
+ if (d < minDist) {
188
+ minDist = d;
189
+ centerIdx = i;
190
+ }
191
+ }
192
+ const isPositive = thresholdImpulse > 0;
193
+ // Scan left for lower bound
194
+ let lowerIdx = centerIdx;
195
+ for (let i = centerIdx - 1; i >= 0; i--) {
196
+ if (isPositive ? curve[i].impulse < thresholdImpulse : curve[i].impulse > thresholdImpulse) {
197
+ lowerIdx = i;
198
+ break;
199
+ }
200
+ lowerIdx = i;
201
+ }
202
+ // Scan right for upper bound
203
+ let upperIdx = centerIdx;
204
+ for (let i = centerIdx + 1; i < curve.length; i++) {
205
+ if (isPositive ? curve[i].impulse < thresholdImpulse : curve[i].impulse > thresholdImpulse) {
206
+ upperIdx = i;
207
+ break;
208
+ }
209
+ upperIdx = i;
210
+ }
211
+ return {
212
+ lower: curve[lowerIdx].price,
213
+ upper: curve[upperIdx].price,
214
+ };
215
+ }
216
+ /**
217
+ * Convert impulse zero crossings into regime edge descriptors.
218
+ */
219
+ function convertZeroCrossingsToEdges(crossings, spot) {
220
+ return crossings.map((crossing) => {
221
+ // A "falling" crossing means impulse goes from positive to negative.
222
+ // If this is below spot, moving down into it means going from
223
+ // stable (positive impulse) to unstable (negative impulse).
224
+ // If above spot, it's the reverse perspective.
225
+ const isBelow = crossing.price < spot;
226
+ let transitionType;
227
+ if (crossing.direction === 'falling') {
228
+ // Impulse goes + → -
229
+ transitionType = isBelow ? 'stable-to-unstable' : 'unstable-to-stable';
230
+ }
231
+ else {
232
+ // Impulse goes - → +
233
+ transitionType = isBelow ? 'unstable-to-stable' : 'stable-to-unstable';
234
+ }
235
+ return {
236
+ price: crossing.price,
237
+ transitionType,
238
+ };
239
+ });
240
+ }
241
+ function sanitize(value) {
242
+ return isFinite(value) && !isNaN(value) ? value : 0;
243
+ }
@@ -114,7 +114,7 @@ export declare const OPEX_CONFIG: ExposureAdjustmentConfig;
114
114
  * @param symbol - Underlying ticker symbol
115
115
  * @param underlyingPrice - Current spot price
116
116
  * @param callOptions - Call options for a single expiry
117
- * @param exposures - Exposure metrics from calculateGammaVannaCharmExposures()
117
+ * @param exposures - Canonical exposure metrics from calculateGammaVannaCharmExposures()
118
118
  * @param config - Adjustment configuration (uses defaults if not provided)
119
119
  * @returns Baseline and adjusted PDFs with comparison metrics
120
120
  *
@@ -122,7 +122,10 @@ export declare const OPEX_CONFIG: ExposureAdjustmentConfig;
122
122
  * ```typescript
123
123
  * // Get exposures first
124
124
  * const allExposures = calculateGammaVannaCharmExposures(chain, ivSurfaces);
125
- * const expiryExposures = allExposures.find(e => e.expiration === targetExpiry);
125
+ * const expiry = allExposures.find(e => e.expiration === targetExpiry);
126
+ * const expiryExposures = expiry
127
+ * ? { spotPrice: expiry.spotPrice, expiration: expiry.expiration, ...expiry.canonical }
128
+ * : undefined;
126
129
  *
127
130
  * // Calculate adjusted PDF
128
131
  * const result = estimateExposureAdjustedPDF(
@@ -122,7 +122,7 @@ exports.OPEX_CONFIG = {
122
122
  * @param symbol - Underlying ticker symbol
123
123
  * @param underlyingPrice - Current spot price
124
124
  * @param callOptions - Call options for a single expiry
125
- * @param exposures - Exposure metrics from calculateGammaVannaCharmExposures()
125
+ * @param exposures - Canonical exposure metrics from calculateGammaVannaCharmExposures()
126
126
  * @param config - Adjustment configuration (uses defaults if not provided)
127
127
  * @returns Baseline and adjusted PDFs with comparison metrics
128
128
  *
@@ -130,7 +130,10 @@ exports.OPEX_CONFIG = {
130
130
  * ```typescript
131
131
  * // Get exposures first
132
132
  * const allExposures = calculateGammaVannaCharmExposures(chain, ivSurfaces);
133
- * const expiryExposures = allExposures.find(e => e.expiration === targetExpiry);
133
+ * const expiry = allExposures.find(e => e.expiration === targetExpiry);
134
+ * const expiryExposures = expiry
135
+ * ? { spotPrice: expiry.spotPrice, expiration: expiry.expiration, ...expiry.canonical }
136
+ * : undefined;
134
137
  *
135
138
  * // Calculate adjusted PDF
136
139
  * const result = estimateExposureAdjustedPDF(
package/dist/index.d.ts CHANGED
@@ -14,8 +14,8 @@ export { buildOCCSymbol, parseOCCSymbol, generateStrikesAroundSpot, generateOCCS
14
14
  export type { OCCSymbolParams, ParsedOCCSymbol, StrikeGenerationParams, } from './utils/occ';
15
15
  export { estimateImpliedProbabilityDistribution, estimateImpliedProbabilityDistributions, getProbabilityInRange, getCumulativeProbability, getQuantile, estimateExposureAdjustedPDF, getEdgeAtPrice, getSignificantAdjustmentLevels, DEFAULT_ADJUSTMENT_CONFIG, LOW_VOL_CONFIG, CRISIS_CONFIG, OPEX_CONFIG, } from './impliedpdf';
16
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
+ export { deriveRegimeParams, interpolateIVAtStrike, computeHedgeImpulseCurve, computeCharmIntegral, computePressureCloud, analyzeHedgeFlow, } from './hedgeflow';
18
+ export type { MarketRegime, RegimeParams, HedgeImpulseConfig, HedgeImpulsePoint, HedgeImpulseCurve, ZeroCrossing, ImpulseExtremum, DirectionalAsymmetry, ImpulseRegime, CharmIntegralConfig, CharmBucket, CharmIntegral, HedgeFlowAnalysis, HedgeContractEstimates, PressureZone, RegimeEdge, PressureLevel, PressureCloudConfig, PressureCloud, } from './hedgeflow';
19
19
  export { FloeClient, Broker } from './client/FloeClient';
20
20
  export { TradierClient } from './client/brokers/TradierClient';
21
21
  export { TastyTradeClient } from './client/brokers/TastyTradeClient';
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.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;
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.computePressureCloud = 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
@@ -67,7 +67,7 @@ Object.defineProperty(exports, "DEFAULT_ADJUSTMENT_CONFIG", { enumerable: true,
67
67
  Object.defineProperty(exports, "LOW_VOL_CONFIG", { enumerable: true, get: function () { return impliedpdf_1.LOW_VOL_CONFIG; } });
68
68
  Object.defineProperty(exports, "CRISIS_CONFIG", { enumerable: true, get: function () { return impliedpdf_1.CRISIS_CONFIG; } });
69
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)
70
+ // Hedge flow analysis (impulse curve, charm integral, regime derivation, pressure cloud)
71
71
  var hedgeflow_1 = require("./hedgeflow");
72
72
  // Regime derivation from IV surface
73
73
  Object.defineProperty(exports, "deriveRegimeParams", { enumerable: true, get: function () { return hedgeflow_1.deriveRegimeParams; } });
@@ -76,6 +76,8 @@ Object.defineProperty(exports, "interpolateIVAtStrike", { enumerable: true, get:
76
76
  Object.defineProperty(exports, "computeHedgeImpulseCurve", { enumerable: true, get: function () { return hedgeflow_1.computeHedgeImpulseCurve; } });
77
77
  // Charm integral
78
78
  Object.defineProperty(exports, "computeCharmIntegral", { enumerable: true, get: function () { return hedgeflow_1.computeCharmIntegral; } });
79
+ // Pressure cloud (stability/acceleration zones)
80
+ Object.defineProperty(exports, "computePressureCloud", { enumerable: true, get: function () { return hedgeflow_1.computePressureCloud; } });
79
81
  // Combined analysis
80
82
  Object.defineProperty(exports, "analyzeHedgeFlow", { enumerable: true, get: function () { return hedgeflow_1.analyzeHedgeFlow; } });
81
83
  // Client
@@ -172,6 +172,52 @@ export interface StrikeExposure {
172
172
  /** Net exposure (gamma + vanna + charm) */
173
173
  netExposure: number;
174
174
  }
175
+ /**
176
+ * Input options for exposure calculations.
177
+ */
178
+ export interface ExposureCalculationOptions {
179
+ /**
180
+ * Reference timestamp in milliseconds.
181
+ * If omitted, Date.now() is used.
182
+ */
183
+ asOfTimestamp?: number;
184
+ }
185
+ /**
186
+ * Exposure vector used by variant outputs.
187
+ */
188
+ export interface ExposureVector {
189
+ gammaExposure: number;
190
+ vannaExposure: number;
191
+ charmExposure: number;
192
+ netExposure: number;
193
+ }
194
+ /**
195
+ * Per-strike exposure variants (canonical, state-weighted, and flow delta).
196
+ */
197
+ export interface StrikeExposureVariants {
198
+ strikePrice: number;
199
+ canonical: ExposureVector;
200
+ stateWeighted: ExposureVector;
201
+ flowDelta: ExposureVector;
202
+ }
203
+ /**
204
+ * Full exposure breakdown for one mode.
205
+ */
206
+ export interface ExposureModeBreakdown {
207
+ totalGammaExposure: number;
208
+ totalVannaExposure: number;
209
+ totalCharmExposure: number;
210
+ totalNetExposure: number;
211
+ strikeOfMaxGamma: number;
212
+ strikeOfMinGamma: number;
213
+ strikeOfMaxVanna: number;
214
+ strikeOfMinVanna: number;
215
+ strikeOfMaxCharm: number;
216
+ strikeOfMinCharm: number;
217
+ strikeOfMaxNet: number;
218
+ strikeOfMinNet: number;
219
+ strikeExposures: StrikeExposure[];
220
+ }
175
221
  /**
176
222
  * Exposure metrics per expiration
177
223
  */
@@ -207,6 +253,26 @@ export interface ExposurePerExpiry {
207
253
  /** Per-strike exposures */
208
254
  strikeExposures: StrikeExposure[];
209
255
  }
256
+ /**
257
+ * Exposure variants per expiration.
258
+ * canonical: classic per-1% / per-vol-point / per-day definitions
259
+ * stateWeighted: level-weighted vanna/charm (gamma remains canonical)
260
+ * flowDelta: exposure deltas from intraday OI changes (liveOpenInterest - openInterest)
261
+ */
262
+ export interface ExposureVariantsPerExpiry {
263
+ /** Current spot price */
264
+ spotPrice: number;
265
+ /** Expiration timestamp in milliseconds */
266
+ expiration: number;
267
+ /** Canonical exposure mode */
268
+ canonical: ExposureModeBreakdown;
269
+ /** State-weighted exposure mode */
270
+ stateWeighted: ExposureModeBreakdown;
271
+ /** Flow-delta exposure mode */
272
+ flowDelta: ExposureModeBreakdown;
273
+ /** Per-strike variants */
274
+ strikeExposureVariants: StrikeExposureVariants[];
275
+ }
210
276
  /**
211
277
  * Dealer Gamma Exposure (GEX) metrics
212
278
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fullstackcraftllc/floe",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "Production-ready options analytics toolkit. Normalize broker data structures and calculate Black-Scholes, Greeks, and exposures with a clean, type-safe API. Built for trading platforms and fintech applications.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,7 +8,9 @@
8
8
  "build": "tsc",
9
9
  "test": "jest",
10
10
  "prepublishOnly": "npm run build",
11
- "type-check": "tsc --noEmit"
11
+ "type-check": "tsc --noEmit",
12
+ "prepare": "husky",
13
+ "copy-whitepaper": "cp whitepaper/whitepaper.pdf site/public/whitepaper.pdf"
12
14
  },
13
15
  "keywords": [
14
16
  "options",
@@ -42,6 +44,7 @@
42
44
  "@types/jest": "^29.5.0",
43
45
  "@types/node": "^20.0.0",
44
46
  "dotenv": "^17.2.3",
47
+ "husky": "^9.1.7",
45
48
  "jest": "^29.5.0",
46
49
  "ts-jest": "^29.1.0",
47
50
  "typescript": "^5.9.3"