market-feed 1.0.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 +280 -133
- package/README.md +519 -39
- 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 +469 -34
- package/dist/cli.js.map +1 -1
- package/dist/{client-ADDEqqP4.d.cts → client-BWZ-PieL.d.cts} +8 -46
- package/dist/{client-B_oIoAtz.d.ts → client-CtXeYQ1h.d.ts} +8 -46
- package/dist/consensus.d.cts +5 -3
- package/dist/consensus.d.ts +5 -3
- 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 +569 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +142 -20
- package/dist/index.d.ts +142 -20
- package/dist/index.js +568 -32
- 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 +235 -0
- package/dist/macro.cjs.map +1 -0
- package/dist/macro.d.cts +126 -0
- package/dist/macro.d.ts +126 -0
- package/dist/macro.js +231 -0
- package/dist/macro.js.map +1 -0
- package/dist/memory-CG6fw-mp.d.ts +19 -0
- package/dist/memory-dGCjMfCu.d.cts +19 -0
- package/dist/options-CyRqRiFk.d.cts +59 -0
- package/dist/options-CyRqRiFk.d.ts +59 -0
- package/dist/options.cjs +13 -0
- package/dist/options.cjs.map +1 -0
- package/dist/options.d.cts +45 -0
- package/dist/options.d.ts +45 -0
- package/dist/options.js +11 -0
- package/dist/options.js.map +1 -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 -9
- package/dist/react.d.ts +54 -9
- package/dist/react.js +1020 -5
- package/dist/react.js.map +1 -1
- package/dist/screener.cjs +6 -0
- package/dist/screener.cjs.map +1 -1
- package/dist/screener.d.cts +7 -1
- package/dist/screener.d.ts +7 -1
- package/dist/screener.js +6 -0
- package/dist/screener.js.map +1 -1
- package/dist/{provider-CnWUOTf9.d.ts → splits-Y1u7sJgO.d.cts} +19 -67
- package/dist/{provider-DC4ydTdu.d.cts → splits-Y1u7sJgO.d.ts} +19 -67
- package/dist/stream.cjs +30 -0
- package/dist/stream.cjs.map +1 -1
- package/dist/stream.d.cts +9 -6
- package/dist/stream.d.ts +9 -6
- 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 -66
- package/dist/ws.d.ts +11 -66
- package/dist/ws.js +606 -45
- package/dist/ws.js.map +1 -1
- package/package.json +44 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
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
|
+
|
|
9
|
+
## 1.1.0 — 2026-03-12
|
|
10
|
+
|
|
11
|
+
### New modules
|
|
12
|
+
|
|
13
|
+
**`market-feed/options`** — Options chains with full Greeks from Polygon.io.
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { getOptionChain } from "market-feed/options";
|
|
17
|
+
import { PolygonProvider } from "market-feed";
|
|
18
|
+
|
|
19
|
+
const polygon = new PolygonProvider({ apiKey: process.env.POLYGON_KEY! });
|
|
20
|
+
const chain = await getOptionChain(polygon, "AAPL", {
|
|
21
|
+
expiry: "2024-07-19",
|
|
22
|
+
type: "call",
|
|
23
|
+
strikeLow: 170,
|
|
24
|
+
strikeHigh: 210,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
for (const c of chain.calls) {
|
|
28
|
+
console.log(
|
|
29
|
+
`Strike $${c.strike} IV ${(c.impliedVolatility! * 100).toFixed(
|
|
30
|
+
1
|
|
31
|
+
)}% Δ ${c.delta?.toFixed(3)}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Fields: `ticker`, `type`, `strike`, `expiry`, `style`, `sharesPerContract`, `bid`, `ask`, `midpoint`, `lastPrice`, `volume`, `openInterest`, `impliedVolatility`, `delta`, `gamma`, `theta`, `vega`, OHLCV.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
**`market-feed/macro`** — Macroeconomic time-series data from the [FRED API](https://fred.stlouisfed.org/).
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
import { FredProvider, getIndicator, INDICATORS } from "market-feed/macro";
|
|
44
|
+
|
|
45
|
+
const fred = new FredProvider({ apiKey: process.env.FRED_KEY! });
|
|
46
|
+
const cpi = await getIndicator(fred, INDICATORS.CPI, { limit: 12 });
|
|
47
|
+
|
|
48
|
+
for (const obs of cpi.observations) {
|
|
49
|
+
console.log(`${obs.date.toISOString().slice(0, 7)}: ${obs.value}`);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
15 named constants in `INDICATORS`: `CPI`, `FED_FUNDS`, `UNEMPLOYMENT`, `GDP`, `M2`, `T10Y`, `T2Y`, `MORTGAGE_30Y`, `PCE`, `PPI`, `INDUSTRIAL_PRODUCTION`, `RETAIL_SALES`, `OIL_WTI`, `HOUSING_STARTS`, `CONSUMER_SENTIMENT`. Any FRED series ID string also accepted.
|
|
54
|
+
|
|
55
|
+
### Provider enhancements
|
|
56
|
+
|
|
57
|
+
**Polygon.io** — added `incomeStatements()`, `balanceSheets()`, `cashFlows()` via `/vX/reference/financials`.
|
|
58
|
+
|
|
59
|
+
**Tiingo** — added `incomeStatements()`, `balanceSheets()`, `cashFlows()` via `/tiingo/fundamentals/{ticker}/statements`.
|
|
60
|
+
|
|
61
|
+
### Screener additions
|
|
62
|
+
|
|
63
|
+
Two new criteria in `market-feed/screener`:
|
|
64
|
+
|
|
65
|
+
| Criterion | Passes when |
|
|
66
|
+
| --------------------- | ------------------------------------------------------------------------------ |
|
|
67
|
+
| `volume_vs_avg_above` | `quote.volume > quote.avgVolume * value` — e.g. `value: 2` = 2× average volume |
|
|
68
|
+
| `volume_vs_avg_below` | `quote.volume < quote.avgVolume * value` |
|
|
69
|
+
|
|
70
|
+
Both pass-through when `avgVolume` is `undefined` (consistent with 52-week criteria).
|
|
71
|
+
|
|
72
|
+
### Client
|
|
73
|
+
|
|
74
|
+
- `MarketFeed.optionChain(symbol, options?)` — delegates to first provider supporting `optionChain`. Cache TTL: 60s.
|
|
75
|
+
- `CacheMethod` union extended with `"optionChain"`.
|
|
76
|
+
|
|
77
|
+
### Package
|
|
78
|
+
|
|
79
|
+
- `market-feed/options` and `market-feed/macro` added to `exports` and `keywords`.
|
|
80
|
+
- Total subpath modules: **14** (`market-feed`, `/calendar`, `/stream`, `/ws`, `/consensus`, `/indicators`, `/portfolio`, `/backtest`, `/alerts`, `/fundamentals`, `/screener`, `/options`, `/macro`, `/react`).
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
3
84
|
## 1.0.0 — 2026-03-12
|
|
4
85
|
|
|
5
86
|
### New module
|
|
@@ -17,24 +98,40 @@ function StockPrice({ symbol }: { symbol: string }) {
|
|
|
17
98
|
const { data, loading, error } = useQuote(feed, symbol);
|
|
18
99
|
if (loading) return <span>…</span>;
|
|
19
100
|
if (error) return <span>Error: {error.message}</span>;
|
|
20
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
<span>
|
|
103
|
+
{symbol}: ${data?.price.toFixed(2)}
|
|
104
|
+
</span>
|
|
105
|
+
);
|
|
21
106
|
}
|
|
22
107
|
|
|
23
108
|
// Subscribe to a live stream
|
|
24
109
|
function LiveFeed() {
|
|
25
110
|
const { event } = useStream(feed, ["AAPL", "MSFT", "GOOGL"]);
|
|
26
111
|
if (!event || event.type !== "quote") return null;
|
|
27
|
-
return
|
|
112
|
+
return (
|
|
113
|
+
<p>
|
|
114
|
+
{event.symbol}: ${event.quote.price}
|
|
115
|
+
</p>
|
|
116
|
+
);
|
|
28
117
|
}
|
|
29
118
|
|
|
30
119
|
// Collect price alerts
|
|
31
120
|
function AlertLog() {
|
|
32
121
|
const { events, clearEvents } = useAlerts(feed, [
|
|
33
|
-
{
|
|
122
|
+
{
|
|
123
|
+
symbol: "AAPL",
|
|
124
|
+
condition: { type: "price_above", threshold: 200 },
|
|
125
|
+
once: false,
|
|
126
|
+
},
|
|
34
127
|
]);
|
|
35
128
|
return (
|
|
36
129
|
<ul>
|
|
37
|
-
{events.map((e, i) =>
|
|
130
|
+
{events.map((e, i) => (
|
|
131
|
+
<li key={i}>
|
|
132
|
+
{e.alert.symbol} triggered @ ${e.quote.price}
|
|
133
|
+
</li>
|
|
134
|
+
))}
|
|
38
135
|
<button onClick={clearEvents}>Clear</button>
|
|
39
136
|
</ul>
|
|
40
137
|
);
|
|
@@ -45,10 +142,10 @@ function AlertLog() {
|
|
|
45
142
|
|
|
46
143
|
**`useQuote(source, symbol, options?)`**
|
|
47
144
|
|
|
48
|
-
| Option
|
|
49
|
-
|
|
50
|
-
| `intervalMs` | `5000`
|
|
51
|
-
| `enabled`
|
|
145
|
+
| Option | Default | Description |
|
|
146
|
+
| ------------ | ------- | --------------------------------- |
|
|
147
|
+
| `intervalMs` | `5000` | Poll interval in milliseconds |
|
|
148
|
+
| `enabled` | `true` | Set to `false` to suspend polling |
|
|
52
149
|
|
|
53
150
|
Returns `{ data: Quote \| null, loading: boolean, error: Error \| null, refetch() }`.
|
|
54
151
|
|
|
@@ -101,25 +198,25 @@ console.log(results.map((r) => `${r.symbol} @ ${r.quote.price}`));
|
|
|
101
198
|
|
|
102
199
|
#### Criterion types
|
|
103
200
|
|
|
104
|
-
| Type
|
|
105
|
-
|
|
106
|
-
| `price_above` / `price_below`
|
|
107
|
-
| `change_pct_above` / `change_pct_below` | Filter by daily % change
|
|
108
|
-
| `volume_above` / `volume_below`
|
|
109
|
-
| `market_cap_above` / `market_cap_below` | Filter by market cap
|
|
110
|
-
| `52w_high_pct_below`
|
|
111
|
-
| `52w_low_pct_above`
|
|
112
|
-
| `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 }` |
|
|
113
210
|
|
|
114
211
|
All criteria are evaluated with **AND logic** — a symbol must pass every criterion to be included.
|
|
115
212
|
|
|
116
213
|
#### Options
|
|
117
214
|
|
|
118
|
-
| Option
|
|
119
|
-
|
|
120
|
-
| `criteria`
|
|
215
|
+
| Option | Description |
|
|
216
|
+
| ----------- | ------------------------------------------------------- |
|
|
217
|
+
| `criteria` | Array of `ScreenerCriterion` (required) |
|
|
121
218
|
| `batchSize` | Max symbols per quote fetch call (default: all at once) |
|
|
122
|
-
| `limit`
|
|
219
|
+
| `limit` | Max number of results to return |
|
|
123
220
|
|
|
124
221
|
#### Result shape
|
|
125
222
|
|
|
@@ -146,45 +243,52 @@ import { getFundamentals } from "market-feed/fundamentals";
|
|
|
146
243
|
import { MarketFeed } from "market-feed";
|
|
147
244
|
|
|
148
245
|
const feed = new MarketFeed();
|
|
149
|
-
const { incomeStatements, balanceSheets, cashFlows } = await getFundamentals(
|
|
246
|
+
const { incomeStatements, balanceSheets, cashFlows } = await getFundamentals(
|
|
247
|
+
feed,
|
|
248
|
+
"AAPL"
|
|
249
|
+
);
|
|
150
250
|
|
|
151
251
|
console.log(`Revenue: $${(incomeStatements[0]!.revenue! / 1e9).toFixed(1)}B`);
|
|
152
|
-
console.log(
|
|
153
|
-
|
|
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
|
+
);
|
|
154
258
|
```
|
|
155
259
|
|
|
156
260
|
`getFundamentals()` fetches all three statements in parallel via `Promise.allSettled` — a failure on one statement still returns the others.
|
|
157
261
|
|
|
158
262
|
#### `IncomeStatement`
|
|
159
263
|
|
|
160
|
-
| Field
|
|
161
|
-
|
|
162
|
-
| `revenue`
|
|
163
|
-
| `grossProfit`
|
|
164
|
-
| `operatingIncome` | EBIT (operating)
|
|
165
|
-
| `netIncome`
|
|
166
|
-
| `ebitda`
|
|
167
|
-
| `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 |
|
|
168
272
|
|
|
169
273
|
#### `BalanceSheet`
|
|
170
274
|
|
|
171
|
-
| Field
|
|
172
|
-
|
|
173
|
-
| `totalAssets`
|
|
174
|
-
| `totalLiabilities`
|
|
175
|
-
| `totalStockholdersEquity` | Book value of equity
|
|
176
|
-
| `cashAndCashEquivalents`
|
|
177
|
-
| `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 |
|
|
178
282
|
|
|
179
283
|
#### `CashFlowStatement`
|
|
180
284
|
|
|
181
|
-
| Field
|
|
182
|
-
|
|
183
|
-
| `operatingCashFlow`
|
|
184
|
-
| `capitalExpenditures` | CapEx (negative = outflow)
|
|
185
|
-
| `freeCashFlow`
|
|
186
|
-
| `investingCashFlow`
|
|
187
|
-
| `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 |
|
|
188
292
|
|
|
189
293
|
All three types include `symbol`, `date` (period end), `periodType` (`"annual"` | `"quarterly"`), and `provider`.
|
|
190
294
|
|
|
@@ -193,20 +297,20 @@ All three types include `symbol`, `date` (period end), `periodType` (`"annual"`
|
|
|
193
297
|
```ts
|
|
194
298
|
const feed = new MarketFeed();
|
|
195
299
|
|
|
196
|
-
const income
|
|
300
|
+
const income = await feed.incomeStatements("AAPL", { limit: 4 });
|
|
197
301
|
const balance = await feed.balanceSheets("AAPL", { quarterly: true });
|
|
198
|
-
const cash
|
|
302
|
+
const cash = await feed.cashFlows("AAPL");
|
|
199
303
|
```
|
|
200
304
|
|
|
201
305
|
`FundamentalsOptions`: `quarterly?` (default `false`), `limit?` (default `4`), `raw?`.
|
|
202
306
|
|
|
203
307
|
### Provider support
|
|
204
308
|
|
|
205
|
-
| Method
|
|
206
|
-
|
|
207
|
-
| `incomeStatements` | ✓ `incomeStatementHistory` / `incomeStatementHistoryQuarterly`
|
|
208
|
-
| `balanceSheets`
|
|
209
|
-
| `cashFlows`
|
|
309
|
+
| Method | `YahooProvider` | others |
|
|
310
|
+
| ------------------ | ------------------------------------------------------------------ | ------ |
|
|
311
|
+
| `incomeStatements` | ✓ `incomeStatementHistory` / `incomeStatementHistoryQuarterly` | — |
|
|
312
|
+
| `balanceSheets` | ✓ `balanceSheetHistory` / `balanceSheetHistoryQuarterly` | — |
|
|
313
|
+
| `cashFlows` | ✓ `cashflowStatementHistory` / `cashflowStatementHistoryQuarterly` | — |
|
|
210
314
|
|
|
211
315
|
### Breaking changes
|
|
212
316
|
|
|
@@ -234,21 +338,21 @@ const feed = new MarketFeed([
|
|
|
234
338
|
new TiingoProvider({ apiKey: process.env.TIINGO_KEY! }),
|
|
235
339
|
]);
|
|
236
340
|
|
|
237
|
-
const quote
|
|
238
|
-
const bars
|
|
341
|
+
const quote = await feed.quote(["AAPL"]);
|
|
342
|
+
const bars = await feed.historical("AAPL", { period1: "2024-01-01" });
|
|
239
343
|
const results = await feed.search("apple");
|
|
240
344
|
const profile = await feed.company("AAPL");
|
|
241
|
-
const news
|
|
345
|
+
const news = await feed.news("AAPL", { limit: 5 });
|
|
242
346
|
```
|
|
243
347
|
|
|
244
348
|
Supports: `quote`, `historical`, `search`, `company`, `news`.
|
|
245
349
|
|
|
246
|
-
| Feature
|
|
247
|
-
|
|
350
|
+
| Feature | Detail |
|
|
351
|
+
| ---------------- | ------------------------------------ |
|
|
248
352
|
| Real-time quotes | IEX endpoint — US equities, intraday |
|
|
249
|
-
| Historical
|
|
250
|
-
| Rate limit
|
|
251
|
-
| Auth
|
|
353
|
+
| Historical | EOD daily bars, includes `adjClose` |
|
|
354
|
+
| Rate limit | ~50 calls/hour (free plan) |
|
|
355
|
+
| Auth | `Authorization: Token KEY` header |
|
|
252
356
|
|
|
253
357
|
Free plan sign-up: https://www.tiingo.com
|
|
254
358
|
|
|
@@ -280,8 +384,8 @@ const feed = new MarketFeed([
|
|
|
280
384
|
new TwelveDataProvider({ apiKey: process.env.TWELVE_DATA_KEY! }),
|
|
281
385
|
]);
|
|
282
386
|
|
|
283
|
-
const quote
|
|
284
|
-
const bars
|
|
387
|
+
const quote = await feed.quote(["AAPL", "BTC/USD", "EUR/USD"]);
|
|
388
|
+
const bars = await feed.historical("AAPL", { interval: "1wk" });
|
|
285
389
|
const results = await feed.search("apple");
|
|
286
390
|
const profile = await feed.company("AAPL");
|
|
287
391
|
```
|
|
@@ -289,6 +393,7 @@ const profile = await feed.company("AAPL");
|
|
|
289
393
|
Supports: `quote`, `historical`, `search`, `company`.
|
|
290
394
|
|
|
291
395
|
Strong coverage for global equities, forex, and crypto pairs. Symbol normalisation handles all common formats:
|
|
396
|
+
|
|
292
397
|
- US stocks: `AAPL` (unchanged)
|
|
293
398
|
- Crypto: `BTC-USD` / `BTC/USD` / `X:BTCUSD` → `BTC/USD`
|
|
294
399
|
- Forex: `EURUSD=X` / `C:EURUSD` → `EUR/USD`
|
|
@@ -298,15 +403,15 @@ Free plan sign-up: https://twelvedata.com
|
|
|
298
403
|
#### Interval mapping
|
|
299
404
|
|
|
300
405
|
| market-feed | Twelve Data |
|
|
301
|
-
|
|
302
|
-
| `1m`
|
|
303
|
-
| `5m`
|
|
304
|
-
| `15m`
|
|
305
|
-
| `30m`
|
|
306
|
-
| `1h`
|
|
307
|
-
| `1d`
|
|
308
|
-
| `1wk`
|
|
309
|
-
| `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` |
|
|
310
415
|
|
|
311
416
|
### New utility
|
|
312
417
|
|
|
@@ -358,8 +463,10 @@ None.
|
|
|
358
463
|
import { backtest } from "market-feed/backtest";
|
|
359
464
|
import type { EntrySignal, ExitSignal } from "market-feed/backtest";
|
|
360
465
|
|
|
361
|
-
const entry: EntrySignal = (bars, i) =>
|
|
362
|
-
|
|
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;
|
|
363
470
|
|
|
364
471
|
const result = backtest("AAPL", bars, entry, exit, { initialCapital: 10_000 });
|
|
365
472
|
console.log(`Total return: ${(result.totalReturn * 100).toFixed(2)}%`);
|
|
@@ -367,16 +474,16 @@ console.log(`Sharpe ratio: ${result.sharpeRatio.toFixed(2)}`);
|
|
|
367
474
|
console.log(`Max drawdown: ${(result.maxDrawdown * 100).toFixed(2)}%`);
|
|
368
475
|
```
|
|
369
476
|
|
|
370
|
-
| Field
|
|
371
|
-
|
|
372
|
-
| `totalReturn`
|
|
373
|
-
| `annualizedReturn` | CAGR as a fraction
|
|
374
|
-
| `sharpeRatio`
|
|
375
|
-
| `maxDrawdown`
|
|
376
|
-
| `winRate`
|
|
377
|
-
| `profitFactor`
|
|
378
|
-
| `totalTrades`
|
|
379
|
-
| `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 |
|
|
380
487
|
|
|
381
488
|
**`market-feed/alerts`** — Poll a feed and yield `AlertEvent` when conditions are met.
|
|
382
489
|
|
|
@@ -387,21 +494,33 @@ import { MarketFeed } from "market-feed";
|
|
|
387
494
|
const feed = new MarketFeed();
|
|
388
495
|
const controller = new AbortController();
|
|
389
496
|
|
|
390
|
-
for await (const event of watchAlerts(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
+
)) {
|
|
394
513
|
console.log(`${event.alert.symbol} triggered: $${event.quote.price}`);
|
|
395
514
|
}
|
|
396
515
|
```
|
|
397
516
|
|
|
398
|
-
| Condition type
|
|
399
|
-
|
|
400
|
-
| `price_above`
|
|
401
|
-
| `price_below`
|
|
402
|
-
| `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 |
|
|
403
522
|
| `change_pct_below` | Daily `%` change falls below threshold |
|
|
404
|
-
| `volume_above`
|
|
523
|
+
| `volume_above` | `quote.volume > threshold` |
|
|
405
524
|
|
|
406
525
|
`AlertConfig` options: `once` (fire at most once), `debounceMs` (suppress re-fires within window).
|
|
407
526
|
|
|
@@ -412,27 +531,33 @@ Three new methods on `MarketFeed` (and on individual providers):
|
|
|
412
531
|
```ts
|
|
413
532
|
const feed = new MarketFeed([new PolygonProvider({ apiKey: "..." })]);
|
|
414
533
|
|
|
415
|
-
const earnings
|
|
534
|
+
const earnings = await feed.earnings("AAPL", { limit: 8 });
|
|
416
535
|
const dividends = await feed.dividends("AAPL");
|
|
417
|
-
const splits
|
|
536
|
+
const splits = await feed.splits("AAPL");
|
|
418
537
|
```
|
|
419
538
|
|
|
420
539
|
#### Provider support
|
|
421
540
|
|
|
422
|
-
| Method
|
|
423
|
-
|
|
424
|
-
| `earnings`
|
|
425
|
-
| `dividends` | ✓ chart `events=div`
|
|
426
|
-
| `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` | — | — |
|
|
427
546
|
|
|
428
547
|
#### `EarningsEvent`
|
|
429
548
|
|
|
430
549
|
```ts
|
|
431
550
|
interface EarningsEvent {
|
|
432
|
-
symbol: string;
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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;
|
|
436
561
|
}
|
|
437
562
|
```
|
|
438
563
|
|
|
@@ -440,10 +565,15 @@ interface EarningsEvent {
|
|
|
440
565
|
|
|
441
566
|
```ts
|
|
442
567
|
interface DividendEvent {
|
|
443
|
-
symbol: string;
|
|
444
|
-
|
|
568
|
+
symbol: string;
|
|
569
|
+
exDate: Date;
|
|
570
|
+
payDate?: Date;
|
|
571
|
+
declaredDate?: Date;
|
|
572
|
+
amount: number;
|
|
573
|
+
currency: string;
|
|
445
574
|
frequency?: "annual" | "semi-annual" | "quarterly" | "monthly" | "irregular";
|
|
446
|
-
provider: string;
|
|
575
|
+
provider: string;
|
|
576
|
+
raw?: unknown;
|
|
447
577
|
}
|
|
448
578
|
```
|
|
449
579
|
|
|
@@ -451,10 +581,12 @@ interface DividendEvent {
|
|
|
451
581
|
|
|
452
582
|
```ts
|
|
453
583
|
interface SplitEvent {
|
|
454
|
-
symbol: string;
|
|
455
|
-
|
|
584
|
+
symbol: string;
|
|
585
|
+
date: Date;
|
|
586
|
+
ratio: number; // 4-for-1 forward split → 4; 1-for-10 reverse → 0.1
|
|
456
587
|
description?: string;
|
|
457
|
-
provider: string;
|
|
588
|
+
provider: string;
|
|
589
|
+
raw?: unknown;
|
|
458
590
|
}
|
|
459
591
|
```
|
|
460
592
|
|
|
@@ -487,10 +619,14 @@ import { FinnhubProvider } from "market-feed";
|
|
|
487
619
|
const provider = new FinnhubProvider({ apiKey: process.env.FINNHUB_KEY! });
|
|
488
620
|
const controller = new AbortController();
|
|
489
621
|
|
|
490
|
-
for await (const event of connect(provider, ["AAPL", "MSFT"], {
|
|
622
|
+
for await (const event of connect(provider, ["AAPL", "MSFT"], {
|
|
623
|
+
signal: controller.signal,
|
|
624
|
+
})) {
|
|
491
625
|
switch (event.type) {
|
|
492
626
|
case "trade":
|
|
493
|
-
console.log(
|
|
627
|
+
console.log(
|
|
628
|
+
`${event.trade.symbol}: $${event.trade.price} × ${event.trade.size}`
|
|
629
|
+
);
|
|
494
630
|
break;
|
|
495
631
|
case "connected":
|
|
496
632
|
console.log(`Connected to ${event.provider}`);
|
|
@@ -507,23 +643,23 @@ for await (const event of connect(provider, ["AAPL", "MSFT"], { signal: controll
|
|
|
507
643
|
|
|
508
644
|
#### Provider support
|
|
509
645
|
|
|
510
|
-
| Provider
|
|
511
|
-
|
|
512
|
-
| **PolygonProvider**
|
|
513
|
-
| **FinnhubProvider**
|
|
514
|
-
| **YahooProvider**
|
|
515
|
-
| **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 |
|
|
516
652
|
|
|
517
653
|
The `connect()` function detects provider capability automatically — no configuration required.
|
|
518
654
|
|
|
519
655
|
#### `WsEvent` union
|
|
520
656
|
|
|
521
|
-
| `type`
|
|
522
|
-
|
|
523
|
-
| `"connected"`
|
|
524
|
-
| `"trade"`
|
|
525
|
-
| `"disconnected"` | `provider`, `reconnecting`, `attempt` | WS closed unexpectedly
|
|
526
|
-
| `"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 |
|
|
527
663
|
|
|
528
664
|
#### `WsTrade`
|
|
529
665
|
|
|
@@ -531,7 +667,7 @@ The `connect()` function detects provider capability automatically — no config
|
|
|
531
667
|
interface WsTrade {
|
|
532
668
|
symbol: string;
|
|
533
669
|
price: number;
|
|
534
|
-
size: number;
|
|
670
|
+
size: number; // shares / units
|
|
535
671
|
timestamp: Date;
|
|
536
672
|
conditions?: number[]; // provider-specific condition codes
|
|
537
673
|
}
|
|
@@ -539,12 +675,12 @@ interface WsTrade {
|
|
|
539
675
|
|
|
540
676
|
#### `WsOptions`
|
|
541
677
|
|
|
542
|
-
| Option
|
|
543
|
-
|
|
544
|
-
| `wsImpl`
|
|
545
|
-
| `maxReconnectAttempts` | `number`
|
|
546
|
-
| `reconnectDelayMs`
|
|
547
|
-
| `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 |
|
|
548
684
|
|
|
549
685
|
#### Node 18–20 compatibility
|
|
550
686
|
|
|
@@ -552,7 +688,9 @@ Node 21+ exposes `WebSocket` globally. For Node 18/20, install the `ws` package
|
|
|
552
688
|
|
|
553
689
|
```ts
|
|
554
690
|
import WebSocket from "ws";
|
|
555
|
-
connect(provider, ["AAPL"], {
|
|
691
|
+
connect(provider, ["AAPL"], {
|
|
692
|
+
wsImpl: WebSocket as unknown as typeof globalThis.WebSocket,
|
|
693
|
+
});
|
|
556
694
|
```
|
|
557
695
|
|
|
558
696
|
### Other changes
|
|
@@ -573,6 +711,7 @@ None. All v0.3.0 imports continue to work unchanged.
|
|
|
573
711
|
### New provider
|
|
574
712
|
|
|
575
713
|
**`FinnhubProvider`** — Finnhub.io (free tier: real-time US stock data, 60 calls/minute).
|
|
714
|
+
|
|
576
715
|
- `quote`, `historical` (candles), `search`, `company`, `news`
|
|
577
716
|
- API key required — get one free at https://finnhub.io
|
|
578
717
|
- Uses `X-Finnhub-Token` header; rate-limited client-side at 60 req/min
|
|
@@ -582,6 +721,7 @@ None. All v0.3.0 imports continue to work unchanged.
|
|
|
582
721
|
### New modules
|
|
583
722
|
|
|
584
723
|
**`market-feed/indicators`** — Technical indicators as pure functions over `HistoricalBar[]`.
|
|
724
|
+
|
|
585
725
|
- `sma(bars, period)` — Simple Moving Average (O(1) sliding window)
|
|
586
726
|
- `ema(bars, period)` — Exponential Moving Average (k = 2/(period+1), SMA-seeded)
|
|
587
727
|
- `rsi(bars, period?)` — Relative Strength Index via Wilder's smoothing (default period: 14)
|
|
@@ -594,6 +734,7 @@ None. All v0.3.0 imports continue to work unchanged.
|
|
|
594
734
|
- All functions return typed result arrays (`IndicatorPoint[]`, `MACDPoint[]`, `BollingerPoint[]`, `StochasticPoint[]`)
|
|
595
735
|
|
|
596
736
|
**`market-feed/portfolio`** — Track positions and compute live P&L.
|
|
737
|
+
|
|
597
738
|
- `new Portfolio(positions?)` — construct with an array of `Position` objects
|
|
598
739
|
- `portfolio.add(position)` / `portfolio.remove(symbol)` — mutable, chainable
|
|
599
740
|
- `portfolio.snapshot(feed)` — fetches live quotes and returns `PortfolioSnapshot` with per-position and aggregate P&L
|
|
@@ -646,6 +787,7 @@ None. All v0.2.0 imports continue to work unchanged.
|
|
|
646
787
|
### New modules
|
|
647
788
|
|
|
648
789
|
**`market-feed/calendar`** — Synchronous exchange calendar. No network required.
|
|
790
|
+
|
|
649
791
|
- `isMarketOpen(exchange, at?)` — boolean, DST-correct via `Intl`
|
|
650
792
|
- `getSession(exchange, at?)` — `"pre" | "regular" | "post" | "closed"`
|
|
651
793
|
- `nextSessionOpen(exchange, from?)` / `nextSessionClose(exchange, from?)` — next UTC Date
|
|
@@ -657,6 +799,7 @@ None. All v0.2.0 imports continue to work unchanged.
|
|
|
657
799
|
- Early-close days (NYSE: day before Thanksgiving, Independence Day, Christmas Eve)
|
|
658
800
|
|
|
659
801
|
**`market-feed/stream`** — Market-hours-aware observable quote stream.
|
|
802
|
+
|
|
660
803
|
- `watch(feed, symbols, options)` — async generator yielding typed `StreamEvent` union
|
|
661
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
|
|
662
805
|
- Emits `market-open` / `market-close` events at session transitions
|
|
@@ -665,6 +808,7 @@ None. All v0.2.0 imports continue to work unchanged.
|
|
|
665
808
|
- Configurable `maxErrors` before the generator throws
|
|
666
809
|
|
|
667
810
|
**`market-feed/consensus`** — Multi-provider parallel price consensus.
|
|
811
|
+
|
|
668
812
|
- `consensus(providers, symbol, options)` — queries all providers simultaneously via `Promise.allSettled`
|
|
669
813
|
- Median-based outlier detection (avoids the all-outlier edge case of mean-based approaches)
|
|
670
814
|
- Staleness detection: providers with quotes older than `stalenessThreshold` receive half weight
|
|
@@ -689,11 +833,13 @@ None. All v0.1.0 imports continue to work unchanged.
|
|
|
689
833
|
### Initial release
|
|
690
834
|
|
|
691
835
|
**Providers**
|
|
836
|
+
|
|
692
837
|
- `YahooProvider` — Yahoo Finance (no API key required): quote, historical, search, company
|
|
693
838
|
- `AlphaVantageProvider` — Alpha Vantage (free: 25/day): quote, historical, search, company
|
|
694
839
|
- `PolygonProvider` — Polygon.io (free: 15-min delayed): quote, historical, search, company, news
|
|
695
840
|
|
|
696
841
|
**Core**
|
|
842
|
+
|
|
697
843
|
- Unified `Quote`, `HistoricalBar`, `CompanyProfile`, `NewsItem`, `SearchResult`, `MarketStatus` types
|
|
698
844
|
- `MarketFeed` client with provider chain, automatic fallback, and LRU caching
|
|
699
845
|
- `MemoryCacheDriver` — zero-dependency LRU cache with TTL and configurable max size
|
|
@@ -704,6 +850,7 @@ None. All v0.1.0 imports continue to work unchanged.
|
|
|
704
850
|
- Error hierarchy: `MarketFeedError`, `ProviderError`, `RateLimitError`, `UnsupportedOperationError`, `AllProvidersFailedError`
|
|
705
851
|
|
|
706
852
|
**DX**
|
|
853
|
+
|
|
707
854
|
- Zero production dependencies (native `fetch` only)
|
|
708
855
|
- Strict TypeScript — `strict: true`, `noUncheckedIndexedAccess`, `noImplicitOverride`
|
|
709
856
|
- ESM + CJS + `.d.ts` dual build
|