@fullstackcraftllc/floe 0.0.3 → 0.0.4
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/README.md +22 -0
- package/dist/client/FloeClient.d.ts +5 -5
- package/dist/client/FloeClient.js +49 -5
- package/dist/client/brokers/TastyTradeClient.d.ts +384 -0
- package/dist/client/brokers/TastyTradeClient.js +1081 -0
- package/dist/impliedpdf/index.d.ts +148 -0
- package/dist/impliedpdf/index.js +277 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -1
- package/dist/volatility/index.d.ts +1 -1
- package/dist/volatility/index.js +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { NormalizedOption } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Strike-level probability from the implied PDF
|
|
4
|
+
*/
|
|
5
|
+
export interface StrikeProbability {
|
|
6
|
+
/** Strike price */
|
|
7
|
+
strike: number;
|
|
8
|
+
/** Probability density at this strike (normalized to sum to 1) */
|
|
9
|
+
probability: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Implied probability distribution derived from option prices
|
|
13
|
+
* using Breeden-Litzenberger style numerical differentiation
|
|
14
|
+
*/
|
|
15
|
+
export interface ImpliedProbabilityDistribution {
|
|
16
|
+
/** Underlying symbol */
|
|
17
|
+
symbol: string;
|
|
18
|
+
/** Expiration timestamp in milliseconds */
|
|
19
|
+
expiryDate: number;
|
|
20
|
+
/** Timestamp when the distribution was calculated (milliseconds) */
|
|
21
|
+
calculationTimestamp: number;
|
|
22
|
+
/** Current underlying price */
|
|
23
|
+
underlyingPrice: number;
|
|
24
|
+
/** Strike probabilities (the PDF) */
|
|
25
|
+
strikeProbabilities: StrikeProbability[];
|
|
26
|
+
/** Most likely price (mode of the distribution) */
|
|
27
|
+
mostLikelyPrice: number;
|
|
28
|
+
/** Median price (50th percentile) */
|
|
29
|
+
medianPrice: number;
|
|
30
|
+
/** Expected value (mean) of the distribution */
|
|
31
|
+
expectedValue: number;
|
|
32
|
+
/** Expected move (standard deviation) */
|
|
33
|
+
expectedMove: number;
|
|
34
|
+
/** Tail skew ratio (right tail / left tail relative to mean) */
|
|
35
|
+
tailSkew: number;
|
|
36
|
+
/** Cumulative probability of finishing above current spot */
|
|
37
|
+
cumulativeProbabilityAboveSpot: number;
|
|
38
|
+
/** Cumulative probability of finishing below current spot */
|
|
39
|
+
cumulativeProbabilityBelowSpot: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Result of estimating implied probability distribution
|
|
43
|
+
*/
|
|
44
|
+
export type ImpliedPDFResult = {
|
|
45
|
+
success: true;
|
|
46
|
+
distribution: ImpliedProbabilityDistribution;
|
|
47
|
+
} | {
|
|
48
|
+
success: false;
|
|
49
|
+
error: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Estimate an implied probability density function (PDF) for a single expiry
|
|
53
|
+
* using Breeden-Litzenberger style numerical differentiation of call prices.
|
|
54
|
+
*
|
|
55
|
+
* This method computes the second derivative of call option prices with respect
|
|
56
|
+
* to strike price, which under risk-neutral pricing gives the probability density
|
|
57
|
+
* of the underlying ending at each strike.
|
|
58
|
+
*
|
|
59
|
+
* @param symbol - Underlying ticker symbol
|
|
60
|
+
* @param underlyingPrice - Current spot/mark price of the underlying
|
|
61
|
+
* @param callOptions - Array of call options for a single expiry (must have bid > 0 and ask > 0)
|
|
62
|
+
* @returns ImpliedProbabilityDistribution with strike-level probabilities and summary statistics
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const result = estimateImpliedProbabilityDistribution(
|
|
67
|
+
* 'QQQ',
|
|
68
|
+
* 500.00,
|
|
69
|
+
* callOptionsForExpiry
|
|
70
|
+
* );
|
|
71
|
+
*
|
|
72
|
+
* if (result.success) {
|
|
73
|
+
* console.log('Mode:', result.distribution.mostLikelyPrice);
|
|
74
|
+
* console.log('Expected move:', result.distribution.expectedMove);
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export declare function estimateImpliedProbabilityDistribution(symbol: string, underlyingPrice: number, callOptions: NormalizedOption[]): ImpliedPDFResult;
|
|
79
|
+
/**
|
|
80
|
+
* Estimate implied probability distributions for all expirations in an option chain
|
|
81
|
+
*
|
|
82
|
+
* @param symbol - Underlying ticker symbol
|
|
83
|
+
* @param underlyingPrice - Current spot/mark price of the underlying
|
|
84
|
+
* @param options - Array of all options (calls and puts, all expirations)
|
|
85
|
+
* @returns Array of ImpliedProbabilityDistribution for each expiration
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const distributions = estimateImpliedProbabilityDistributions(
|
|
90
|
+
* 'QQQ',
|
|
91
|
+
* 500.00,
|
|
92
|
+
* chain.options
|
|
93
|
+
* );
|
|
94
|
+
*
|
|
95
|
+
* for (const dist of distributions) {
|
|
96
|
+
* console.log(`Expiry: ${new Date(dist.expiryDate).toISOString()}`);
|
|
97
|
+
* console.log(`Mode: ${dist.mostLikelyPrice}`);
|
|
98
|
+
* }
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function estimateImpliedProbabilityDistributions(symbol: string, underlyingPrice: number, options: NormalizedOption[]): ImpliedProbabilityDistribution[];
|
|
102
|
+
/**
|
|
103
|
+
* Get the probability of the underlying finishing between two price levels
|
|
104
|
+
*
|
|
105
|
+
* @param distribution - Implied probability distribution
|
|
106
|
+
* @param lowerBound - Lower price bound
|
|
107
|
+
* @param upperBound - Upper price bound
|
|
108
|
+
* @returns Probability of finishing between the bounds
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* // Probability of QQQ finishing between 490 and 510
|
|
113
|
+
* const prob = getProbabilityInRange(distribution, 490, 510);
|
|
114
|
+
* console.log(`${(prob * 100).toFixed(1)}% chance of finishing in range`);
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export declare function getProbabilityInRange(distribution: ImpliedProbabilityDistribution, lowerBound: number, upperBound: number): number;
|
|
118
|
+
/**
|
|
119
|
+
* Get the cumulative probability up to a given price level
|
|
120
|
+
*
|
|
121
|
+
* @param distribution - Implied probability distribution
|
|
122
|
+
* @param price - Price level
|
|
123
|
+
* @returns Cumulative probability of finishing at or below the price
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* // Probability of QQQ finishing at or below 495
|
|
128
|
+
* const prob = getCumulativeProbability(distribution, 495);
|
|
129
|
+
* console.log(`${(prob * 100).toFixed(1)}% chance of finishing <= 495`);
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export declare function getCumulativeProbability(distribution: ImpliedProbabilityDistribution, price: number): number;
|
|
133
|
+
/**
|
|
134
|
+
* Get the quantile (inverse CDF) for a given probability
|
|
135
|
+
*
|
|
136
|
+
* @param distribution - Implied probability distribution
|
|
137
|
+
* @param probability - Probability value between 0 and 1
|
|
138
|
+
* @returns Strike price at the given probability quantile
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```typescript
|
|
142
|
+
* // Find the 5th and 95th percentile strikes
|
|
143
|
+
* const p5 = getQuantile(distribution, 0.05);
|
|
144
|
+
* const p95 = getQuantile(distribution, 0.95);
|
|
145
|
+
* console.log(`90% confidence interval: [${p5}, ${p95}]`);
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export declare function getQuantile(distribution: ImpliedProbabilityDistribution, probability: number): number;
|
package/dist/impliedpdf/index.js
CHANGED
|
@@ -1 +1,278 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.estimateImpliedProbabilityDistribution = estimateImpliedProbabilityDistribution;
|
|
4
|
+
exports.estimateImpliedProbabilityDistributions = estimateImpliedProbabilityDistributions;
|
|
5
|
+
exports.getProbabilityInRange = getProbabilityInRange;
|
|
6
|
+
exports.getCumulativeProbability = getCumulativeProbability;
|
|
7
|
+
exports.getQuantile = getQuantile;
|
|
8
|
+
/**
|
|
9
|
+
* Estimate an implied probability density function (PDF) for a single expiry
|
|
10
|
+
* using Breeden-Litzenberger style numerical differentiation of call prices.
|
|
11
|
+
*
|
|
12
|
+
* This method computes the second derivative of call option prices with respect
|
|
13
|
+
* to strike price, which under risk-neutral pricing gives the probability density
|
|
14
|
+
* of the underlying ending at each strike.
|
|
15
|
+
*
|
|
16
|
+
* @param symbol - Underlying ticker symbol
|
|
17
|
+
* @param underlyingPrice - Current spot/mark price of the underlying
|
|
18
|
+
* @param callOptions - Array of call options for a single expiry (must have bid > 0 and ask > 0)
|
|
19
|
+
* @returns ImpliedProbabilityDistribution with strike-level probabilities and summary statistics
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const result = estimateImpliedProbabilityDistribution(
|
|
24
|
+
* 'QQQ',
|
|
25
|
+
* 500.00,
|
|
26
|
+
* callOptionsForExpiry
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* if (result.success) {
|
|
30
|
+
* console.log('Mode:', result.distribution.mostLikelyPrice);
|
|
31
|
+
* console.log('Expected move:', result.distribution.expectedMove);
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
function estimateImpliedProbabilityDistribution(symbol, underlyingPrice, callOptions) {
|
|
36
|
+
// Sort call options by strike price ascending
|
|
37
|
+
const sortedOptions = [...callOptions].sort((a, b) => a.strike - b.strike);
|
|
38
|
+
const n = sortedOptions.length;
|
|
39
|
+
if (n < 3) {
|
|
40
|
+
return { success: false, error: 'Not enough data points (need at least 3 call options)' };
|
|
41
|
+
}
|
|
42
|
+
// Get expiration from first option (assuming all same expiry)
|
|
43
|
+
const expiryDate = sortedOptions[0].expirationTimestamp;
|
|
44
|
+
// Estimate second derivative numerically (central difference)
|
|
45
|
+
// f(K) ≈ d²C/dK² where C is the call price
|
|
46
|
+
const strikeProbabilities = new Array(n);
|
|
47
|
+
// Initialize edge cases with zero probability
|
|
48
|
+
strikeProbabilities[0] = { strike: sortedOptions[0].strike, probability: 0 };
|
|
49
|
+
strikeProbabilities[n - 1] = { strike: sortedOptions[n - 1].strike, probability: 0 };
|
|
50
|
+
for (let i = 1; i < n - 1; i++) {
|
|
51
|
+
const kPrev = sortedOptions[i - 1].strike;
|
|
52
|
+
const kCurr = sortedOptions[i].strike;
|
|
53
|
+
const kNext = sortedOptions[i + 1].strike;
|
|
54
|
+
// Use mid prices for stability
|
|
55
|
+
const midPrev = (sortedOptions[i - 1].bid + sortedOptions[i - 1].ask) / 2;
|
|
56
|
+
const midCurr = (sortedOptions[i].bid + sortedOptions[i].ask) / 2;
|
|
57
|
+
const midNext = (sortedOptions[i + 1].bid + sortedOptions[i + 1].ask) / 2;
|
|
58
|
+
const cPrev = midPrev;
|
|
59
|
+
const cNext = midNext;
|
|
60
|
+
// Protect against division by zero if strikes are too close
|
|
61
|
+
const strikeDiff = kNext - kPrev;
|
|
62
|
+
if (Math.abs(strikeDiff) < 1e-9) {
|
|
63
|
+
strikeProbabilities[i] = { strike: kCurr, probability: 0 };
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
// Second derivative: d²C/dK² ≈ (C(K+) - 2*C(K) + C(K-)) / (ΔK)²
|
|
67
|
+
const d2 = (cNext - 2 * midCurr + cPrev) / Math.pow(strikeDiff, 2);
|
|
68
|
+
// f(K) = e^{rT} * d²C/dK², ignoring discount for simplicity
|
|
69
|
+
// Ensure non-negative probability
|
|
70
|
+
strikeProbabilities[i] = { strike: kCurr, probability: Math.max(d2, 0) };
|
|
71
|
+
}
|
|
72
|
+
// Normalize densities to sum to 1
|
|
73
|
+
let sum = 0;
|
|
74
|
+
for (const sp of strikeProbabilities) {
|
|
75
|
+
sum += sp.probability;
|
|
76
|
+
}
|
|
77
|
+
// Protect against division by zero if all probabilities are 0
|
|
78
|
+
if (sum < 1e-9) {
|
|
79
|
+
return { success: false, error: `Insufficient probability mass to normalize (sum=${sum})` };
|
|
80
|
+
}
|
|
81
|
+
for (let i = 0; i < strikeProbabilities.length; i++) {
|
|
82
|
+
strikeProbabilities[i].probability /= sum;
|
|
83
|
+
}
|
|
84
|
+
// Compute summary statistics
|
|
85
|
+
// Most likely price (mode)
|
|
86
|
+
let mostLikelyPrice = strikeProbabilities[0].strike;
|
|
87
|
+
let maxProb = 0;
|
|
88
|
+
for (const sp of strikeProbabilities) {
|
|
89
|
+
if (sp.probability > maxProb) {
|
|
90
|
+
maxProb = sp.probability;
|
|
91
|
+
mostLikelyPrice = sp.strike;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Compute cumulative distribution for median
|
|
95
|
+
let cumulative = 0;
|
|
96
|
+
let medianPrice = strikeProbabilities[Math.floor(strikeProbabilities.length / 2)].strike;
|
|
97
|
+
for (const sp of strikeProbabilities) {
|
|
98
|
+
cumulative += sp.probability;
|
|
99
|
+
if (cumulative >= 0.5) {
|
|
100
|
+
medianPrice = sp.strike;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Expected value (mean)
|
|
105
|
+
let mean = 0;
|
|
106
|
+
for (const sp of strikeProbabilities) {
|
|
107
|
+
mean += sp.strike * sp.probability;
|
|
108
|
+
}
|
|
109
|
+
// Variance and expected move (standard deviation)
|
|
110
|
+
let variance = 0;
|
|
111
|
+
for (const sp of strikeProbabilities) {
|
|
112
|
+
const diff = sp.strike - mean;
|
|
113
|
+
variance += diff * diff * sp.probability;
|
|
114
|
+
}
|
|
115
|
+
const expectedMove = Math.sqrt(variance);
|
|
116
|
+
// Tail skew: rightTail / leftTail relative to mean
|
|
117
|
+
let leftTail = 0;
|
|
118
|
+
let rightTail = 0;
|
|
119
|
+
for (const sp of strikeProbabilities) {
|
|
120
|
+
if (sp.strike < mean) {
|
|
121
|
+
leftTail += sp.probability;
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
rightTail += sp.probability;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
const tailSkew = rightTail / Math.max(leftTail, 1e-9);
|
|
128
|
+
// Cumulative probabilities above and below spot price
|
|
129
|
+
let cumulativeBelowSpot = 0;
|
|
130
|
+
let cumulativeAboveSpot = 0;
|
|
131
|
+
for (const sp of strikeProbabilities) {
|
|
132
|
+
if (sp.strike < underlyingPrice) {
|
|
133
|
+
cumulativeBelowSpot += sp.probability;
|
|
134
|
+
}
|
|
135
|
+
else if (sp.strike > underlyingPrice) {
|
|
136
|
+
cumulativeAboveSpot += sp.probability;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return {
|
|
140
|
+
success: true,
|
|
141
|
+
distribution: {
|
|
142
|
+
symbol,
|
|
143
|
+
expiryDate,
|
|
144
|
+
calculationTimestamp: Date.now(),
|
|
145
|
+
underlyingPrice,
|
|
146
|
+
strikeProbabilities,
|
|
147
|
+
mostLikelyPrice,
|
|
148
|
+
medianPrice,
|
|
149
|
+
expectedValue: mean,
|
|
150
|
+
expectedMove,
|
|
151
|
+
tailSkew,
|
|
152
|
+
cumulativeProbabilityAboveSpot: cumulativeAboveSpot,
|
|
153
|
+
cumulativeProbabilityBelowSpot: cumulativeBelowSpot,
|
|
154
|
+
},
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Estimate implied probability distributions for all expirations in an option chain
|
|
159
|
+
*
|
|
160
|
+
* @param symbol - Underlying ticker symbol
|
|
161
|
+
* @param underlyingPrice - Current spot/mark price of the underlying
|
|
162
|
+
* @param options - Array of all options (calls and puts, all expirations)
|
|
163
|
+
* @returns Array of ImpliedProbabilityDistribution for each expiration
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* ```typescript
|
|
167
|
+
* const distributions = estimateImpliedProbabilityDistributions(
|
|
168
|
+
* 'QQQ',
|
|
169
|
+
* 500.00,
|
|
170
|
+
* chain.options
|
|
171
|
+
* );
|
|
172
|
+
*
|
|
173
|
+
* for (const dist of distributions) {
|
|
174
|
+
* console.log(`Expiry: ${new Date(dist.expiryDate).toISOString()}`);
|
|
175
|
+
* console.log(`Mode: ${dist.mostLikelyPrice}`);
|
|
176
|
+
* }
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
function estimateImpliedProbabilityDistributions(symbol, underlyingPrice, options) {
|
|
180
|
+
// Get unique expirations
|
|
181
|
+
const expirationsSet = new Set();
|
|
182
|
+
for (const option of options) {
|
|
183
|
+
expirationsSet.add(option.expirationTimestamp);
|
|
184
|
+
}
|
|
185
|
+
const expirations = Array.from(expirationsSet).sort((a, b) => a - b);
|
|
186
|
+
const distributions = [];
|
|
187
|
+
for (const expiry of expirations) {
|
|
188
|
+
// Filter to call options at this expiry with valid bid/ask
|
|
189
|
+
const callOptionsAtExpiry = options.filter((opt) => opt.expirationTimestamp === expiry &&
|
|
190
|
+
opt.optionType === 'call' &&
|
|
191
|
+
opt.bid > 0 &&
|
|
192
|
+
opt.ask > 0);
|
|
193
|
+
const result = estimateImpliedProbabilityDistribution(symbol, underlyingPrice, callOptionsAtExpiry);
|
|
194
|
+
if (result.success) {
|
|
195
|
+
distributions.push(result.distribution);
|
|
196
|
+
}
|
|
197
|
+
// Silently skip expirations that don't have enough data
|
|
198
|
+
}
|
|
199
|
+
return distributions;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Get the probability of the underlying finishing between two price levels
|
|
203
|
+
*
|
|
204
|
+
* @param distribution - Implied probability distribution
|
|
205
|
+
* @param lowerBound - Lower price bound
|
|
206
|
+
* @param upperBound - Upper price bound
|
|
207
|
+
* @returns Probability of finishing between the bounds
|
|
208
|
+
*
|
|
209
|
+
* @example
|
|
210
|
+
* ```typescript
|
|
211
|
+
* // Probability of QQQ finishing between 490 and 510
|
|
212
|
+
* const prob = getProbabilityInRange(distribution, 490, 510);
|
|
213
|
+
* console.log(`${(prob * 100).toFixed(1)}% chance of finishing in range`);
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
function getProbabilityInRange(distribution, lowerBound, upperBound) {
|
|
217
|
+
let probability = 0;
|
|
218
|
+
for (const sp of distribution.strikeProbabilities) {
|
|
219
|
+
if (sp.strike >= lowerBound && sp.strike <= upperBound) {
|
|
220
|
+
probability += sp.probability;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return probability;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get the cumulative probability up to a given price level
|
|
227
|
+
*
|
|
228
|
+
* @param distribution - Implied probability distribution
|
|
229
|
+
* @param price - Price level
|
|
230
|
+
* @returns Cumulative probability of finishing at or below the price
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* ```typescript
|
|
234
|
+
* // Probability of QQQ finishing at or below 495
|
|
235
|
+
* const prob = getCumulativeProbability(distribution, 495);
|
|
236
|
+
* console.log(`${(prob * 100).toFixed(1)}% chance of finishing <= 495`);
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
function getCumulativeProbability(distribution, price) {
|
|
240
|
+
let probability = 0;
|
|
241
|
+
for (const sp of distribution.strikeProbabilities) {
|
|
242
|
+
if (sp.strike <= price) {
|
|
243
|
+
probability += sp.probability;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return probability;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get the quantile (inverse CDF) for a given probability
|
|
250
|
+
*
|
|
251
|
+
* @param distribution - Implied probability distribution
|
|
252
|
+
* @param probability - Probability value between 0 and 1
|
|
253
|
+
* @returns Strike price at the given probability quantile
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* ```typescript
|
|
257
|
+
* // Find the 5th and 95th percentile strikes
|
|
258
|
+
* const p5 = getQuantile(distribution, 0.05);
|
|
259
|
+
* const p95 = getQuantile(distribution, 0.95);
|
|
260
|
+
* console.log(`90% confidence interval: [${p5}, ${p95}]`);
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
function getQuantile(distribution, probability) {
|
|
264
|
+
if (probability <= 0) {
|
|
265
|
+
return distribution.strikeProbabilities[0]?.strike ?? 0;
|
|
266
|
+
}
|
|
267
|
+
if (probability >= 1) {
|
|
268
|
+
return distribution.strikeProbabilities[distribution.strikeProbabilities.length - 1]?.strike ?? 0;
|
|
269
|
+
}
|
|
270
|
+
let cumulative = 0;
|
|
271
|
+
for (const sp of distribution.strikeProbabilities) {
|
|
272
|
+
cumulative += sp.probability;
|
|
273
|
+
if (cumulative >= probability) {
|
|
274
|
+
return sp.strike;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return distribution.strikeProbabilities[distribution.strikeProbabilities.length - 1]?.strike ?? 0;
|
|
278
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ 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
17
|
export { FloeClient, Broker } from './client/FloeClient';
|
|
16
18
|
export { TradierClient } from './client/brokers/TradierClient';
|
|
17
19
|
export type { AggressorSide, IntradayTrade } from './client/brokers/TradierClient';
|
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.TradierClient = exports.Broker = exports.FloeClient = 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.createOptionChain = exports.getAdapter = exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = 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;
|
|
24
24
|
// Core types
|
|
25
25
|
__exportStar(require("./types"), exports);
|
|
26
26
|
// Black-Scholes pricing and Greeks
|
|
@@ -52,6 +52,13 @@ Object.defineProperty(exports, "parseOCCSymbol", { enumerable: true, get: functi
|
|
|
52
52
|
Object.defineProperty(exports, "generateStrikesAroundSpot", { enumerable: true, get: function () { return occ_1.generateStrikesAroundSpot; } });
|
|
53
53
|
Object.defineProperty(exports, "generateOCCSymbolsForStrikes", { enumerable: true, get: function () { return occ_1.generateOCCSymbolsForStrikes; } });
|
|
54
54
|
Object.defineProperty(exports, "generateOCCSymbolsAroundSpot", { enumerable: true, get: function () { return occ_1.generateOCCSymbolsAroundSpot; } });
|
|
55
|
+
// Implied PDF (probability density function)
|
|
56
|
+
var impliedpdf_1 = require("./impliedpdf");
|
|
57
|
+
Object.defineProperty(exports, "estimateImpliedProbabilityDistribution", { enumerable: true, get: function () { return impliedpdf_1.estimateImpliedProbabilityDistribution; } });
|
|
58
|
+
Object.defineProperty(exports, "estimateImpliedProbabilityDistributions", { enumerable: true, get: function () { return impliedpdf_1.estimateImpliedProbabilityDistributions; } });
|
|
59
|
+
Object.defineProperty(exports, "getProbabilityInRange", { enumerable: true, get: function () { return impliedpdf_1.getProbabilityInRange; } });
|
|
60
|
+
Object.defineProperty(exports, "getCumulativeProbability", { enumerable: true, get: function () { return impliedpdf_1.getCumulativeProbability; } });
|
|
61
|
+
Object.defineProperty(exports, "getQuantile", { enumerable: true, get: function () { return impliedpdf_1.getQuantile; } });
|
|
55
62
|
// Client
|
|
56
63
|
var FloeClient_1 = require("./client/FloeClient");
|
|
57
64
|
Object.defineProperty(exports, "FloeClient", { enumerable: true, get: function () { return FloeClient_1.FloeClient; } });
|
|
@@ -20,7 +20,7 @@ import { OptionChain, IVSurface, VolatilityModel, SmoothingModel } from '../type
|
|
|
20
20
|
*/
|
|
21
21
|
export declare function getIVSurfaces(volatilityModel: VolatilityModel, smoothingModel: SmoothingModel, chain: OptionChain): IVSurface[];
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* Helper function to get the smoothed IV for a specific expiration, option type, and strike combination
|
|
24
24
|
*
|
|
25
25
|
* @param ivSurfaces - Array of IV surfaces
|
|
26
26
|
* @param expiration - Expiration timestamp in milliseconds
|
package/dist/volatility/index.js
CHANGED
|
@@ -134,7 +134,7 @@ function getIVSurfaces(volatilityModel, smoothingModel, chain) {
|
|
|
134
134
|
return ivSurfaces;
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
|
-
*
|
|
137
|
+
* Helper function to get the smoothed IV for a specific expiration, option type, and strike combination
|
|
138
138
|
*
|
|
139
139
|
* @param ivSurfaces - Array of IV surfaces
|
|
140
140
|
* @param expiration - Expiration timestamp in milliseconds
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fullstackcraftllc/floe",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
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",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@types/jest": "^29.5.0",
|
|
43
43
|
"@types/node": "^20.0.0",
|
|
44
|
+
"dotenv": "^17.2.3",
|
|
44
45
|
"jest": "^29.5.0",
|
|
45
46
|
"ts-jest": "^29.1.0",
|
|
46
47
|
"typescript": "^5.9.3"
|