market-feed 1.1.0 → 1.1.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/CHANGELOG.md +213 -137
- package/README.md +300 -40
- package/dist/alerts.cjs +8 -4
- package/dist/alerts.cjs.map +1 -1
- package/dist/alerts.d.cts +3 -3
- package/dist/alerts.d.ts +3 -3
- package/dist/alerts.js +8 -4
- package/dist/alerts.js.map +1 -1
- package/dist/backtest.cjs +205 -2
- package/dist/backtest.cjs.map +1 -1
- package/dist/backtest.d.cts +92 -2
- package/dist/backtest.d.ts +92 -2
- package/dist/backtest.js +205 -3
- package/dist/backtest.js.map +1 -1
- package/dist/browser.cjs +3766 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +85 -0
- package/dist/browser.d.ts +85 -0
- package/dist/browser.js +3735 -0
- package/dist/browser.js.map +1 -0
- package/dist/cache.cjs +186 -0
- package/dist/cache.cjs.map +1 -0
- package/dist/cache.d.cts +144 -0
- package/dist/cache.d.ts +144 -0
- package/dist/cache.js +181 -0
- package/dist/cache.js.map +1 -0
- package/dist/cli.js +178 -42
- package/dist/cli.js.map +1 -1
- package/dist/{client-CRK3kKjp.d.cts → client-BWZ-PieL.d.cts} +6 -46
- package/dist/{client-CX5RGcWX.d.ts → client-CtXeYQ1h.d.ts} +6 -46
- package/dist/consensus.d.cts +5 -4
- package/dist/consensus.d.ts +5 -4
- package/dist/fundamentals.cjs.map +1 -1
- package/dist/fundamentals.js.map +1 -1
- package/dist/{historical-BbCuwqyZ.d.cts → historical-C2E1zFA_.d.cts} +1 -1
- package/dist/{historical-BbCuwqyZ.d.ts → historical-C2E1zFA_.d.ts} +1 -1
- package/dist/index.cjs +284 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +132 -20
- package/dist/index.d.ts +132 -20
- package/dist/index.js +283 -46
- package/dist/index.js.map +1 -1
- package/dist/indicators.cjs +6 -2
- package/dist/indicators.cjs.map +1 -1
- package/dist/indicators.d.cts +1 -1
- package/dist/indicators.d.ts +1 -1
- package/dist/indicators.js +6 -2
- package/dist/indicators.js.map +1 -1
- package/dist/macro.cjs +3 -1
- package/dist/macro.cjs.map +1 -1
- package/dist/macro.js +3 -1
- package/dist/macro.js.map +1 -1
- package/dist/memory-CG6fw-mp.d.ts +19 -0
- package/dist/memory-dGCjMfCu.d.cts +19 -0
- package/dist/orderbook-B6vFsskE.d.ts +115 -0
- package/dist/orderbook-Cxp95hhW.d.cts +115 -0
- package/dist/portfolio.cjs.map +1 -1
- package/dist/portfolio.d.cts +1 -1
- package/dist/portfolio.d.ts +1 -1
- package/dist/portfolio.js.map +1 -1
- package/dist/provider-CitIZBZX.d.ts +73 -0
- package/dist/provider-DQGRULh_.d.cts +73 -0
- package/dist/{quote-Cfh_7Cgg.d.cts → quote-BpPnhI4K.d.cts} +1 -1
- package/dist/{quote-Cfh_7Cgg.d.ts → quote-BpPnhI4K.d.ts} +1 -1
- package/dist/react.cjs +1021 -4
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +54 -10
- package/dist/react.d.ts +54 -10
- package/dist/react.js +1020 -5
- package/dist/react.js.map +1 -1
- package/dist/screener.d.cts +1 -1
- package/dist/screener.d.ts +1 -1
- package/dist/{provider-B3PfcWLV.d.ts → splits-Y1u7sJgO.d.cts} +19 -72
- package/dist/{provider-DROKdbpH.d.cts → splits-Y1u7sJgO.d.ts} +19 -72
- package/dist/stream.cjs +30 -0
- package/dist/stream.cjs.map +1 -1
- package/dist/stream.d.cts +9 -7
- package/dist/stream.d.ts +9 -7
- package/dist/stream.js +30 -0
- package/dist/stream.js.map +1 -1
- package/dist/trpc.cjs +97 -0
- package/dist/trpc.cjs.map +1 -0
- package/dist/trpc.d.cts +175 -0
- package/dist/trpc.d.ts +175 -0
- package/dist/trpc.js +94 -0
- package/dist/trpc.js.map +1 -0
- package/dist/{types-hzOO9Cst.d.cts → types-BpKjqHAF.d.cts} +1 -1
- package/dist/{types-C8OPQ-Yj.d.cts → types-BziYJD5M.d.cts} +24 -3
- package/dist/{types-D90J5xwU.d.ts → types-C9q2uBkf.d.ts} +1 -1
- package/dist/{types-B5oHwrPy.d.ts → types-DXjo6Y4W.d.ts} +24 -3
- package/dist/types-Dz0yKXpx.d.cts +43 -0
- package/dist/types-Dz0yKXpx.d.ts +43 -0
- package/dist/ws.cjs +606 -44
- package/dist/ws.cjs.map +1 -1
- package/dist/ws.d.cts +11 -67
- package/dist/ws.d.ts +11 -67
- package/dist/ws.js +606 -45
- package/dist/ws.js.map +1 -1
- package/package.json +38 -22
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# market-feed Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Maintenance: update dev tooling to vitest 4 / vite 6, add @types/node, patch devDep vulnerabilities, fix docs logo and Node 20+ references
|
|
8
|
+
|
|
3
9
|
## 1.1.0 — 2026-03-12
|
|
4
10
|
|
|
5
11
|
### New modules
|
|
@@ -19,7 +25,11 @@ const chain = await getOptionChain(polygon, "AAPL", {
|
|
|
19
25
|
});
|
|
20
26
|
|
|
21
27
|
for (const c of chain.calls) {
|
|
22
|
-
console.log(
|
|
28
|
+
console.log(
|
|
29
|
+
`Strike $${c.strike} IV ${(c.impliedVolatility! * 100).toFixed(
|
|
30
|
+
1
|
|
31
|
+
)}% Δ ${c.delta?.toFixed(3)}`
|
|
32
|
+
);
|
|
23
33
|
}
|
|
24
34
|
```
|
|
25
35
|
|
|
@@ -52,10 +62,10 @@ for (const obs of cpi.observations) {
|
|
|
52
62
|
|
|
53
63
|
Two new criteria in `market-feed/screener`:
|
|
54
64
|
|
|
55
|
-
| Criterion
|
|
56
|
-
|
|
65
|
+
| Criterion | Passes when |
|
|
66
|
+
| --------------------- | ------------------------------------------------------------------------------ |
|
|
57
67
|
| `volume_vs_avg_above` | `quote.volume > quote.avgVolume * value` — e.g. `value: 2` = 2× average volume |
|
|
58
|
-
| `volume_vs_avg_below` | `quote.volume < quote.avgVolume * value`
|
|
68
|
+
| `volume_vs_avg_below` | `quote.volume < quote.avgVolume * value` |
|
|
59
69
|
|
|
60
70
|
Both pass-through when `avgVolume` is `undefined` (consistent with 52-week criteria).
|
|
61
71
|
|
|
@@ -88,24 +98,40 @@ function StockPrice({ symbol }: { symbol: string }) {
|
|
|
88
98
|
const { data, loading, error } = useQuote(feed, symbol);
|
|
89
99
|
if (loading) return <span>…</span>;
|
|
90
100
|
if (error) return <span>Error: {error.message}</span>;
|
|
91
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
<span>
|
|
103
|
+
{symbol}: ${data?.price.toFixed(2)}
|
|
104
|
+
</span>
|
|
105
|
+
);
|
|
92
106
|
}
|
|
93
107
|
|
|
94
108
|
// Subscribe to a live stream
|
|
95
109
|
function LiveFeed() {
|
|
96
110
|
const { event } = useStream(feed, ["AAPL", "MSFT", "GOOGL"]);
|
|
97
111
|
if (!event || event.type !== "quote") return null;
|
|
98
|
-
return
|
|
112
|
+
return (
|
|
113
|
+
<p>
|
|
114
|
+
{event.symbol}: ${event.quote.price}
|
|
115
|
+
</p>
|
|
116
|
+
);
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
// Collect price alerts
|
|
102
120
|
function AlertLog() {
|
|
103
121
|
const { events, clearEvents } = useAlerts(feed, [
|
|
104
|
-
{
|
|
122
|
+
{
|
|
123
|
+
symbol: "AAPL",
|
|
124
|
+
condition: { type: "price_above", threshold: 200 },
|
|
125
|
+
once: false,
|
|
126
|
+
},
|
|
105
127
|
]);
|
|
106
128
|
return (
|
|
107
129
|
<ul>
|
|
108
|
-
{events.map((e, i) =>
|
|
130
|
+
{events.map((e, i) => (
|
|
131
|
+
<li key={i}>
|
|
132
|
+
{e.alert.symbol} triggered @ ${e.quote.price}
|
|
133
|
+
</li>
|
|
134
|
+
))}
|
|
109
135
|
<button onClick={clearEvents}>Clear</button>
|
|
110
136
|
</ul>
|
|
111
137
|
);
|
|
@@ -116,10 +142,10 @@ function AlertLog() {
|
|
|
116
142
|
|
|
117
143
|
**`useQuote(source, symbol, options?)`**
|
|
118
144
|
|
|
119
|
-
| Option
|
|
120
|
-
|
|
121
|
-
| `intervalMs` | `5000`
|
|
122
|
-
| `enabled`
|
|
145
|
+
| Option | Default | Description |
|
|
146
|
+
| ------------ | ------- | --------------------------------- |
|
|
147
|
+
| `intervalMs` | `5000` | Poll interval in milliseconds |
|
|
148
|
+
| `enabled` | `true` | Set to `false` to suspend polling |
|
|
123
149
|
|
|
124
150
|
Returns `{ data: Quote \| null, loading: boolean, error: Error \| null, refetch() }`.
|
|
125
151
|
|
|
@@ -172,25 +198,25 @@ console.log(results.map((r) => `${r.symbol} @ ${r.quote.price}`));
|
|
|
172
198
|
|
|
173
199
|
#### Criterion types
|
|
174
200
|
|
|
175
|
-
| Type
|
|
176
|
-
|
|
177
|
-
| `price_above` / `price_below`
|
|
178
|
-
| `change_pct_above` / `change_pct_below` | Filter by daily % change
|
|
179
|
-
| `volume_above` / `volume_below`
|
|
180
|
-
| `market_cap_above` / `market_cap_below` | Filter by market cap
|
|
181
|
-
| `52w_high_pct_below`
|
|
182
|
-
| `52w_low_pct_above`
|
|
183
|
-
| `custom`
|
|
201
|
+
| Type | Description |
|
|
202
|
+
| --------------------------------------- | ----------------------------------------------------------------- |
|
|
203
|
+
| `price_above` / `price_below` | Filter by current price |
|
|
204
|
+
| `change_pct_above` / `change_pct_below` | Filter by daily % change |
|
|
205
|
+
| `volume_above` / `volume_below` | Filter by trading volume |
|
|
206
|
+
| `market_cap_above` / `market_cap_below` | Filter by market cap |
|
|
207
|
+
| `52w_high_pct_below` | Price is within N% of the 52-week high |
|
|
208
|
+
| `52w_low_pct_above` | Price is at least N% above the 52-week low |
|
|
209
|
+
| `custom` | Arbitrary predicate: `{ type: "custom", fn: (quote) => boolean }` |
|
|
184
210
|
|
|
185
211
|
All criteria are evaluated with **AND logic** — a symbol must pass every criterion to be included.
|
|
186
212
|
|
|
187
213
|
#### Options
|
|
188
214
|
|
|
189
|
-
| Option
|
|
190
|
-
|
|
191
|
-
| `criteria`
|
|
215
|
+
| Option | Description |
|
|
216
|
+
| ----------- | ------------------------------------------------------- |
|
|
217
|
+
| `criteria` | Array of `ScreenerCriterion` (required) |
|
|
192
218
|
| `batchSize` | Max symbols per quote fetch call (default: all at once) |
|
|
193
|
-
| `limit`
|
|
219
|
+
| `limit` | Max number of results to return |
|
|
194
220
|
|
|
195
221
|
#### Result shape
|
|
196
222
|
|
|
@@ -217,45 +243,52 @@ import { getFundamentals } from "market-feed/fundamentals";
|
|
|
217
243
|
import { MarketFeed } from "market-feed";
|
|
218
244
|
|
|
219
245
|
const feed = new MarketFeed();
|
|
220
|
-
const { incomeStatements, balanceSheets, cashFlows } = await getFundamentals(
|
|
246
|
+
const { incomeStatements, balanceSheets, cashFlows } = await getFundamentals(
|
|
247
|
+
feed,
|
|
248
|
+
"AAPL"
|
|
249
|
+
);
|
|
221
250
|
|
|
222
251
|
console.log(`Revenue: $${(incomeStatements[0]!.revenue! / 1e9).toFixed(1)}B`);
|
|
223
|
-
console.log(
|
|
224
|
-
|
|
252
|
+
console.log(
|
|
253
|
+
`Total assets: $${(balanceSheets[0]!.totalAssets! / 1e9).toFixed(1)}B`
|
|
254
|
+
);
|
|
255
|
+
console.log(
|
|
256
|
+
`Free cash flow: $${(cashFlows[0]!.freeCashFlow! / 1e9).toFixed(1)}B`
|
|
257
|
+
);
|
|
225
258
|
```
|
|
226
259
|
|
|
227
260
|
`getFundamentals()` fetches all three statements in parallel via `Promise.allSettled` — a failure on one statement still returns the others.
|
|
228
261
|
|
|
229
262
|
#### `IncomeStatement`
|
|
230
263
|
|
|
231
|
-
| Field
|
|
232
|
-
|
|
233
|
-
| `revenue`
|
|
234
|
-
| `grossProfit`
|
|
235
|
-
| `operatingIncome` | EBIT (operating)
|
|
236
|
-
| `netIncome`
|
|
237
|
-
| `ebitda`
|
|
238
|
-
| `dilutedEps`
|
|
264
|
+
| Field | Description |
|
|
265
|
+
| ----------------- | ------------------------- |
|
|
266
|
+
| `revenue` | Total revenue |
|
|
267
|
+
| `grossProfit` | Revenue - cost of revenue |
|
|
268
|
+
| `operatingIncome` | EBIT (operating) |
|
|
269
|
+
| `netIncome` | Bottom-line net income |
|
|
270
|
+
| `ebitda` | EBITDA when available |
|
|
271
|
+
| `dilutedEps` | Diluted EPS |
|
|
239
272
|
|
|
240
273
|
#### `BalanceSheet`
|
|
241
274
|
|
|
242
|
-
| Field
|
|
243
|
-
|
|
244
|
-
| `totalAssets`
|
|
245
|
-
| `totalLiabilities`
|
|
246
|
-
| `totalStockholdersEquity` | Book value of equity
|
|
247
|
-
| `cashAndCashEquivalents`
|
|
248
|
-
| `totalDebt`
|
|
275
|
+
| Field | Description |
|
|
276
|
+
| ------------------------- | ----------------------- |
|
|
277
|
+
| `totalAssets` | Total assets |
|
|
278
|
+
| `totalLiabilities` | Total liabilities |
|
|
279
|
+
| `totalStockholdersEquity` | Book value of equity |
|
|
280
|
+
| `cashAndCashEquivalents` | Cash + cash equivalents |
|
|
281
|
+
| `totalDebt` | Short + long-term debt |
|
|
249
282
|
|
|
250
283
|
#### `CashFlowStatement`
|
|
251
284
|
|
|
252
|
-
| Field
|
|
253
|
-
|
|
254
|
-
| `operatingCashFlow`
|
|
255
|
-
| `capitalExpenditures` | CapEx (negative = outflow)
|
|
256
|
-
| `freeCashFlow`
|
|
257
|
-
| `investingCashFlow`
|
|
258
|
-
| `financingCashFlow`
|
|
285
|
+
| Field | Description |
|
|
286
|
+
| --------------------- | --------------------------------------- |
|
|
287
|
+
| `operatingCashFlow` | Cash from operations |
|
|
288
|
+
| `capitalExpenditures` | CapEx (negative = outflow) |
|
|
289
|
+
| `freeCashFlow` | operatingCashFlow + capitalExpenditures |
|
|
290
|
+
| `investingCashFlow` | Cash from investing |
|
|
291
|
+
| `financingCashFlow` | Cash from financing |
|
|
259
292
|
|
|
260
293
|
All three types include `symbol`, `date` (period end), `periodType` (`"annual"` | `"quarterly"`), and `provider`.
|
|
261
294
|
|
|
@@ -264,20 +297,20 @@ All three types include `symbol`, `date` (period end), `periodType` (`"annual"`
|
|
|
264
297
|
```ts
|
|
265
298
|
const feed = new MarketFeed();
|
|
266
299
|
|
|
267
|
-
const income
|
|
300
|
+
const income = await feed.incomeStatements("AAPL", { limit: 4 });
|
|
268
301
|
const balance = await feed.balanceSheets("AAPL", { quarterly: true });
|
|
269
|
-
const cash
|
|
302
|
+
const cash = await feed.cashFlows("AAPL");
|
|
270
303
|
```
|
|
271
304
|
|
|
272
305
|
`FundamentalsOptions`: `quarterly?` (default `false`), `limit?` (default `4`), `raw?`.
|
|
273
306
|
|
|
274
307
|
### Provider support
|
|
275
308
|
|
|
276
|
-
| Method
|
|
277
|
-
|
|
278
|
-
| `incomeStatements` | ✓ `incomeStatementHistory` / `incomeStatementHistoryQuarterly`
|
|
279
|
-
| `balanceSheets`
|
|
280
|
-
| `cashFlows`
|
|
309
|
+
| Method | `YahooProvider` | others |
|
|
310
|
+
| ------------------ | ------------------------------------------------------------------ | ------ |
|
|
311
|
+
| `incomeStatements` | ✓ `incomeStatementHistory` / `incomeStatementHistoryQuarterly` | — |
|
|
312
|
+
| `balanceSheets` | ✓ `balanceSheetHistory` / `balanceSheetHistoryQuarterly` | — |
|
|
313
|
+
| `cashFlows` | ✓ `cashflowStatementHistory` / `cashflowStatementHistoryQuarterly` | — |
|
|
281
314
|
|
|
282
315
|
### Breaking changes
|
|
283
316
|
|
|
@@ -305,21 +338,21 @@ const feed = new MarketFeed([
|
|
|
305
338
|
new TiingoProvider({ apiKey: process.env.TIINGO_KEY! }),
|
|
306
339
|
]);
|
|
307
340
|
|
|
308
|
-
const quote
|
|
309
|
-
const bars
|
|
341
|
+
const quote = await feed.quote(["AAPL"]);
|
|
342
|
+
const bars = await feed.historical("AAPL", { period1: "2024-01-01" });
|
|
310
343
|
const results = await feed.search("apple");
|
|
311
344
|
const profile = await feed.company("AAPL");
|
|
312
|
-
const news
|
|
345
|
+
const news = await feed.news("AAPL", { limit: 5 });
|
|
313
346
|
```
|
|
314
347
|
|
|
315
348
|
Supports: `quote`, `historical`, `search`, `company`, `news`.
|
|
316
349
|
|
|
317
|
-
| Feature
|
|
318
|
-
|
|
350
|
+
| Feature | Detail |
|
|
351
|
+
| ---------------- | ------------------------------------ |
|
|
319
352
|
| Real-time quotes | IEX endpoint — US equities, intraday |
|
|
320
|
-
| Historical
|
|
321
|
-
| Rate limit
|
|
322
|
-
| Auth
|
|
353
|
+
| Historical | EOD daily bars, includes `adjClose` |
|
|
354
|
+
| Rate limit | ~50 calls/hour (free plan) |
|
|
355
|
+
| Auth | `Authorization: Token KEY` header |
|
|
323
356
|
|
|
324
357
|
Free plan sign-up: https://www.tiingo.com
|
|
325
358
|
|
|
@@ -351,8 +384,8 @@ const feed = new MarketFeed([
|
|
|
351
384
|
new TwelveDataProvider({ apiKey: process.env.TWELVE_DATA_KEY! }),
|
|
352
385
|
]);
|
|
353
386
|
|
|
354
|
-
const quote
|
|
355
|
-
const bars
|
|
387
|
+
const quote = await feed.quote(["AAPL", "BTC/USD", "EUR/USD"]);
|
|
388
|
+
const bars = await feed.historical("AAPL", { interval: "1wk" });
|
|
356
389
|
const results = await feed.search("apple");
|
|
357
390
|
const profile = await feed.company("AAPL");
|
|
358
391
|
```
|
|
@@ -360,6 +393,7 @@ const profile = await feed.company("AAPL");
|
|
|
360
393
|
Supports: `quote`, `historical`, `search`, `company`.
|
|
361
394
|
|
|
362
395
|
Strong coverage for global equities, forex, and crypto pairs. Symbol normalisation handles all common formats:
|
|
396
|
+
|
|
363
397
|
- US stocks: `AAPL` (unchanged)
|
|
364
398
|
- Crypto: `BTC-USD` / `BTC/USD` / `X:BTCUSD` → `BTC/USD`
|
|
365
399
|
- Forex: `EURUSD=X` / `C:EURUSD` → `EUR/USD`
|
|
@@ -369,15 +403,15 @@ Free plan sign-up: https://twelvedata.com
|
|
|
369
403
|
#### Interval mapping
|
|
370
404
|
|
|
371
405
|
| market-feed | Twelve Data |
|
|
372
|
-
|
|
373
|
-
| `1m`
|
|
374
|
-
| `5m`
|
|
375
|
-
| `15m`
|
|
376
|
-
| `30m`
|
|
377
|
-
| `1h`
|
|
378
|
-
| `1d`
|
|
379
|
-
| `1wk`
|
|
380
|
-
| `1mo`
|
|
406
|
+
| ----------- | ----------- |
|
|
407
|
+
| `1m` | `1min` |
|
|
408
|
+
| `5m` | `5min` |
|
|
409
|
+
| `15m` | `15min` |
|
|
410
|
+
| `30m` | `30min` |
|
|
411
|
+
| `1h` | `1h` |
|
|
412
|
+
| `1d` | `1day` |
|
|
413
|
+
| `1wk` | `1week` |
|
|
414
|
+
| `1mo` | `1month` |
|
|
381
415
|
|
|
382
416
|
### New utility
|
|
383
417
|
|
|
@@ -429,8 +463,10 @@ None.
|
|
|
429
463
|
import { backtest } from "market-feed/backtest";
|
|
430
464
|
import type { EntrySignal, ExitSignal } from "market-feed/backtest";
|
|
431
465
|
|
|
432
|
-
const entry: EntrySignal = (bars, i) =>
|
|
433
|
-
|
|
466
|
+
const entry: EntrySignal = (bars, i) =>
|
|
467
|
+
i > 0 && bars[i]!.close > bars[i - 1]!.close;
|
|
468
|
+
const exit: ExitSignal = (bars, i) =>
|
|
469
|
+
i > 0 && bars[i]!.close < bars[i - 1]!.close;
|
|
434
470
|
|
|
435
471
|
const result = backtest("AAPL", bars, entry, exit, { initialCapital: 10_000 });
|
|
436
472
|
console.log(`Total return: ${(result.totalReturn * 100).toFixed(2)}%`);
|
|
@@ -438,16 +474,16 @@ console.log(`Sharpe ratio: ${result.sharpeRatio.toFixed(2)}`);
|
|
|
438
474
|
console.log(`Max drawdown: ${(result.maxDrawdown * 100).toFixed(2)}%`);
|
|
439
475
|
```
|
|
440
476
|
|
|
441
|
-
| Field
|
|
442
|
-
|
|
443
|
-
| `totalReturn`
|
|
444
|
-
| `annualizedReturn` | CAGR as a fraction
|
|
445
|
-
| `sharpeRatio`
|
|
446
|
-
| `maxDrawdown`
|
|
447
|
-
| `winRate`
|
|
448
|
-
| `profitFactor`
|
|
449
|
-
| `totalTrades`
|
|
450
|
-
| `trades`
|
|
477
|
+
| Field | Description |
|
|
478
|
+
| ------------------ | ----------------------------------------------------- |
|
|
479
|
+
| `totalReturn` | Fraction, e.g. 0.25 = 25% |
|
|
480
|
+
| `annualizedReturn` | CAGR as a fraction |
|
|
481
|
+
| `sharpeRatio` | Annualised Sharpe (risk-free rate = 0) |
|
|
482
|
+
| `maxDrawdown` | Peak-to-trough as a positive fraction |
|
|
483
|
+
| `winRate` | Fraction of profitable trades |
|
|
484
|
+
| `profitFactor` | Gross profit / gross loss (`Infinity` when no losses) |
|
|
485
|
+
| `totalTrades` | Number of completed round-trip trades |
|
|
486
|
+
| `trades` | Full `BacktestTrade[]` ledger |
|
|
451
487
|
|
|
452
488
|
**`market-feed/alerts`** — Poll a feed and yield `AlertEvent` when conditions are met.
|
|
453
489
|
|
|
@@ -458,21 +494,33 @@ import { MarketFeed } from "market-feed";
|
|
|
458
494
|
const feed = new MarketFeed();
|
|
459
495
|
const controller = new AbortController();
|
|
460
496
|
|
|
461
|
-
for await (const event of watchAlerts(
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
497
|
+
for await (const event of watchAlerts(
|
|
498
|
+
feed,
|
|
499
|
+
[
|
|
500
|
+
{
|
|
501
|
+
symbol: "AAPL",
|
|
502
|
+
condition: { type: "price_above", threshold: 200 },
|
|
503
|
+
once: true,
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
symbol: "TSLA",
|
|
507
|
+
condition: { type: "change_pct_below", threshold: -5 },
|
|
508
|
+
debounceMs: 60_000,
|
|
509
|
+
},
|
|
510
|
+
],
|
|
511
|
+
{ signal: controller.signal }
|
|
512
|
+
)) {
|
|
465
513
|
console.log(`${event.alert.symbol} triggered: $${event.quote.price}`);
|
|
466
514
|
}
|
|
467
515
|
```
|
|
468
516
|
|
|
469
|
-
| Condition type
|
|
470
|
-
|
|
471
|
-
| `price_above`
|
|
472
|
-
| `price_below`
|
|
473
|
-
| `change_pct_above` | Daily `%` change exceeds threshold
|
|
517
|
+
| Condition type | Description |
|
|
518
|
+
| ------------------ | -------------------------------------- |
|
|
519
|
+
| `price_above` | `quote.price > threshold` |
|
|
520
|
+
| `price_below` | `quote.price < threshold` |
|
|
521
|
+
| `change_pct_above` | Daily `%` change exceeds threshold |
|
|
474
522
|
| `change_pct_below` | Daily `%` change falls below threshold |
|
|
475
|
-
| `volume_above`
|
|
523
|
+
| `volume_above` | `quote.volume > threshold` |
|
|
476
524
|
|
|
477
525
|
`AlertConfig` options: `once` (fire at most once), `debounceMs` (suppress re-fires within window).
|
|
478
526
|
|
|
@@ -483,27 +531,33 @@ Three new methods on `MarketFeed` (and on individual providers):
|
|
|
483
531
|
```ts
|
|
484
532
|
const feed = new MarketFeed([new PolygonProvider({ apiKey: "..." })]);
|
|
485
533
|
|
|
486
|
-
const earnings
|
|
534
|
+
const earnings = await feed.earnings("AAPL", { limit: 8 });
|
|
487
535
|
const dividends = await feed.dividends("AAPL");
|
|
488
|
-
const splits
|
|
536
|
+
const splits = await feed.splits("AAPL");
|
|
489
537
|
```
|
|
490
538
|
|
|
491
539
|
#### Provider support
|
|
492
540
|
|
|
493
|
-
| Method
|
|
494
|
-
|
|
495
|
-
| `earnings`
|
|
496
|
-
| `dividends` | ✓ chart `events=div`
|
|
497
|
-
| `splits`
|
|
541
|
+
| Method | `YahooProvider` | `PolygonProvider` | `FinnhubProvider` | `AlphaVantageProvider` |
|
|
542
|
+
| ----------- | -------------------------------- | --------------------------- | ------------------- | ---------------------- |
|
|
543
|
+
| `earnings` | ✓ quoteSummary `earningsHistory` | — | ✓ `/stock/earnings` | — |
|
|
544
|
+
| `dividends` | ✓ chart `events=div` | ✓ `/v3/reference/dividends` | — | — |
|
|
545
|
+
| `splits` | ✓ chart `events=split` | ✓ `/v3/reference/splits` | — | — |
|
|
498
546
|
|
|
499
547
|
#### `EarningsEvent`
|
|
500
548
|
|
|
501
549
|
```ts
|
|
502
550
|
interface EarningsEvent {
|
|
503
|
-
symbol: string;
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
551
|
+
symbol: string;
|
|
552
|
+
date: Date;
|
|
553
|
+
period?: string;
|
|
554
|
+
epsActual?: number;
|
|
555
|
+
epsEstimate?: number;
|
|
556
|
+
epsSurprisePct?: number;
|
|
557
|
+
revenueActual?: number;
|
|
558
|
+
revenueEstimate?: number;
|
|
559
|
+
provider: string;
|
|
560
|
+
raw?: unknown;
|
|
507
561
|
}
|
|
508
562
|
```
|
|
509
563
|
|
|
@@ -511,10 +565,15 @@ interface EarningsEvent {
|
|
|
511
565
|
|
|
512
566
|
```ts
|
|
513
567
|
interface DividendEvent {
|
|
514
|
-
symbol: string;
|
|
515
|
-
|
|
568
|
+
symbol: string;
|
|
569
|
+
exDate: Date;
|
|
570
|
+
payDate?: Date;
|
|
571
|
+
declaredDate?: Date;
|
|
572
|
+
amount: number;
|
|
573
|
+
currency: string;
|
|
516
574
|
frequency?: "annual" | "semi-annual" | "quarterly" | "monthly" | "irregular";
|
|
517
|
-
provider: string;
|
|
575
|
+
provider: string;
|
|
576
|
+
raw?: unknown;
|
|
518
577
|
}
|
|
519
578
|
```
|
|
520
579
|
|
|
@@ -522,10 +581,12 @@ interface DividendEvent {
|
|
|
522
581
|
|
|
523
582
|
```ts
|
|
524
583
|
interface SplitEvent {
|
|
525
|
-
symbol: string;
|
|
526
|
-
|
|
584
|
+
symbol: string;
|
|
585
|
+
date: Date;
|
|
586
|
+
ratio: number; // 4-for-1 forward split → 4; 1-for-10 reverse → 0.1
|
|
527
587
|
description?: string;
|
|
528
|
-
provider: string;
|
|
588
|
+
provider: string;
|
|
589
|
+
raw?: unknown;
|
|
529
590
|
}
|
|
530
591
|
```
|
|
531
592
|
|
|
@@ -558,10 +619,14 @@ import { FinnhubProvider } from "market-feed";
|
|
|
558
619
|
const provider = new FinnhubProvider({ apiKey: process.env.FINNHUB_KEY! });
|
|
559
620
|
const controller = new AbortController();
|
|
560
621
|
|
|
561
|
-
for await (const event of connect(provider, ["AAPL", "MSFT"], {
|
|
622
|
+
for await (const event of connect(provider, ["AAPL", "MSFT"], {
|
|
623
|
+
signal: controller.signal,
|
|
624
|
+
})) {
|
|
562
625
|
switch (event.type) {
|
|
563
626
|
case "trade":
|
|
564
|
-
console.log(
|
|
627
|
+
console.log(
|
|
628
|
+
`${event.trade.symbol}: $${event.trade.price} × ${event.trade.size}`
|
|
629
|
+
);
|
|
565
630
|
break;
|
|
566
631
|
case "connected":
|
|
567
632
|
console.log(`Connected to ${event.provider}`);
|
|
@@ -578,23 +643,23 @@ for await (const event of connect(provider, ["AAPL", "MSFT"], { signal: controll
|
|
|
578
643
|
|
|
579
644
|
#### Provider support
|
|
580
645
|
|
|
581
|
-
| Provider
|
|
582
|
-
|
|
583
|
-
| **PolygonProvider**
|
|
584
|
-
| **FinnhubProvider**
|
|
585
|
-
| **YahooProvider**
|
|
586
|
-
| **AlphaVantageProvider** | Polling fallback | Same as Yahoo
|
|
646
|
+
| Provider | WebSocket | Notes |
|
|
647
|
+
| ------------------------ | ---------------- | --------------------------------------------------------------------------------------------- |
|
|
648
|
+
| **PolygonProvider** | Native WS | `wss://socket.polygon.io/stocks` — auth via JSON handshake, subscribes to `T.*` trade channel |
|
|
649
|
+
| **FinnhubProvider** | Native WS | `wss://ws.finnhub.io?token=KEY` — per-symbol subscribe, batched trade messages |
|
|
650
|
+
| **YahooProvider** | Polling fallback | Polls `provider.quote()` every 5 s; emits `WsTrade` from quote data |
|
|
651
|
+
| **AlphaVantageProvider** | Polling fallback | Same as Yahoo |
|
|
587
652
|
|
|
588
653
|
The `connect()` function detects provider capability automatically — no configuration required.
|
|
589
654
|
|
|
590
655
|
#### `WsEvent` union
|
|
591
656
|
|
|
592
|
-
| `type`
|
|
593
|
-
|
|
594
|
-
| `"connected"`
|
|
595
|
-
| `"trade"`
|
|
596
|
-
| `"disconnected"` | `provider`, `reconnecting`, `attempt` | WS closed unexpectedly
|
|
597
|
-
| `"error"`
|
|
657
|
+
| `type` | Payload | When |
|
|
658
|
+
| ---------------- | ------------------------------------- | ------------------------------------ |
|
|
659
|
+
| `"connected"` | `provider: string` | WS opened (and after each reconnect) |
|
|
660
|
+
| `"trade"` | `trade: WsTrade` | Each trade tick |
|
|
661
|
+
| `"disconnected"` | `provider`, `reconnecting`, `attempt` | WS closed unexpectedly |
|
|
662
|
+
| `"error"` | `error`, `recoverable` | Protocol or network error |
|
|
598
663
|
|
|
599
664
|
#### `WsTrade`
|
|
600
665
|
|
|
@@ -602,7 +667,7 @@ The `connect()` function detects provider capability automatically — no config
|
|
|
602
667
|
interface WsTrade {
|
|
603
668
|
symbol: string;
|
|
604
669
|
price: number;
|
|
605
|
-
size: number;
|
|
670
|
+
size: number; // shares / units
|
|
606
671
|
timestamp: Date;
|
|
607
672
|
conditions?: number[]; // provider-specific condition codes
|
|
608
673
|
}
|
|
@@ -610,12 +675,12 @@ interface WsTrade {
|
|
|
610
675
|
|
|
611
676
|
#### `WsOptions`
|
|
612
677
|
|
|
613
|
-
| Option
|
|
614
|
-
|
|
615
|
-
| `wsImpl`
|
|
616
|
-
| `maxReconnectAttempts` | `number`
|
|
617
|
-
| `reconnectDelayMs`
|
|
618
|
-
| `signal`
|
|
678
|
+
| Option | Type | Default | Description |
|
|
679
|
+
| ---------------------- | ----------------------------- | ---------------------- | ------------------------------------------ |
|
|
680
|
+
| `wsImpl` | `typeof globalThis.WebSocket` | `globalThis.WebSocket` | Custom WS constructor for Node 18–20 |
|
|
681
|
+
| `maxReconnectAttempts` | `number` | `10` | Reconnect attempts before closing |
|
|
682
|
+
| `reconnectDelayMs` | `number` | `1000` | Base delay (doubles per attempt, max 30 s) |
|
|
683
|
+
| `signal` | `AbortSignal` | — | Stop the stream |
|
|
619
684
|
|
|
620
685
|
#### Node 18–20 compatibility
|
|
621
686
|
|
|
@@ -623,7 +688,9 @@ Node 21+ exposes `WebSocket` globally. For Node 18/20, install the `ws` package
|
|
|
623
688
|
|
|
624
689
|
```ts
|
|
625
690
|
import WebSocket from "ws";
|
|
626
|
-
connect(provider, ["AAPL"], {
|
|
691
|
+
connect(provider, ["AAPL"], {
|
|
692
|
+
wsImpl: WebSocket as unknown as typeof globalThis.WebSocket,
|
|
693
|
+
});
|
|
627
694
|
```
|
|
628
695
|
|
|
629
696
|
### Other changes
|
|
@@ -644,6 +711,7 @@ None. All v0.3.0 imports continue to work unchanged.
|
|
|
644
711
|
### New provider
|
|
645
712
|
|
|
646
713
|
**`FinnhubProvider`** — Finnhub.io (free tier: real-time US stock data, 60 calls/minute).
|
|
714
|
+
|
|
647
715
|
- `quote`, `historical` (candles), `search`, `company`, `news`
|
|
648
716
|
- API key required — get one free at https://finnhub.io
|
|
649
717
|
- Uses `X-Finnhub-Token` header; rate-limited client-side at 60 req/min
|
|
@@ -653,6 +721,7 @@ None. All v0.3.0 imports continue to work unchanged.
|
|
|
653
721
|
### New modules
|
|
654
722
|
|
|
655
723
|
**`market-feed/indicators`** — Technical indicators as pure functions over `HistoricalBar[]`.
|
|
724
|
+
|
|
656
725
|
- `sma(bars, period)` — Simple Moving Average (O(1) sliding window)
|
|
657
726
|
- `ema(bars, period)` — Exponential Moving Average (k = 2/(period+1), SMA-seeded)
|
|
658
727
|
- `rsi(bars, period?)` — Relative Strength Index via Wilder's smoothing (default period: 14)
|
|
@@ -665,6 +734,7 @@ None. All v0.3.0 imports continue to work unchanged.
|
|
|
665
734
|
- All functions return typed result arrays (`IndicatorPoint[]`, `MACDPoint[]`, `BollingerPoint[]`, `StochasticPoint[]`)
|
|
666
735
|
|
|
667
736
|
**`market-feed/portfolio`** — Track positions and compute live P&L.
|
|
737
|
+
|
|
668
738
|
- `new Portfolio(positions?)` — construct with an array of `Position` objects
|
|
669
739
|
- `portfolio.add(position)` / `portfolio.remove(symbol)` — mutable, chainable
|
|
670
740
|
- `portfolio.snapshot(feed)` — fetches live quotes and returns `PortfolioSnapshot` with per-position and aggregate P&L
|
|
@@ -717,6 +787,7 @@ None. All v0.2.0 imports continue to work unchanged.
|
|
|
717
787
|
### New modules
|
|
718
788
|
|
|
719
789
|
**`market-feed/calendar`** — Synchronous exchange calendar. No network required.
|
|
790
|
+
|
|
720
791
|
- `isMarketOpen(exchange, at?)` — boolean, DST-correct via `Intl`
|
|
721
792
|
- `getSession(exchange, at?)` — `"pre" | "regular" | "post" | "closed"`
|
|
722
793
|
- `nextSessionOpen(exchange, from?)` / `nextSessionClose(exchange, from?)` — next UTC Date
|
|
@@ -728,6 +799,7 @@ None. All v0.2.0 imports continue to work unchanged.
|
|
|
728
799
|
- Early-close days (NYSE: day before Thanksgiving, Independence Day, Christmas Eve)
|
|
729
800
|
|
|
730
801
|
**`market-feed/stream`** — Market-hours-aware observable quote stream.
|
|
802
|
+
|
|
731
803
|
- `watch(feed, symbols, options)` — async generator yielding typed `StreamEvent` union
|
|
732
804
|
- Polls at `interval.open` (default 5s) during regular hours, `interval.prepost` (default 30s) pre/post, pauses at `interval.closed` (default 60s) when closed — saves API quota overnight and on weekends
|
|
733
805
|
- Emits `market-open` / `market-close` events at session transitions
|
|
@@ -736,6 +808,7 @@ None. All v0.2.0 imports continue to work unchanged.
|
|
|
736
808
|
- Configurable `maxErrors` before the generator throws
|
|
737
809
|
|
|
738
810
|
**`market-feed/consensus`** — Multi-provider parallel price consensus.
|
|
811
|
+
|
|
739
812
|
- `consensus(providers, symbol, options)` — queries all providers simultaneously via `Promise.allSettled`
|
|
740
813
|
- Median-based outlier detection (avoids the all-outlier edge case of mean-based approaches)
|
|
741
814
|
- Staleness detection: providers with quotes older than `stalenessThreshold` receive half weight
|
|
@@ -760,11 +833,13 @@ None. All v0.1.0 imports continue to work unchanged.
|
|
|
760
833
|
### Initial release
|
|
761
834
|
|
|
762
835
|
**Providers**
|
|
836
|
+
|
|
763
837
|
- `YahooProvider` — Yahoo Finance (no API key required): quote, historical, search, company
|
|
764
838
|
- `AlphaVantageProvider` — Alpha Vantage (free: 25/day): quote, historical, search, company
|
|
765
839
|
- `PolygonProvider` — Polygon.io (free: 15-min delayed): quote, historical, search, company, news
|
|
766
840
|
|
|
767
841
|
**Core**
|
|
842
|
+
|
|
768
843
|
- Unified `Quote`, `HistoricalBar`, `CompanyProfile`, `NewsItem`, `SearchResult`, `MarketStatus` types
|
|
769
844
|
- `MarketFeed` client with provider chain, automatic fallback, and LRU caching
|
|
770
845
|
- `MemoryCacheDriver` — zero-dependency LRU cache with TTL and configurable max size
|
|
@@ -775,6 +850,7 @@ None. All v0.1.0 imports continue to work unchanged.
|
|
|
775
850
|
- Error hierarchy: `MarketFeedError`, `ProviderError`, `RateLimitError`, `UnsupportedOperationError`, `AllProvidersFailedError`
|
|
776
851
|
|
|
777
852
|
**DX**
|
|
853
|
+
|
|
778
854
|
- Zero production dependencies (native `fetch` only)
|
|
779
855
|
- Strict TypeScript — `strict: true`, `noUncheckedIndexedAccess`, `noImplicitOverride`
|
|
780
856
|
- ESM + CJS + `.d.ts` dual build
|