market-feed 0.1.0
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 +28 -0
- package/LICENSE +21 -0
- package/README.md +332 -0
- package/dist/index.cjs +1141 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +507 -0
- package/dist/index.d.ts +507 -0
- package/dist/index.js +1123 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# market-feed Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-03-10
|
|
4
|
+
|
|
5
|
+
### Initial release
|
|
6
|
+
|
|
7
|
+
**Providers**
|
|
8
|
+
- `YahooProvider` — Yahoo Finance (no API key required): quote, historical, search, company
|
|
9
|
+
- `AlphaVantageProvider` — Alpha Vantage (free: 25/day): quote, historical, search, company
|
|
10
|
+
- `PolygonProvider` — Polygon.io (free: 15-min delayed): quote, historical, search, company, news
|
|
11
|
+
|
|
12
|
+
**Core**
|
|
13
|
+
- Unified `Quote`, `HistoricalBar`, `CompanyProfile`, `NewsItem`, `SearchResult`, `MarketStatus` types
|
|
14
|
+
- `MarketFeed` client with provider chain, automatic fallback, and LRU caching
|
|
15
|
+
- `MemoryCacheDriver` — zero-dependency LRU cache with TTL and configurable max size
|
|
16
|
+
- `CacheDriver` interface for plugging in Redis, Upstash, Cloudflare KV, or any store
|
|
17
|
+
- `RateLimiter` — token-bucket rate limiter (used internally; also exported for custom providers)
|
|
18
|
+
- `HttpClient` — fetch wrapper with exponential-backoff retry and per-request timeout
|
|
19
|
+
- Symbol utilities: `normalise`, `stripExchange`, `toYahooSymbol`, `toAlphaVantageSymbol`, `toPolygonSymbol`, `dedupeSymbols`
|
|
20
|
+
- Error hierarchy: `MarketFeedError`, `ProviderError`, `RateLimitError`, `UnsupportedOperationError`, `AllProvidersFailedError`
|
|
21
|
+
|
|
22
|
+
**DX**
|
|
23
|
+
- Zero production dependencies (native `fetch` only)
|
|
24
|
+
- Strict TypeScript — `strict: true`, `noUncheckedIndexedAccess`, `noImplicitOverride`
|
|
25
|
+
- ESM + CJS + `.d.ts` dual build
|
|
26
|
+
- Multi-runtime: Node 18+, Bun 1+, Deno 2+, Cloudflare Workers
|
|
27
|
+
- 80+ unit tests covering all providers, cache, rate limiter, symbol utils, and error classes
|
|
28
|
+
- VitePress documentation site
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 market-feed contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# market-feed
|
|
2
|
+
|
|
3
|
+
> Unified TypeScript client for financial market data.
|
|
4
|
+
> Wraps Yahoo Finance, Alpha Vantage, and Polygon.io under one consistent interface — with caching and automatic fallback built in.
|
|
5
|
+
|
|
6
|
+
[](https://github.com/piyushgupta344/market-feed/actions/workflows/ci.yml)
|
|
7
|
+
[](https://www.npmjs.com/package/market-feed)
|
|
8
|
+
[](https://www.npmjs.com/package/market-feed)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](tsconfig.json)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The problem
|
|
15
|
+
|
|
16
|
+
Every free financial API speaks a different language:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
// Yahoo Finance
|
|
20
|
+
result.chart.result[0].meta.regularMarketPrice
|
|
21
|
+
|
|
22
|
+
// Alpha Vantage
|
|
23
|
+
data["Global Quote"]["05. price"]
|
|
24
|
+
|
|
25
|
+
// Polygon.io
|
|
26
|
+
data.ticker.lastTrade.p
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
You write adapters, you add caching, you handle fallback — for every project, every time.
|
|
30
|
+
|
|
31
|
+
## The solution
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { MarketFeed } from "market-feed";
|
|
35
|
+
|
|
36
|
+
const feed = new MarketFeed();
|
|
37
|
+
const quote = await feed.quote("AAPL");
|
|
38
|
+
|
|
39
|
+
console.log(quote.price); // always a number, always the same key
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
One interface. Three providers. Zero API key required for Yahoo Finance.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **Unified types** — `Quote`, `HistoricalBar`, `CompanyProfile`, `NewsItem`, `SearchResult` are consistent regardless of which provider answers
|
|
49
|
+
- **Zero production dependencies** — uses native `fetch`, works everywhere
|
|
50
|
+
- **Built-in LRU cache** — configurable TTL per method, pluggable driver (Redis, Upstash, etc.)
|
|
51
|
+
- **Automatic fallback** — if Yahoo is down, tries Alpha Vantage, then Polygon
|
|
52
|
+
- **Rate-limit aware** — won't silently burn your free Alpha Vantage / Polygon quota
|
|
53
|
+
- **Strict TypeScript** — no `any`, full autocomplete, compile-time safety
|
|
54
|
+
- **Multi-runtime** — Node 18+, Bun 1+, Deno 2+, Cloudflare Workers
|
|
55
|
+
- **Escape hatch** — pass `{ raw: true }` to get the original provider response
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
npm install market-feed
|
|
63
|
+
# or
|
|
64
|
+
pnpm add market-feed
|
|
65
|
+
# or
|
|
66
|
+
bun add market-feed
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Quick Start
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { MarketFeed } from "market-feed";
|
|
75
|
+
|
|
76
|
+
// Zero-config — uses Yahoo Finance, no API key needed
|
|
77
|
+
const feed = new MarketFeed();
|
|
78
|
+
|
|
79
|
+
// Single quote
|
|
80
|
+
const aapl = await feed.quote("AAPL");
|
|
81
|
+
console.log(`${aapl.symbol}: $${aapl.price.toFixed(2)}`);
|
|
82
|
+
|
|
83
|
+
// Multiple quotes (parallel)
|
|
84
|
+
const quotes = await feed.quote(["MSFT", "GOOGL", "AMZN"]);
|
|
85
|
+
|
|
86
|
+
// Historical data
|
|
87
|
+
const history = await feed.historical("AAPL", {
|
|
88
|
+
period1: "2024-01-01",
|
|
89
|
+
period2: "2024-12-31",
|
|
90
|
+
interval: "1d",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Search
|
|
94
|
+
const results = await feed.search("Tesla");
|
|
95
|
+
|
|
96
|
+
// Company profile
|
|
97
|
+
const profile = await feed.company("AAPL");
|
|
98
|
+
console.log(profile.sector); // "Technology"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Providers
|
|
104
|
+
|
|
105
|
+
| Provider | API Key | Quote | Historical | Search | Company | News | Market Status |
|
|
106
|
+
|----------|---------|:-----:|:----------:|:------:|:-------:|:----:|:-------------:|
|
|
107
|
+
| **Yahoo Finance** | Not required | ✓ | ✓ | ✓ | ✓ | — | — |
|
|
108
|
+
| **Alpha Vantage** | Free (25/day) | ✓ | ✓ | ✓ | ✓ | — | — |
|
|
109
|
+
| **Polygon.io** | Free (delayed) | ✓ | ✓ | ✓ | ✓ | ✓ | — |
|
|
110
|
+
|
|
111
|
+
Get free keys: [Alpha Vantage](https://www.alphavantage.co/support/#api-key) · [Polygon.io](https://polygon.io/)
|
|
112
|
+
|
|
113
|
+
### Using multiple providers
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import {
|
|
117
|
+
MarketFeed,
|
|
118
|
+
YahooProvider,
|
|
119
|
+
AlphaVantageProvider,
|
|
120
|
+
PolygonProvider,
|
|
121
|
+
} from "market-feed";
|
|
122
|
+
|
|
123
|
+
const feed = new MarketFeed({
|
|
124
|
+
providers: [
|
|
125
|
+
new YahooProvider(),
|
|
126
|
+
new AlphaVantageProvider({ apiKey: process.env.AV_KEY }),
|
|
127
|
+
new PolygonProvider({ apiKey: process.env.POLYGON_KEY }),
|
|
128
|
+
],
|
|
129
|
+
fallback: true, // auto-try next provider on failure
|
|
130
|
+
});
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## Caching
|
|
136
|
+
|
|
137
|
+
The default LRU cache stores responses in memory with sensible TTLs:
|
|
138
|
+
|
|
139
|
+
| Method | Default TTL |
|
|
140
|
+
|--------|-------------|
|
|
141
|
+
| `quote` | 60s |
|
|
142
|
+
| `historical` | 1 hour |
|
|
143
|
+
| `company` | 24 hours |
|
|
144
|
+
| `news` | 5 minutes |
|
|
145
|
+
| `search` | 10 minutes |
|
|
146
|
+
| `marketStatus` | 60s |
|
|
147
|
+
|
|
148
|
+
### Override TTLs
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
const feed = new MarketFeed({
|
|
152
|
+
cache: {
|
|
153
|
+
ttl: 60, // default fallback TTL
|
|
154
|
+
maxSize: 1000, // max entries in memory
|
|
155
|
+
ttlOverrides: {
|
|
156
|
+
quote: 15, // aggressive refresh for real-time feel
|
|
157
|
+
company: 604800, // company profiles change rarely
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Disable caching
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const feed = new MarketFeed({ cache: false });
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Custom cache driver (Redis, Upstash, filesystem...)
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import type { CacheDriver } from "market-feed";
|
|
173
|
+
import { createClient } from "redis";
|
|
174
|
+
|
|
175
|
+
const redis = createClient();
|
|
176
|
+
await redis.connect();
|
|
177
|
+
|
|
178
|
+
const driver: CacheDriver = {
|
|
179
|
+
async get<T>(key: string) {
|
|
180
|
+
const val = await redis.get(key);
|
|
181
|
+
return val ? (JSON.parse(val) as T) : undefined;
|
|
182
|
+
},
|
|
183
|
+
async set<T>(key: string, value: T, ttl = 60) {
|
|
184
|
+
await redis.set(key, JSON.stringify(value), { EX: ttl });
|
|
185
|
+
},
|
|
186
|
+
async delete(key: string) { await redis.del(key); },
|
|
187
|
+
async clear() { await redis.flushDb(); },
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const feed = new MarketFeed({ cache: { driver } });
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## API Reference
|
|
196
|
+
|
|
197
|
+
### `new MarketFeed(options?)`
|
|
198
|
+
|
|
199
|
+
| Option | Type | Default | Description |
|
|
200
|
+
|--------|------|---------|-------------|
|
|
201
|
+
| `providers` | `MarketProvider[]` | `[new YahooProvider()]` | Provider chain |
|
|
202
|
+
| `cache` | `CacheConfig \| false` | LRU, 60s TTL | Cache configuration |
|
|
203
|
+
| `fallback` | `boolean` | `true` | Auto-failover on provider error |
|
|
204
|
+
|
|
205
|
+
### Methods
|
|
206
|
+
|
|
207
|
+
```ts
|
|
208
|
+
// Quotes
|
|
209
|
+
feed.quote(symbol: string, options?: QuoteOptions): Promise<Quote>
|
|
210
|
+
feed.quote(symbols: string[], options?: QuoteOptions): Promise<Quote[]>
|
|
211
|
+
|
|
212
|
+
// Historical bars
|
|
213
|
+
feed.historical(symbol: string, options?: HistoricalOptions): Promise<HistoricalBar[]>
|
|
214
|
+
|
|
215
|
+
// Symbol search
|
|
216
|
+
feed.search(query: string, options?: SearchOptions): Promise<SearchResult[]>
|
|
217
|
+
|
|
218
|
+
// Company profile
|
|
219
|
+
feed.company(symbol: string, options?: CompanyOptions): Promise<CompanyProfile>
|
|
220
|
+
|
|
221
|
+
// News
|
|
222
|
+
feed.news(symbol: string, options?: NewsOptions): Promise<NewsItem[]>
|
|
223
|
+
|
|
224
|
+
// Market status
|
|
225
|
+
feed.marketStatus(market?: string): Promise<MarketStatus>
|
|
226
|
+
|
|
227
|
+
// Cache management
|
|
228
|
+
feed.clearCache(): Promise<void>
|
|
229
|
+
feed.invalidate(key: string): Promise<void>
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### `HistoricalOptions`
|
|
233
|
+
|
|
234
|
+
```ts
|
|
235
|
+
interface HistoricalOptions {
|
|
236
|
+
period1?: string | Date; // start date, default: 1 year ago
|
|
237
|
+
period2?: string | Date; // end date, default: today
|
|
238
|
+
interval?: "1m" | "2m" | "5m" | "15m" | "30m" | "60m" | "1h"
|
|
239
|
+
| "1d" | "5d" | "1wk" | "1mo" | "3mo"; // default: "1d"
|
|
240
|
+
raw?: boolean;
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
## Error Handling
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import {
|
|
250
|
+
MarketFeedError,
|
|
251
|
+
ProviderError,
|
|
252
|
+
RateLimitError,
|
|
253
|
+
AllProvidersFailedError,
|
|
254
|
+
} from "market-feed";
|
|
255
|
+
|
|
256
|
+
try {
|
|
257
|
+
const quote = await feed.quote("AAPL");
|
|
258
|
+
} catch (err) {
|
|
259
|
+
if (err instanceof RateLimitError) {
|
|
260
|
+
console.log(`Rate limited. Retry after: ${err.retryAfter?.toISOString()}`);
|
|
261
|
+
} else if (err instanceof AllProvidersFailedError) {
|
|
262
|
+
console.log("All providers failed:", err.errors.map(e => e.message));
|
|
263
|
+
} else if (err instanceof ProviderError) {
|
|
264
|
+
console.log(`Provider error (${err.provider}): ${err.message}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## Building a Custom Provider
|
|
272
|
+
|
|
273
|
+
Implement the `MarketProvider` interface to add any data source:
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
import type { MarketProvider, Quote } from "market-feed";
|
|
277
|
+
|
|
278
|
+
class MyProvider implements MarketProvider {
|
|
279
|
+
readonly name = "my-provider";
|
|
280
|
+
|
|
281
|
+
async quote(symbols: string[]): Promise<Quote[]> {
|
|
282
|
+
// fetch from your API, return normalised Quote objects
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async historical(symbol: string, options) {
|
|
286
|
+
// ...
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async search(query: string) {
|
|
290
|
+
// ...
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const feed = new MarketFeed({ providers: [new MyProvider()] });
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
## Runtime Compatibility
|
|
300
|
+
|
|
301
|
+
| Runtime | Version | Notes |
|
|
302
|
+
|---------|---------|-------|
|
|
303
|
+
| Node.js | 18+ | Requires native `fetch` (available since Node 18) |
|
|
304
|
+
| Bun | 1+ | Fully supported |
|
|
305
|
+
| Deno | 2+ | Fully supported |
|
|
306
|
+
| Cloudflare Workers | Latest | Fully supported |
|
|
307
|
+
| Browser | — | Not supported — Yahoo Finance blocks CORS. Use a server-side proxy. |
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## Contributing
|
|
312
|
+
|
|
313
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). All contributions welcome.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
git clone https://github.com/piyushgupta344/market-feed
|
|
317
|
+
cd market-feed
|
|
318
|
+
pnpm install
|
|
319
|
+
pnpm test
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Disclaimer
|
|
325
|
+
|
|
326
|
+
This library is not affiliated with or endorsed by Yahoo Finance, Alpha Vantage, or Polygon.io. Data is provided for informational purposes only and should not be used as the sole basis for investment decisions.
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## License
|
|
331
|
+
|
|
332
|
+
[MIT](LICENSE)
|