@fullstackcraftllc/floe 0.0.1
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.
- package/LICENSE-COMMERCIAL +74 -0
- package/LICENSE-MIT +27 -0
- package/LICENSE.md +65 -0
- package/README.md +272 -0
- package/dist/adapters/index.d.ts +52 -0
- package/dist/adapters/index.js +132 -0
- package/dist/blackscholes/index.d.ts +73 -0
- package/dist/blackscholes/index.js +266 -0
- package/dist/exposure/index.d.ts +35 -0
- package/dist/exposure/index.js +212 -0
- package/dist/greeks/index.d.ts +29 -0
- package/dist/greeks/index.js +107 -0
- package/dist/impliedpdf/index.d.ts +0 -0
- package/dist/impliedpdf/index.js +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +56 -0
- package/dist/types/index.d.ts +228 -0
- package/dist/types/index.js +14 -0
- package/dist/utils/statistics.d.ts +18 -0
- package/dist/utils/statistics.js +32 -0
- package/dist/volatility/index.d.ts +37 -0
- package/dist/volatility/index.js +163 -0
- package/dist/volatility/smoothing.d.ts +17 -0
- package/dist/volatility/smoothing.js +171 -0
- package/package.json +55 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @fullstackcraftllc/floe - Options Analytics Library
|
|
4
|
+
*
|
|
5
|
+
* A comprehensive TypeScript library for options pricing, Greeks calculation,
|
|
6
|
+
* and dealer exposure analysis across multiple brokers.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
20
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
+
exports.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = 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
|
+
// Core types
|
|
25
|
+
__exportStar(require("./types"), exports);
|
|
26
|
+
// Black-Scholes pricing and Greeks
|
|
27
|
+
var blackscholes_1 = require("./blackscholes");
|
|
28
|
+
Object.defineProperty(exports, "blackScholes", { enumerable: true, get: function () { return blackscholes_1.blackScholes; } });
|
|
29
|
+
Object.defineProperty(exports, "calculateGreeks", { enumerable: true, get: function () { return blackscholes_1.calculateGreeks; } });
|
|
30
|
+
Object.defineProperty(exports, "calculateImpliedVolatility", { enumerable: true, get: function () { return blackscholes_1.calculateImpliedVolatility; } });
|
|
31
|
+
Object.defineProperty(exports, "getMillisecondsToExpiration", { enumerable: true, get: function () { return blackscholes_1.getMillisecondsToExpiration; } });
|
|
32
|
+
Object.defineProperty(exports, "getTimeToExpirationInYears", { enumerable: true, get: function () { return blackscholes_1.getTimeToExpirationInYears; } });
|
|
33
|
+
// Volatility surface construction
|
|
34
|
+
var volatility_1 = require("./volatility");
|
|
35
|
+
Object.defineProperty(exports, "getIVSurfaces", { enumerable: true, get: function () { return volatility_1.getIVSurfaces; } });
|
|
36
|
+
Object.defineProperty(exports, "getIVForStrike", { enumerable: true, get: function () { return volatility_1.getIVForStrike; } });
|
|
37
|
+
// Smoothing algorithms
|
|
38
|
+
var smoothing_1 = require("./volatility/smoothing");
|
|
39
|
+
Object.defineProperty(exports, "smoothTotalVarianceSmile", { enumerable: true, get: function () { return smoothing_1.smoothTotalVarianceSmile; } });
|
|
40
|
+
// Exposure calculations
|
|
41
|
+
var exposure_1 = require("./exposure");
|
|
42
|
+
Object.defineProperty(exports, "calculateGammaVannaCharmExposures", { enumerable: true, get: function () { return exposure_1.calculateGammaVannaCharmExposures; } });
|
|
43
|
+
Object.defineProperty(exports, "calculateSharesNeededToCover", { enumerable: true, get: function () { return exposure_1.calculateSharesNeededToCover; } });
|
|
44
|
+
// Statistical utilities
|
|
45
|
+
var statistics_1 = require("./utils/statistics");
|
|
46
|
+
Object.defineProperty(exports, "cumulativeNormalDistribution", { enumerable: true, get: function () { return statistics_1.cumulativeNormalDistribution; } });
|
|
47
|
+
Object.defineProperty(exports, "normalPDF", { enumerable: true, get: function () { return statistics_1.normalPDF; } });
|
|
48
|
+
// Broker adapters
|
|
49
|
+
var adapters_1 = require("./adapters");
|
|
50
|
+
Object.defineProperty(exports, "genericAdapter", { enumerable: true, get: function () { return adapters_1.genericAdapter; } });
|
|
51
|
+
Object.defineProperty(exports, "schwabAdapter", { enumerable: true, get: function () { return adapters_1.schwabAdapter; } });
|
|
52
|
+
Object.defineProperty(exports, "ibkrAdapter", { enumerable: true, get: function () { return adapters_1.ibkrAdapter; } });
|
|
53
|
+
Object.defineProperty(exports, "tdaAdapter", { enumerable: true, get: function () { return adapters_1.tdaAdapter; } });
|
|
54
|
+
Object.defineProperty(exports, "brokerAdapters", { enumerable: true, get: function () { return adapters_1.brokerAdapters; } });
|
|
55
|
+
Object.defineProperty(exports, "getAdapter", { enumerable: true, get: function () { return adapters_1.getAdapter; } });
|
|
56
|
+
Object.defineProperty(exports, "createOptionChain", { enumerable: true, get: function () { return adapters_1.createOptionChain; } });
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for options analytics
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Option type (call or put)
|
|
6
|
+
*/
|
|
7
|
+
export type OptionType = 'call' | 'put';
|
|
8
|
+
/**
|
|
9
|
+
* Volatility calculation method
|
|
10
|
+
*/
|
|
11
|
+
export type VolatilityModel = 'blackscholes' | 'svm' | 'garch';
|
|
12
|
+
/**
|
|
13
|
+
* Smoothing method for IV surface
|
|
14
|
+
*/
|
|
15
|
+
export type SmoothingModel = 'totalvariance' | 'none';
|
|
16
|
+
/**
|
|
17
|
+
* Parameters for Black-Scholes calculation
|
|
18
|
+
*/
|
|
19
|
+
export interface BlackScholesParams {
|
|
20
|
+
/** Current price of the underlying asset */
|
|
21
|
+
spot: number;
|
|
22
|
+
/** Strike price of the option */
|
|
23
|
+
strike: number;
|
|
24
|
+
/** Time to expiration in years */
|
|
25
|
+
timeToExpiry: number;
|
|
26
|
+
/** Implied volatility (annualized, as decimal e.g., 0.20 for 20%) */
|
|
27
|
+
volatility: number;
|
|
28
|
+
/** Risk-free interest rate (annualized, as decimal) */
|
|
29
|
+
riskFreeRate: number;
|
|
30
|
+
/** Option type */
|
|
31
|
+
optionType: OptionType;
|
|
32
|
+
/** Dividend yield (annualized, as decimal) */
|
|
33
|
+
dividendYield?: number;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Complete set of option Greeks and higher-order Greeks
|
|
37
|
+
*/
|
|
38
|
+
export interface Greeks {
|
|
39
|
+
/** Price: Option theoretical value */
|
|
40
|
+
price: number;
|
|
41
|
+
/** Delta: Rate of change of option price with respect to underlying price */
|
|
42
|
+
delta: number;
|
|
43
|
+
/** Gamma: Rate of change of delta with respect to underlying price */
|
|
44
|
+
gamma: number;
|
|
45
|
+
/** Theta: Rate of change of option price with respect to time (per day) */
|
|
46
|
+
theta: number;
|
|
47
|
+
/** Vega: Rate of change of option price with respect to volatility (per 1% change) */
|
|
48
|
+
vega: number;
|
|
49
|
+
/** Rho: Rate of change of option price with respect to interest rate (per 1% change) */
|
|
50
|
+
rho: number;
|
|
51
|
+
/** Charm: Rate of change of delta with respect to time (per day) */
|
|
52
|
+
charm: number;
|
|
53
|
+
/** Vanna: Rate of change of delta with respect to volatility */
|
|
54
|
+
vanna: number;
|
|
55
|
+
/** Volga (Vomma): Rate of change of vega with respect to volatility */
|
|
56
|
+
volga: number;
|
|
57
|
+
/** Speed: Rate of change of gamma with respect to underlying price */
|
|
58
|
+
speed: number;
|
|
59
|
+
/** Zomma: Rate of change of gamma with respect to volatility */
|
|
60
|
+
zomma: number;
|
|
61
|
+
/** Color: Rate of change of gamma with respect to time */
|
|
62
|
+
color: number;
|
|
63
|
+
/** Ultima: Rate of change of volga with respect to volatility */
|
|
64
|
+
ultima: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Normalized option data structure (broker-agnostic)
|
|
68
|
+
*/
|
|
69
|
+
export interface NormalizedOption {
|
|
70
|
+
/** Strike price */
|
|
71
|
+
strike: number;
|
|
72
|
+
/** Expiration date (ISO 8601) */
|
|
73
|
+
expiration: string;
|
|
74
|
+
/** Expiration timestamp in milliseconds */
|
|
75
|
+
expirationTimestamp: number;
|
|
76
|
+
/** Option type */
|
|
77
|
+
optionType: OptionType;
|
|
78
|
+
/** Current bid price */
|
|
79
|
+
bid: number;
|
|
80
|
+
/** Current ask price */
|
|
81
|
+
ask: number;
|
|
82
|
+
/** Mark (mid) price */
|
|
83
|
+
mark: number;
|
|
84
|
+
/** Last traded price */
|
|
85
|
+
last: number;
|
|
86
|
+
/** Trading volume */
|
|
87
|
+
volume: number;
|
|
88
|
+
/** Open interest */
|
|
89
|
+
openInterest: number;
|
|
90
|
+
/** Implied volatility (as decimal) */
|
|
91
|
+
impliedVolatility: number;
|
|
92
|
+
/** Pre-calculated Greeks (optional) */
|
|
93
|
+
greeks?: Greeks;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Complete option chain with market context
|
|
97
|
+
*/
|
|
98
|
+
export interface OptionChain {
|
|
99
|
+
/** Underlying symbol */
|
|
100
|
+
symbol: string;
|
|
101
|
+
/** Current spot price of the underlying */
|
|
102
|
+
spot: number;
|
|
103
|
+
/** Risk-free interest rate (as decimal, e.g., 0.05 for 5%) */
|
|
104
|
+
riskFreeRate: number;
|
|
105
|
+
/** Dividend yield (as decimal, e.g., 0.02 for 2%) */
|
|
106
|
+
dividendYield: number;
|
|
107
|
+
/** All options in the chain */
|
|
108
|
+
options: NormalizedOption[];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* IV Surface for a single expiration and option type
|
|
112
|
+
*/
|
|
113
|
+
export interface IVSurface {
|
|
114
|
+
/** Expiration timestamp in milliseconds */
|
|
115
|
+
expirationDate: number;
|
|
116
|
+
/** Option type (CALL or PUT) */
|
|
117
|
+
putCall: OptionType;
|
|
118
|
+
/** Sorted strike prices */
|
|
119
|
+
strikes: number[];
|
|
120
|
+
/** Raw calculated IVs (as percentages) */
|
|
121
|
+
rawIVs: number[];
|
|
122
|
+
/** Smoothed IVs after applying smoothing algorithm (as percentages) */
|
|
123
|
+
smoothedIVs: number[];
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Strike-level exposure metrics
|
|
127
|
+
*/
|
|
128
|
+
export interface StrikeExposure {
|
|
129
|
+
/** Strike price */
|
|
130
|
+
strikePrice: number;
|
|
131
|
+
/** Gamma exposure at this strike */
|
|
132
|
+
gammaExposure: number;
|
|
133
|
+
/** Vanna exposure at this strike */
|
|
134
|
+
vannaExposure: number;
|
|
135
|
+
/** Charm exposure at this strike */
|
|
136
|
+
charmExposure: number;
|
|
137
|
+
/** Net exposure (gamma + vanna + charm) */
|
|
138
|
+
netExposure: number;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Exposure metrics per expiration
|
|
142
|
+
*/
|
|
143
|
+
export interface ExposurePerExpiry {
|
|
144
|
+
/** Current spot price */
|
|
145
|
+
spotPrice: number;
|
|
146
|
+
/** Expiration timestamp in milliseconds */
|
|
147
|
+
expiration: number;
|
|
148
|
+
/** Total gamma exposure for this expiration */
|
|
149
|
+
totalGammaExposure: number;
|
|
150
|
+
/** Total vanna exposure for this expiration */
|
|
151
|
+
totalVannaExposure: number;
|
|
152
|
+
/** Total charm exposure for this expiration */
|
|
153
|
+
totalCharmExposure: number;
|
|
154
|
+
/** Total net exposure (sum of all three) */
|
|
155
|
+
totalNetExposure: number;
|
|
156
|
+
/** Strike with maximum gamma */
|
|
157
|
+
strikeOfMaxGamma: number;
|
|
158
|
+
/** Strike with minimum gamma */
|
|
159
|
+
strikeOfMinGamma: number;
|
|
160
|
+
/** Strike with maximum vanna */
|
|
161
|
+
strikeOfMaxVanna: number;
|
|
162
|
+
/** Strike with minimum vanna */
|
|
163
|
+
strikeOfMinVanna: number;
|
|
164
|
+
/** Strike with maximum charm */
|
|
165
|
+
strikeOfMaxCharm: number;
|
|
166
|
+
/** Strike with minimum charm */
|
|
167
|
+
strikeOfMinCharm: number;
|
|
168
|
+
/** Strike with maximum net exposure */
|
|
169
|
+
strikeOfMaxNet: number;
|
|
170
|
+
/** Strike with minimum net exposure */
|
|
171
|
+
strikeOfMinNet: number;
|
|
172
|
+
/** Per-strike exposures */
|
|
173
|
+
strikeExposures: StrikeExposure[];
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Dealer Gamma Exposure (GEX) metrics
|
|
177
|
+
*/
|
|
178
|
+
export interface GEXMetrics {
|
|
179
|
+
/** Total gamma exposure by strike */
|
|
180
|
+
byStrike: Map<number, number>;
|
|
181
|
+
/** Net gamma exposure */
|
|
182
|
+
netGamma: number;
|
|
183
|
+
/** Largest positive gamma strike */
|
|
184
|
+
maxPositiveStrike: number;
|
|
185
|
+
/** Largest negative gamma strike */
|
|
186
|
+
maxNegativeStrike: number;
|
|
187
|
+
/** Zero gamma level (flip point) */
|
|
188
|
+
zeroGammaLevel: number | null;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Dealer Vanna Exposure (VEX) metrics
|
|
192
|
+
*/
|
|
193
|
+
export interface VannaMetrics {
|
|
194
|
+
/** Total vanna exposure by strike */
|
|
195
|
+
byStrike: Map<number, number>;
|
|
196
|
+
/** Net vanna exposure */
|
|
197
|
+
netVanna: number;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Dealer Charm Exposure (CEX) metrics
|
|
201
|
+
*/
|
|
202
|
+
export interface CharmMetrics {
|
|
203
|
+
/** Total charm exposure by strike */
|
|
204
|
+
byStrike: Map<number, number>;
|
|
205
|
+
/** Net charm exposure */
|
|
206
|
+
netCharm: number;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Broker-specific data types
|
|
210
|
+
*/
|
|
211
|
+
/**
|
|
212
|
+
* Raw option data from any broker (to be normalized)
|
|
213
|
+
*/
|
|
214
|
+
export interface RawOptionData {
|
|
215
|
+
[key: string]: any;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Adapter function type for normalizing broker data
|
|
219
|
+
*/
|
|
220
|
+
export type BrokerAdapter = (data: RawOptionData) => NormalizedOption;
|
|
221
|
+
/**
|
|
222
|
+
* Constants
|
|
223
|
+
*/
|
|
224
|
+
export declare const MILLISECONDS_PER_YEAR = 31536000000;
|
|
225
|
+
export declare const MILLISECONDS_PER_DAY = 86400000;
|
|
226
|
+
export declare const MINUTES_PER_YEAR = 525600;
|
|
227
|
+
export declare const MINUTES_PER_DAY = 1440;
|
|
228
|
+
export declare const DAYS_PER_YEAR = 365;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core types for options analytics
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DAYS_PER_YEAR = exports.MINUTES_PER_DAY = exports.MINUTES_PER_YEAR = exports.MILLISECONDS_PER_DAY = exports.MILLISECONDS_PER_YEAR = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Constants
|
|
9
|
+
*/
|
|
10
|
+
exports.MILLISECONDS_PER_YEAR = 31536000000;
|
|
11
|
+
exports.MILLISECONDS_PER_DAY = 86400000;
|
|
12
|
+
exports.MINUTES_PER_YEAR = 525600;
|
|
13
|
+
exports.MINUTES_PER_DAY = 1440;
|
|
14
|
+
exports.DAYS_PER_YEAR = 365;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistical utility functions
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Cumulative distribution function for standard normal distribution
|
|
6
|
+
* Using an approximation method (Abramowitz and Stegun)
|
|
7
|
+
*
|
|
8
|
+
* @param x - Input value
|
|
9
|
+
* @returns Cumulative probability
|
|
10
|
+
*/
|
|
11
|
+
export declare function cumulativeNormalDistribution(x: number): number;
|
|
12
|
+
/**
|
|
13
|
+
* Probability density function for standard normal distribution
|
|
14
|
+
*
|
|
15
|
+
* @param x - Input value
|
|
16
|
+
* @returns Probability density
|
|
17
|
+
*/
|
|
18
|
+
export declare function normalPDF(x: number): number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Statistical utility functions
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.cumulativeNormalDistribution = cumulativeNormalDistribution;
|
|
7
|
+
exports.normalPDF = normalPDF;
|
|
8
|
+
/**
|
|
9
|
+
* Cumulative distribution function for standard normal distribution
|
|
10
|
+
* Using an approximation method (Abramowitz and Stegun)
|
|
11
|
+
*
|
|
12
|
+
* @param x - Input value
|
|
13
|
+
* @returns Cumulative probability
|
|
14
|
+
*/
|
|
15
|
+
function cumulativeNormalDistribution(x) {
|
|
16
|
+
const t = 1 / (1 + 0.2316419 * Math.abs(x));
|
|
17
|
+
const d = 0.3989423 * Math.exp((-x * x) / 2);
|
|
18
|
+
const probability = d *
|
|
19
|
+
t *
|
|
20
|
+
(0.3193815 +
|
|
21
|
+
t * (-0.3565638 + t * (1.781478 + t * (-1.821256 + t * 1.330274))));
|
|
22
|
+
return x > 0 ? 1 - probability : probability;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Probability density function for standard normal distribution
|
|
26
|
+
*
|
|
27
|
+
* @param x - Input value
|
|
28
|
+
* @returns Probability density
|
|
29
|
+
*/
|
|
30
|
+
function normalPDF(x) {
|
|
31
|
+
return Math.exp((-x * x) / 2) / Math.sqrt(2 * Math.PI);
|
|
32
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { OptionChain, IVSurface, VolatilityModel, SmoothingModel } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Build IV surfaces for all expirations in an option chain
|
|
4
|
+
*
|
|
5
|
+
* @param volatilityModel - Method to calculate IV
|
|
6
|
+
* - 'blackscholes': Use Black-Scholes IV inversion (implemented) ✅
|
|
7
|
+
* - 'svm': TODO - Support Vector Machine based IV estimation
|
|
8
|
+
* - 'garch': TODO - GARCH model for volatility forecasting
|
|
9
|
+
* @param smoothingModel - Smoothing method to apply
|
|
10
|
+
* - 'totalvariance': Cubic spline + convex hull (implemented) ✅
|
|
11
|
+
* - 'none': No smoothing, use raw IVs ✅
|
|
12
|
+
* - TODO: Future - 'svi', 'ssvi', 'sabr' parametric models
|
|
13
|
+
* @param chain - Option chain with market context (spot, rates, options)
|
|
14
|
+
* @returns Array of IV surfaces (one per expiration per option type)
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const surfaces = getIVSurfaces('blackscholes', 'totalvariance', chain);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function getIVSurfaces(volatilityModel: VolatilityModel, smoothingModel: SmoothingModel, chain: OptionChain): IVSurface[];
|
|
22
|
+
/**
|
|
23
|
+
* Get smoothed IV for a specific expiration, option type, and strike
|
|
24
|
+
*
|
|
25
|
+
* @param ivSurfaces - Array of IV surfaces
|
|
26
|
+
* @param expiration - Expiration timestamp in milliseconds
|
|
27
|
+
* @param optionType - 'call' or 'put'
|
|
28
|
+
* @param strike - Strike price
|
|
29
|
+
* @returns Smoothed IV as a percentage, or 0 if not found
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const iv = getIVForStrike(surfaces, 1234567890000, 'call', 105);
|
|
34
|
+
* console.log(`IV: ${iv}%`);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function getIVForStrike(ivSurfaces: IVSurface[], expiration: number, optionType: 'call' | 'put', strike: number): number;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getIVSurfaces = getIVSurfaces;
|
|
4
|
+
exports.getIVForStrike = getIVForStrike;
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
const blackscholes_1 = require("../blackscholes");
|
|
7
|
+
const smoothing_1 = require("./smoothing");
|
|
8
|
+
/**
|
|
9
|
+
* Build IV surfaces for all expirations in an option chain
|
|
10
|
+
*
|
|
11
|
+
* @param volatilityModel - Method to calculate IV
|
|
12
|
+
* - 'blackscholes': Use Black-Scholes IV inversion (implemented) ✅
|
|
13
|
+
* - 'svm': TODO - Support Vector Machine based IV estimation
|
|
14
|
+
* - 'garch': TODO - GARCH model for volatility forecasting
|
|
15
|
+
* @param smoothingModel - Smoothing method to apply
|
|
16
|
+
* - 'totalvariance': Cubic spline + convex hull (implemented) ✅
|
|
17
|
+
* - 'none': No smoothing, use raw IVs ✅
|
|
18
|
+
* - TODO: Future - 'svi', 'ssvi', 'sabr' parametric models
|
|
19
|
+
* @param chain - Option chain with market context (spot, rates, options)
|
|
20
|
+
* @returns Array of IV surfaces (one per expiration per option type)
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const surfaces = getIVSurfaces('blackscholes', 'totalvariance', chain);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function getIVSurfaces(volatilityModel, smoothingModel, chain) {
|
|
28
|
+
const { spot, riskFreeRate, dividendYield, options } = chain;
|
|
29
|
+
const ivSurfaces = [];
|
|
30
|
+
// Get all unique expirations from options
|
|
31
|
+
const expirationsSet = new Set();
|
|
32
|
+
for (const option of options) {
|
|
33
|
+
expirationsSet.add(option.expirationTimestamp);
|
|
34
|
+
}
|
|
35
|
+
const expirations = Array.from(expirationsSet).sort((a, b) => a - b);
|
|
36
|
+
// Loop through all expirations
|
|
37
|
+
for (const expiration of expirations) {
|
|
38
|
+
// Separate CALL and PUT surfaces for this expiration
|
|
39
|
+
const callStrikes = [];
|
|
40
|
+
const callIVs = [];
|
|
41
|
+
const putStrikes = [];
|
|
42
|
+
const putIVs = [];
|
|
43
|
+
// Loop through options to find those that match the expiration
|
|
44
|
+
for (const option of options) {
|
|
45
|
+
if (option.expirationTimestamp !== expiration) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
// Compute IV using selected model
|
|
49
|
+
if (volatilityModel === 'blackscholes') {
|
|
50
|
+
const timeToExpirationInYears = (0, blackscholes_1.getTimeToExpirationInYears)(option.expirationTimestamp);
|
|
51
|
+
const isCall = option.optionType === 'call';
|
|
52
|
+
// Rates are already decimals in OptionChain
|
|
53
|
+
const iv = (0, blackscholes_1.calculateImpliedVolatility)(option.mark, spot, option.strike, riskFreeRate, dividendYield, timeToExpirationInYears, option.optionType);
|
|
54
|
+
if (isCall) {
|
|
55
|
+
callStrikes.push(option.strike);
|
|
56
|
+
callIVs.push(iv);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
putStrikes.push(option.strike);
|
|
60
|
+
putIVs.push(iv);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// TODO: Implement 'svm' model
|
|
64
|
+
// else if (volatilityModel === 'svm') {
|
|
65
|
+
// // Support Vector Machine based IV estimation
|
|
66
|
+
// // Could use historical data patterns to predict IV
|
|
67
|
+
// }
|
|
68
|
+
// TODO: Implement 'garch' model
|
|
69
|
+
// else if (volatilityModel === 'garch') {
|
|
70
|
+
// // GARCH model for volatility forecasting
|
|
71
|
+
// // Time series approach for IV prediction
|
|
72
|
+
// }
|
|
73
|
+
}
|
|
74
|
+
// Helper to sort and append surface
|
|
75
|
+
const appendSurface = (strikes, ivs, optionType) => {
|
|
76
|
+
if (strikes.length === 0) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Sort by strike
|
|
80
|
+
const pairs = strikes.map((strike, i) => ({ strike, iv: ivs[i] }));
|
|
81
|
+
pairs.sort((a, b) => a.strike - b.strike);
|
|
82
|
+
const sortedStrikes = pairs.map((p) => p.strike);
|
|
83
|
+
const rawIVs = pairs.map((p) => p.iv);
|
|
84
|
+
// Default: no smoothing, smoothedIVs = copy of rawIVs
|
|
85
|
+
let smoothedIVs = [...rawIVs];
|
|
86
|
+
// Only smooth if we have enough valid data points (IV > 1.5% floor)
|
|
87
|
+
// Filter out floor values before smoothing to avoid contamination
|
|
88
|
+
if (smoothingModel === 'totalvariance' && expiration > Date.now()) {
|
|
89
|
+
const IV_FLOOR = 1.5; // IVs at or below this are considered unreliable (deep ITM/OTM)
|
|
90
|
+
// Find the range of valid IVs
|
|
91
|
+
const validData = [];
|
|
92
|
+
for (let i = 0; i < rawIVs.length; i++) {
|
|
93
|
+
if (rawIVs[i] > IV_FLOOR) {
|
|
94
|
+
validData.push({
|
|
95
|
+
strike: sortedStrikes[i],
|
|
96
|
+
iv: rawIVs[i],
|
|
97
|
+
index: i,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// Only smooth if we have at least 5 valid points
|
|
102
|
+
if (validData.length >= 5) {
|
|
103
|
+
const validStrikes = validData.map((d) => d.strike);
|
|
104
|
+
const validIVs = validData.map((d) => d.iv);
|
|
105
|
+
const T = (expiration - Date.now()) / types_1.MILLISECONDS_PER_YEAR;
|
|
106
|
+
const smoothedValidIVs = (0, smoothing_1.smoothTotalVarianceSmile)(validStrikes, validIVs, T);
|
|
107
|
+
// Copy smoothed values back to corresponding indices
|
|
108
|
+
smoothedIVs = [...rawIVs]; // Start with raw IVs
|
|
109
|
+
for (let j = 0; j < validData.length; j++) {
|
|
110
|
+
smoothedIVs[validData[j].index] = smoothedValidIVs[j];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// TODO: Future smoothing models
|
|
115
|
+
// else if (smoothingModel === 'svi') {
|
|
116
|
+
// // Stochastic Volatility Inspired parametric model
|
|
117
|
+
// }
|
|
118
|
+
// else if (smoothingModel === 'ssvi') {
|
|
119
|
+
// // Surface SVI for multiple expirations
|
|
120
|
+
// }
|
|
121
|
+
const ivSurface = {
|
|
122
|
+
expirationDate: expiration,
|
|
123
|
+
putCall: optionType,
|
|
124
|
+
strikes: sortedStrikes,
|
|
125
|
+
rawIVs,
|
|
126
|
+
smoothedIVs,
|
|
127
|
+
};
|
|
128
|
+
ivSurfaces.push(ivSurface);
|
|
129
|
+
};
|
|
130
|
+
// Append call and put surfaces if present
|
|
131
|
+
appendSurface(callStrikes, callIVs, 'call');
|
|
132
|
+
appendSurface(putStrikes, putIVs, 'put');
|
|
133
|
+
}
|
|
134
|
+
return ivSurfaces;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get smoothed IV for a specific expiration, option type, and strike
|
|
138
|
+
*
|
|
139
|
+
* @param ivSurfaces - Array of IV surfaces
|
|
140
|
+
* @param expiration - Expiration timestamp in milliseconds
|
|
141
|
+
* @param optionType - 'call' or 'put'
|
|
142
|
+
* @param strike - Strike price
|
|
143
|
+
* @returns Smoothed IV as a percentage, or 0 if not found
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const iv = getIVForStrike(surfaces, 1234567890000, 'call', 105);
|
|
148
|
+
* console.log(`IV: ${iv}%`);
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
function getIVForStrike(ivSurfaces, expiration, optionType, strike) {
|
|
152
|
+
for (const ivSurface of ivSurfaces) {
|
|
153
|
+
if (ivSurface.expirationDate === expiration && ivSurface.putCall === optionType) {
|
|
154
|
+
// Find the strike in the strikes array
|
|
155
|
+
for (let i = 0; i < ivSurface.strikes.length; i++) {
|
|
156
|
+
if (ivSurface.strikes[i] === strike) {
|
|
157
|
+
return ivSurface.smoothedIVs[i];
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return 0.0;
|
|
163
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Volatility surface smoothing algorithms
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Smooth total variance smile using cubic spline interpolation and convexity enforcement
|
|
6
|
+
*
|
|
7
|
+
* @param strikes - Sorted array of strike prices
|
|
8
|
+
* @param ivs - Array of IVs as percentages (e.g., 20 = 20%)
|
|
9
|
+
* @param T - Time to expiration in years
|
|
10
|
+
* @returns Smoothed IVs as percentages
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* const smoothed = smoothTotalVarianceSmile([90, 95, 100, 105, 110], [22, 20, 18, 20, 22], 0.25);
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function smoothTotalVarianceSmile(strikes: number[], ivs: number[], T: number): number[];
|