@fullstackcraftllc/floe 0.0.1 → 0.0.3

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 CHANGED
@@ -1,8 +1,14 @@
1
1
  # `floe`
2
2
 
3
- Browser-only TypeScript functions for calculating Black-Scholes, Greeks, and dealer exposures with a clean, type-safe API. Built for use in trading platforms and fintech applications.
3
+ ![npm](https://img.shields.io/npm/v/@fullstackcraftllc/floe?style=flat-square) ![License](https://img.shields.io/npm/l/@fullstackcraftllc/floe?style=flat-square) ![TypeScript](https://img.shields.io/badge/TypeScript-4.9-blue?style=flat-square&logo=typescript)
4
4
 
5
- The same library that is used in Full Stack Craft's various fintech products including [The Wheel Screener](https://wheelscreener.com), [LEAPS Screener](https://leapsscreener.com), [Option Screener](https://option-screener.com), [AMT JOY](https://amtjoy.com), and [VannaCharm](https://vannacharm.com).
5
+ Zero-dependency TypeScript functions for options flow: Black-Scholes, Greeks, and dealer exposures, and more, with a clean, type-safe API. Built for use in trading platforms and fintech applications.
6
+
7
+ The same library that is used in Full Stack Craft's various fintech products including [The Wheel Screener](https://wheelscreener.com), [LEAPS Screener](https://leapsscreener.com), [Option Screener](https://option-screener.com), [AMT JOY](https://amtjoy.com), and [VannaCharm](https://vannacharm.com).
8
+
9
+ ## Quick Start / Documentation / Examples
10
+
11
+ [fullstackcraft.github.io/floe](https://fullstackcraft.github.io/floe)
6
12
 
7
13
  ## 📋 Dual License
8
14
 
@@ -30,171 +36,6 @@ The same library that is used in Full Stack Craft's various fintech products inc
30
36
  npm install @fullstackcraftllc/floe
31
37
  ```
32
38
 
33
- ## Quick Start
34
-
35
- Nearly everything in `floe` revolves around the `NormalizedOption` interface, which provides a consistent way to represent options data regardless of the broker source:
36
-
37
- ```typescript
38
- /**
39
- * Normalized option data structure (broker-agnostic)
40
- */
41
- export interface NormalizedOption {
42
- /** Underlying symbol */
43
- symbol: string;
44
- /** Strike price */
45
- strike: number;
46
- /** Expiration date (ISO 8601) */
47
- expiration: string;
48
- /** Option type */
49
- optionType: OptionType;
50
- /** Current bid price */
51
- bid: number;
52
- /** Current ask price */
53
- ask: number;
54
- /** Last traded price */
55
- last: number;
56
- /** Trading volume */
57
- volume: number;
58
- /** Open interest */
59
- openInterest: number;
60
- /** Implied volatility (as decimal) */
61
- impliedVolatility: number;
62
- /** Pre-calculated Greeks (optional) */
63
- greeks?: Greeks;
64
- }
65
- ```
66
-
67
- ```typescript
68
- import { blackScholes, calculateGreeks, calculateGEX } from '@fullstackcraftllc/floe';
69
-
70
- // Calculate option price
71
- const price = blackScholes({
72
- spot: 100,
73
- strike: 105,
74
- timeToExpiry: 0.25,
75
- volatility: 0.20,
76
- riskFreeRate: 0.05,
77
- optionType: 'call'
78
- });
79
-
80
- // Calculate Greeks
81
- const greeks = calculateGreeks({
82
- spot: 100,
83
- strike: 105,
84
- timeToExpiry: 0.25,
85
- volatility: 0.20,
86
- riskFreeRate: 0.05,
87
- optionType: 'call'
88
- });
89
-
90
- const normalizedOptions = [
91
- {
92
- symbol: 'AAPL',
93
- strike: 105,
94
- expiration: '2025-12-20',
95
- optionType: 'call',
96
- bid: 2.50,
97
- ask: 2.60,
98
- last: 2.55,
99
- volume: 150,
100
- openInterest: 2000,
101
- impliedVolatility: 0.22
102
- },
103
- // ...more options
104
- ];
105
-
106
- const gexData = calculateGEX(normalizedOptions, 100, 100);
107
-
108
- console.log('Option Price:', price);
109
- console.log('Delta:', greeks.delta);
110
- console.log('Gamma:', greeks.gamma);
111
- console.log('Net Gamma:', gexData.netGamma);
112
- console.log('Strike of Max Gamma:', gexData.maxPositiveStrike);
113
- console.log('Strike of Min Gamma:', gexData.maxNegativeStrike);
114
- console.log('Zero Gamma Level:', gexData.zeroGammaLevel);
115
- ```
116
-
117
- ## Broker Normalization
118
-
119
- Floe provides adapters for normalizing options data from multiple brokers:
120
-
121
- ```typescript
122
- import { normalizeTastyworksData } from '@fullstackcraftllc/floe';
123
-
124
- // Tastyworks data
125
- const normalizedOptions = normalizeTastyworksData(rawTastyData);
126
-
127
- // Now both use the same interface
128
- normalizedOptions.forEach(option => {
129
- const greeks = calculateGreeks(option);
130
- console.log(greeks);
131
- });
132
- ```
133
-
134
- ## For Devs - Example / Documentation Site
135
-
136
- The website at [fullstackcraft.github.io/floe](https://fullstackcraft.github.io/floe) contains live code examples and documentation and is here locally at `./site`. It is a static site build with Next.js.
137
-
138
- ## API Documentation
139
-
140
- ### Black-Scholes Pricing
141
-
142
- ```typescript
143
- blackScholes(params: BlackScholesParams): number
144
- ```
145
-
146
- Calculate option price using Black-Scholes model.
147
-
148
- **Parameters:**
149
- - `spot`: Current price of underlying asset
150
- - `strike`: Strike price of option
151
- - `timeToExpiry`: Time to expiration in years
152
- - `volatility`: Implied volatility (annualized)
153
- - `riskFreeRate`: Risk-free interest rate (annualized)
154
- - `optionType`: 'call' | 'put'
155
- - `dividendYield?`: Dividend yield (optional, default 0)
156
-
157
- ### Greeks Calculation
158
-
159
- ```typescript
160
- calculateGreeks(params: BlackScholesParams): Greeks
161
- ```
162
-
163
- Calculate all Greeks for an option.
164
-
165
- **Returns:**
166
- - `delta`: Rate of change of option price with respect to underlying price
167
- - `gamma`: Rate of change of delta with respect to underlying price
168
- - `theta`: Rate of change of option price with respect to time
169
- - `vega`: Rate of change of option price with respect to volatility
170
- - `rho`: Rate of change of option price with respect to interest rate
171
-
172
- ### Estimate Implied Volatility Surface
173
-
174
- ```typescript
175
- estimateIVSurface(options: NormalizedOption[], spot: number): IVSurface
176
- ```
177
-
178
- ### Dealer Exposure Metrics
179
-
180
- ```typescript
181
- calculateGEX(options: NormalizedOption[]): GEXMetrics
182
- calculateVanna(options: NormalizedOption[]): VannaMetrics
183
- calculateCharm(options: NormalizedOption[]): CharmMetrics
184
- ```
185
-
186
- ### Notional Intraday Dealer Exposure Metrics
187
-
188
- This process assume that dealer inventory consists of the previously reported open interest at t=0 (09:30AM EST), and that all options volume is bought or sold at market price based on the NBBO at the time of the trade. This is a simplification and may not reflect actual dealer inventory changes throughout the day.
189
-
190
- Note that the nature of this calculation requires continuous intraday data including time-stamped trades and quotes to accurately model dealer inventory changes.
191
-
192
- ```typescript
193
- calculateIntradayGEX(trades: Trade[], quotes: Quote[], initialOpenInterest: Map<string, number>, spotPrices: Map<string, number>): IntradayGEXMetrics
194
- ```
195
-
196
- Calculate dealer exposure metrics across an options chain.
197
-
198
39
  ## License
199
40
 
200
41
  **Free for Individuals** - Use the MIT License for personal, educational, and non-commercial projects.
@@ -207,45 +48,7 @@ See [LICENSE.md](LICENSE.md) for full details.
207
48
 
208
49
  ## Pricing
209
50
 
210
- ### Individual (MIT License)
211
- **$0/month** - Free forever
212
- - ✅ Personal projects
213
- - ✅ Open-source projects
214
- - ✅ Educational use
215
- - ✅ Community support
216
-
217
- ### Developer (Commercial)
218
- **$149/month** or **$1,490/year**
219
- - ✅ Commercial use
220
- - ✅ Up to 100K calculations/month
221
- - ✅ Email support
222
- - ✅ All broker integrations
223
-
224
- ### Professional (Commercial)
225
- **$499/month** or **$4,990/year**
226
- - ✅ Unlimited calculations
227
- - ✅ Priority support
228
- - ✅ SLA guarantees
229
- - ✅ Custom integrations
230
-
231
- ### Enterprise (Commercial)
232
- **Custom pricing**
233
- - ✅ White-label options
234
- - ✅ Dedicated support
235
- - ✅ On-premise deployment
236
- - ✅ Custom broker adapters
237
-
238
- [Contact hi@fullstackcraft.com for Enterprise pricing](mailto:hi@fullstackcraft.com)
239
-
240
- ## Documentation
241
-
242
- Full documentation coming soon at [fullstackcraft.github.io/floe](https://fullstackcraft.github.io/floe)
243
-
244
- ## Support
245
-
246
- - **Email:** hi@fullstackcraft.com
247
- - **Bug Reports:** [GitHub Issues](https://github.com/FullStackCraft/floe/issues)
248
- - **Discussions:** [GitHub Discussions](https://github.com/FullStackCraft/floe/discussions)
51
+ [Contact hi@fullstackcraft.com for pricing](mailto:hi@fullstackcraft.com)
249
52
 
250
53
  ## Contributing
251
54
 
@@ -253,16 +56,13 @@ Contributions welcome! Please open an issue or PR.
253
56
 
254
57
  By contributing, you agree that your contributions will be licensed under the same dual-license terms.
255
58
 
256
- ## Roadmap
59
+ ## TODOs
257
60
 
258
- - [ ] Homepage / documentation site
259
- - [ ] Volatility surface estimation with a variety of interpolation methods
260
61
  - [ ] Implied PDF calculations
261
62
  - [ ] Tradier integration, normalization, and docs
262
63
  - [ ] TradeStation integration, normalization, and docs
263
64
  - [ ] Interactive Brokers integration, normalization, and docs
264
65
 
265
-
266
66
  ## Credits
267
67
 
268
68
  Built with ❤️ by [Full Stack Craft LLC](https://fullstackcraft.com)
@@ -3,23 +3,44 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.brokerAdapters = exports.tdaAdapter = exports.ibkrAdapter = exports.schwabAdapter = exports.genericAdapter = void 0;
4
4
  exports.getAdapter = getAdapter;
5
5
  exports.createOptionChain = createOptionChain;
6
+ const occ_1 = require("../utils/occ");
7
+ /**
8
+ * Helper to build OCC symbol from raw data
9
+ */
10
+ function buildOCCFromRaw(underlying, expiration, optionType, strike) {
11
+ try {
12
+ return (0, occ_1.buildOCCSymbol)({ symbol: underlying, expiration, optionType, strike });
13
+ }
14
+ catch {
15
+ return '';
16
+ }
17
+ }
6
18
  /**
7
19
  * Generic adapter that maps common field names
8
20
  * This is a fallback adapter when broker-specific adapters are not available
9
21
  */
10
22
  const genericAdapter = (data) => {
23
+ const underlying = String(data.underlying || data.symbol || data.underlyingSymbol || '');
24
+ const expiration = data.expiration || data.expirationDate || '';
25
+ const optionType = (data.optionType || data.putCall || '').toLowerCase() === 'call' ? 'call' : 'put';
26
+ const strike = Number(data.strike || data.strikePrice || 0);
11
27
  return {
12
- strike: Number(data.strike || data.strikePrice || 0),
13
- expiration: data.expiration || data.expirationDate || '',
14
- expirationTimestamp: data.expirationTimestamp || new Date(data.expiration || data.expirationDate).getTime(),
15
- optionType: (data.optionType || data.putCall || '').toLowerCase() === 'call' ? 'call' : 'put',
28
+ occSymbol: data.occSymbol || data.symbol || buildOCCFromRaw(underlying, expiration, optionType, strike),
29
+ underlying,
30
+ strike,
31
+ expiration,
32
+ expirationTimestamp: data.expirationTimestamp || new Date(expiration).getTime(),
33
+ optionType,
16
34
  bid: Number(data.bid || 0),
35
+ bidSize: Number(data.bidSize || data.bidQty || 0),
17
36
  ask: Number(data.ask || 0),
37
+ askSize: Number(data.askSize || data.askQty || 0),
18
38
  mark: Number(data.mark || (data.bid + data.ask) / 2 || 0),
19
39
  last: Number(data.last || data.lastPrice || 0),
20
40
  volume: Number(data.volume || 0),
21
41
  openInterest: Number(data.openInterest || 0),
22
42
  impliedVolatility: Number(data.impliedVolatility || data.iv || 0),
43
+ timestamp: Number(data.timestamp || Date.now()),
23
44
  };
24
45
  };
25
46
  exports.genericAdapter = genericAdapter;
@@ -27,18 +48,27 @@ exports.genericAdapter = genericAdapter;
27
48
  * Schwab-specific adapter for Schwab API responses
28
49
  */
29
50
  const schwabAdapter = (data) => {
51
+ const underlying = String(data.underlying || data.underlyingSymbol || '');
52
+ const expiration = data.expirationDate || '';
53
+ const optionType = (data.putCall || '').toLowerCase() === 'call' ? 'call' : 'put';
54
+ const strike = Number(data.strikePrice || 0);
30
55
  return {
31
- strike: Number(data.strikePrice || 0),
32
- expiration: data.expirationDate || '',
33
- expirationTimestamp: new Date(data.expirationDate).getTime(),
34
- optionType: (data.putCall || '').toLowerCase() === 'call' ? 'call' : 'put',
56
+ occSymbol: data.symbol || buildOCCFromRaw(underlying, expiration, optionType, strike),
57
+ underlying,
58
+ strike,
59
+ expiration,
60
+ expirationTimestamp: new Date(expiration).getTime(),
61
+ optionType,
35
62
  bid: Number(data.bid || 0),
63
+ bidSize: Number(data.bidSize || 0),
36
64
  ask: Number(data.ask || 0),
65
+ askSize: Number(data.askSize || 0),
37
66
  mark: Number(data.mark || 0),
38
67
  last: Number(data.last || 0),
39
68
  volume: Number(data.totalVolume || 0),
40
69
  openInterest: Number(data.openInterest || 0),
41
70
  impliedVolatility: Number(data.volatility || 0),
71
+ timestamp: Number(data.quoteTime || Date.now()),
42
72
  };
43
73
  };
44
74
  exports.schwabAdapter = schwabAdapter;
@@ -46,18 +76,27 @@ exports.schwabAdapter = schwabAdapter;
46
76
  * Interactive Brokers adapter
47
77
  */
48
78
  const ibkrAdapter = (data) => {
79
+ const underlying = String(data.underlying || data.symbol || '');
80
+ const expiration = data.lastTradeDateOrContractMonth || '';
81
+ const optionType = (data.right || '').toLowerCase() === 'c' ? 'call' : 'put';
82
+ const strike = Number(data.strike || 0);
49
83
  return {
50
- strike: Number(data.strike || 0),
51
- expiration: data.lastTradeDateOrContractMonth || '',
52
- expirationTimestamp: new Date(data.lastTradeDateOrContractMonth).getTime(),
53
- optionType: (data.right || '').toLowerCase() === 'c' ? 'call' : 'put',
84
+ occSymbol: data.localSymbol || buildOCCFromRaw(underlying, expiration, optionType, strike),
85
+ underlying,
86
+ strike,
87
+ expiration,
88
+ expirationTimestamp: new Date(expiration).getTime(),
89
+ optionType,
54
90
  bid: Number(data.bid || 0),
91
+ bidSize: Number(data.bidSize || 0),
55
92
  ask: Number(data.ask || 0),
93
+ askSize: Number(data.askSize || 0),
56
94
  mark: Number(data.mark || (data.bid + data.ask) / 2 || 0),
57
95
  last: Number(data.lastTradedPrice || 0),
58
96
  volume: Number(data.volume || 0),
59
97
  openInterest: Number(data.openInterest || 0),
60
98
  impliedVolatility: Number(data.impliedVolatility || 0),
99
+ timestamp: Number(data.time || Date.now()),
61
100
  };
62
101
  };
63
102
  exports.ibkrAdapter = ibkrAdapter;
@@ -65,18 +104,27 @@ exports.ibkrAdapter = ibkrAdapter;
65
104
  * TD Ameritrade / Schwab TDA API adapter
66
105
  */
67
106
  const tdaAdapter = (data) => {
107
+ const underlying = String(data.underlying || data.underlyingSymbol || '');
108
+ const expiration = data.expirationDate || '';
109
+ const optionType = (data.putCall || '').toLowerCase() === 'call' ? 'call' : 'put';
110
+ const strike = Number(data.strikePrice || 0);
68
111
  return {
69
- strike: Number(data.strikePrice || 0),
70
- expiration: data.expirationDate || '',
71
- expirationTimestamp: data.expirationDate ? new Date(data.expirationDate).getTime() : 0,
72
- optionType: (data.putCall || '').toLowerCase() === 'call' ? 'call' : 'put',
112
+ occSymbol: data.symbol || buildOCCFromRaw(underlying, expiration, optionType, strike),
113
+ underlying,
114
+ strike,
115
+ expiration,
116
+ expirationTimestamp: expiration ? new Date(expiration).getTime() : 0,
117
+ optionType,
73
118
  bid: Number(data.bid || 0),
119
+ bidSize: Number(data.bidSize || 0),
74
120
  ask: Number(data.ask || 0),
121
+ askSize: Number(data.askSize || 0),
75
122
  mark: Number(data.mark || 0),
76
123
  last: Number(data.last || 0),
77
124
  volume: Number(data.totalVolume || 0),
78
125
  openInterest: Number(data.openInterest || 0),
79
126
  impliedVolatility: Number(data.volatility || 0),
127
+ timestamp: Number(data.quoteTimeInLong || Date.now()),
80
128
  };
81
129
  };
82
130
  exports.tdaAdapter = tdaAdapter;