@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
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Volatility surface smoothing algorithms
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.smoothTotalVarianceSmile = smoothTotalVarianceSmile;
|
|
7
|
+
/**
|
|
8
|
+
* Smooth total variance smile using cubic spline interpolation and convexity enforcement
|
|
9
|
+
*
|
|
10
|
+
* @param strikes - Sorted array of strike prices
|
|
11
|
+
* @param ivs - Array of IVs as percentages (e.g., 20 = 20%)
|
|
12
|
+
* @param T - Time to expiration in years
|
|
13
|
+
* @returns Smoothed IVs as percentages
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* const smoothed = smoothTotalVarianceSmile([90, 95, 100, 105, 110], [22, 20, 18, 20, 22], 0.25);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function smoothTotalVarianceSmile(strikes, ivs, T) {
|
|
21
|
+
const n = strikes.length;
|
|
22
|
+
if (n <= 2) {
|
|
23
|
+
return ivs; // Not enough points to smooth meaningfully
|
|
24
|
+
}
|
|
25
|
+
// Convert IV% → decimal and total variance
|
|
26
|
+
const w = new Array(n);
|
|
27
|
+
for (let i = 0; i < n; i++) {
|
|
28
|
+
const vol = ivs[i] / 100.0; // percent → decimal
|
|
29
|
+
w[i] = vol * vol * T;
|
|
30
|
+
}
|
|
31
|
+
// Step 1: cubic spline interpolation on w(K)
|
|
32
|
+
const spline = new CubicSpline(strikes, w);
|
|
33
|
+
// Step 2: evaluate spline at existing strikes (already sorted)
|
|
34
|
+
const smoothedW = new Array(n);
|
|
35
|
+
for (let i = 0; i < n; i++) {
|
|
36
|
+
smoothedW[i] = spline.eval(strikes[i]);
|
|
37
|
+
}
|
|
38
|
+
// Step 3: enforce convexity of total variance (simple projection)
|
|
39
|
+
enforceConvexity(strikes, smoothedW);
|
|
40
|
+
// Step 4: convert total variance back to IV (percent)
|
|
41
|
+
const smoothedIV = new Array(n);
|
|
42
|
+
for (let i = 0; i < n; i++) {
|
|
43
|
+
if (smoothedW[i] <= 0) {
|
|
44
|
+
smoothedIV[i] = ivs[i]; // fallback
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
smoothedIV[i] = Math.sqrt(smoothedW[i] / T) * 100.0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return smoothedIV;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Cubic spline interpolation
|
|
54
|
+
*/
|
|
55
|
+
class CubicSpline {
|
|
56
|
+
constructor(x, y) {
|
|
57
|
+
const n = x.length;
|
|
58
|
+
this.x = x;
|
|
59
|
+
this.a = [...y];
|
|
60
|
+
this.b = new Array(n).fill(0);
|
|
61
|
+
this.c = new Array(n).fill(0);
|
|
62
|
+
this.d = new Array(n).fill(0);
|
|
63
|
+
const h = new Array(n - 1);
|
|
64
|
+
for (let i = 0; i < n - 1; i++) {
|
|
65
|
+
h[i] = x[i + 1] - x[i];
|
|
66
|
+
}
|
|
67
|
+
const alpha = new Array(n - 1).fill(0);
|
|
68
|
+
for (let i = 1; i < n - 1; i++) {
|
|
69
|
+
alpha[i] = (3 * (this.a[i + 1] - this.a[i])) / h[i] - (3 * (this.a[i] - this.a[i - 1])) / h[i - 1];
|
|
70
|
+
}
|
|
71
|
+
const l = new Array(n).fill(0);
|
|
72
|
+
const mu = new Array(n).fill(0);
|
|
73
|
+
const z = new Array(n).fill(0);
|
|
74
|
+
l[0] = 1;
|
|
75
|
+
mu[0] = 0;
|
|
76
|
+
z[0] = 0;
|
|
77
|
+
for (let i = 1; i < n - 1; i++) {
|
|
78
|
+
l[i] = 2 * (x[i + 1] - x[i - 1]) - h[i - 1] * mu[i - 1];
|
|
79
|
+
mu[i] = h[i] / l[i];
|
|
80
|
+
z[i] = (alpha[i] - h[i - 1] * z[i - 1]) / l[i];
|
|
81
|
+
}
|
|
82
|
+
l[n - 1] = 1;
|
|
83
|
+
z[n - 1] = 0;
|
|
84
|
+
this.c[n - 1] = 0;
|
|
85
|
+
for (let j = n - 2; j >= 0; j--) {
|
|
86
|
+
this.c[j] = z[j] - mu[j] * this.c[j + 1];
|
|
87
|
+
this.b[j] = (this.a[j + 1] - this.a[j]) / h[j] - (h[j] * (this.c[j + 1] + 2 * this.c[j])) / 3;
|
|
88
|
+
this.d[j] = (this.c[j + 1] - this.c[j]) / (3 * h[j]);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
eval(x) {
|
|
92
|
+
// Find interval
|
|
93
|
+
const n = this.x.length;
|
|
94
|
+
let i = this.findInterval(x, n);
|
|
95
|
+
if (i > 0 && i >= n - 1) {
|
|
96
|
+
i = n - 2;
|
|
97
|
+
}
|
|
98
|
+
const dx = x - this.x[i];
|
|
99
|
+
return this.a[i] + this.b[i] * dx + this.c[i] * dx * dx + this.d[i] * dx * dx * dx;
|
|
100
|
+
}
|
|
101
|
+
findInterval(x, n) {
|
|
102
|
+
// Binary search
|
|
103
|
+
let left = 0;
|
|
104
|
+
let right = n - 1;
|
|
105
|
+
while (left < right - 1) {
|
|
106
|
+
const mid = Math.floor((left + right) / 2);
|
|
107
|
+
if (this.x[mid] <= x) {
|
|
108
|
+
left = mid;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
right = mid;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return left;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Enforce convexity of total variance using convex hull approach
|
|
119
|
+
*
|
|
120
|
+
* @param x - Strike prices
|
|
121
|
+
* @param w - Total variance values (modified in place)
|
|
122
|
+
*/
|
|
123
|
+
function enforceConvexity(x, w) {
|
|
124
|
+
const n = w.length;
|
|
125
|
+
if (n < 3) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Build the lower convex hull of (x, w) points
|
|
129
|
+
// This guarantees convexity and runs in O(n)
|
|
130
|
+
const hull = [];
|
|
131
|
+
for (let i = 0; i < n; i++) {
|
|
132
|
+
const p = { x: x[i], w: w[i] };
|
|
133
|
+
// Remove points that would break convexity
|
|
134
|
+
while (hull.length >= 2) {
|
|
135
|
+
const h1 = hull[hull.length - 1];
|
|
136
|
+
const h2 = hull[hull.length - 2];
|
|
137
|
+
// Cross product to check if we make a left turn (convex)
|
|
138
|
+
// (h1 - h2) x (p - h2) should be >= 0 for convexity
|
|
139
|
+
const cross = (h1.x - h2.x) * (p.w - h2.w) - (h1.w - h2.w) * (p.x - h2.x);
|
|
140
|
+
if (cross >= 0) {
|
|
141
|
+
break; // convex, keep h1
|
|
142
|
+
}
|
|
143
|
+
hull.pop(); // remove h1, it breaks convexity
|
|
144
|
+
}
|
|
145
|
+
hull.push(p);
|
|
146
|
+
}
|
|
147
|
+
// Now interpolate the hull values back to original x positions
|
|
148
|
+
let hullIdx = 0;
|
|
149
|
+
for (let i = 0; i < n; i++) {
|
|
150
|
+
// Find the hull segment containing x[i]
|
|
151
|
+
while (hullIdx < hull.length - 1 && hull[hullIdx + 1].x < x[i]) {
|
|
152
|
+
hullIdx++;
|
|
153
|
+
}
|
|
154
|
+
if (hullIdx >= hull.length - 1) {
|
|
155
|
+
hullIdx = hull.length - 2;
|
|
156
|
+
}
|
|
157
|
+
if (hullIdx < 0) {
|
|
158
|
+
hullIdx = 0;
|
|
159
|
+
}
|
|
160
|
+
// Linear interpolation on the hull
|
|
161
|
+
const h1 = hull[hullIdx];
|
|
162
|
+
const h2 = hull[hullIdx + 1];
|
|
163
|
+
if (h2.x === h1.x) {
|
|
164
|
+
w[i] = h1.w;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const t = (x[i] - h1.x) / (h2.x - h1.x);
|
|
168
|
+
w[i] = h1.w + t * (h2.w - h1.w);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fullstackcraftllc/floe",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Production-ready options analytics toolkit. Normalize broker data structures and calculate Black-Scholes, Greeks, and GEX with a clean, type-safe API. Built for trading platforms and fintech applications.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "jest",
|
|
10
|
+
"prepublishOnly": "npm run build",
|
|
11
|
+
"type-check": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"options",
|
|
15
|
+
"black-scholes",
|
|
16
|
+
"greeks",
|
|
17
|
+
"gamma",
|
|
18
|
+
"delta",
|
|
19
|
+
"vanna",
|
|
20
|
+
"charm",
|
|
21
|
+
"dealer-exposure",
|
|
22
|
+
"gex",
|
|
23
|
+
"trading",
|
|
24
|
+
"finance",
|
|
25
|
+
"typescript"
|
|
26
|
+
],
|
|
27
|
+
"author": {
|
|
28
|
+
"name": "Full Stack Craft LLC",
|
|
29
|
+
"email": "hi@fullstackcraft.com",
|
|
30
|
+
"url": "https://fullstackcraft.com"
|
|
31
|
+
},
|
|
32
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/FullStackCraft/floe.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/FullStackCraft/floe/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/FullStackCraft/floe#readme",
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@types/jest": "^29.5.0",
|
|
43
|
+
"@types/node": "^20.0.0",
|
|
44
|
+
"jest": "^29.5.0",
|
|
45
|
+
"ts-jest": "^29.1.0",
|
|
46
|
+
"typescript": "^5.0.0"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE.md",
|
|
52
|
+
"LICENSE-MIT",
|
|
53
|
+
"LICENSE-COMMERCIAL"
|
|
54
|
+
]
|
|
55
|
+
}
|