@lyniaca/lynstock 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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/dist/cryptoSymbols.d.ts +9 -0
  4. package/dist/cryptoSymbols.d.ts.map +1 -0
  5. package/dist/cryptoSymbols.js +23 -0
  6. package/dist/cryptoSymbols.js.map +1 -0
  7. package/dist/errors.d.ts +28 -0
  8. package/dist/errors.d.ts.map +1 -0
  9. package/dist/errors.js +50 -0
  10. package/dist/errors.js.map +1 -0
  11. package/dist/historicalCache.d.ts +23 -0
  12. package/dist/historicalCache.d.ts.map +1 -0
  13. package/dist/historicalCache.js +33 -0
  14. package/dist/historicalCache.js.map +1 -0
  15. package/dist/index.d.ts +6 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +6 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/lynstock.d.ts +45 -0
  20. package/dist/lynstock.d.ts.map +1 -0
  21. package/dist/lynstock.js +131 -0
  22. package/dist/lynstock.js.map +1 -0
  23. package/dist/sources/alphaVantage.d.ts +3 -0
  24. package/dist/sources/alphaVantage.d.ts.map +1 -0
  25. package/dist/sources/alphaVantage.js +79 -0
  26. package/dist/sources/alphaVantage.js.map +1 -0
  27. package/dist/sources/binance.d.ts +3 -0
  28. package/dist/sources/binance.d.ts.map +1 -0
  29. package/dist/sources/binance.js +68 -0
  30. package/dist/sources/binance.js.map +1 -0
  31. package/dist/sources/coinCap.d.ts +3 -0
  32. package/dist/sources/coinCap.d.ts.map +1 -0
  33. package/dist/sources/coinCap.js +37 -0
  34. package/dist/sources/coinCap.js.map +1 -0
  35. package/dist/sources/coinGecko.d.ts +3 -0
  36. package/dist/sources/coinGecko.d.ts.map +1 -0
  37. package/dist/sources/coinGecko.js +76 -0
  38. package/dist/sources/coinGecko.js.map +1 -0
  39. package/dist/sources/coinbase.d.ts +3 -0
  40. package/dist/sources/coinbase.d.ts.map +1 -0
  41. package/dist/sources/coinbase.js +71 -0
  42. package/dist/sources/coinbase.js.map +1 -0
  43. package/dist/sources/finnhub.d.ts +3 -0
  44. package/dist/sources/finnhub.d.ts.map +1 -0
  45. package/dist/sources/finnhub.js +35 -0
  46. package/dist/sources/finnhub.js.map +1 -0
  47. package/dist/sources/kraken.d.ts +3 -0
  48. package/dist/sources/kraken.d.ts.map +1 -0
  49. package/dist/sources/kraken.js +79 -0
  50. package/dist/sources/kraken.js.map +1 -0
  51. package/dist/sources/stooq.d.ts +3 -0
  52. package/dist/sources/stooq.d.ts.map +1 -0
  53. package/dist/sources/stooq.js +83 -0
  54. package/dist/sources/stooq.js.map +1 -0
  55. package/dist/sources/twelveData.d.ts +3 -0
  56. package/dist/sources/twelveData.d.ts.map +1 -0
  57. package/dist/sources/twelveData.js +80 -0
  58. package/dist/sources/twelveData.js.map +1 -0
  59. package/dist/sources/yahoo.d.ts +3 -0
  60. package/dist/sources/yahoo.d.ts.map +1 -0
  61. package/dist/sources/yahoo.js +65 -0
  62. package/dist/sources/yahoo.js.map +1 -0
  63. package/dist/time.d.ts +4 -0
  64. package/dist/time.d.ts.map +1 -0
  65. package/dist/time.js +10 -0
  66. package/dist/time.js.map +1 -0
  67. package/dist/types.d.ts +47 -0
  68. package/dist/types.d.ts.map +1 -0
  69. package/dist/types.js +2 -0
  70. package/dist/types.js.map +1 -0
  71. package/package.json +43 -0
@@ -0,0 +1,83 @@
1
+ import { RateLimitError, SourceError } from "../errors.js";
2
+ import { toDateString } from "../time.js";
3
+ function toStooqSymbol(symbol) {
4
+ return symbol.includes(".") ? symbol.toLowerCase() : `${symbol.toLowerCase()}.us`;
5
+ }
6
+ export function createStooqSource(fetchImpl = fetch) {
7
+ return {
8
+ name: "stooq",
9
+ assetClasses: ["stock"],
10
+ requiresApiKey: false,
11
+ async getQuote(symbol) {
12
+ const stooqSymbol = toStooqSymbol(symbol);
13
+ const url = `https://stooq.com/q/l/?s=${encodeURIComponent(stooqSymbol)}&f=sd2t2ohlcv&h&e=csv`;
14
+ const response = await fetchImpl(url);
15
+ if (response.status === 429) {
16
+ throw new RateLimitError("stooq");
17
+ }
18
+ if (!response.ok) {
19
+ throw new SourceError("stooq", `HTTP ${response.status}`);
20
+ }
21
+ const csv = (await response.text()).trim();
22
+ const lines = csv.split("\n");
23
+ if (lines.length < 2) {
24
+ throw new SourceError("stooq", "Malformed CSV response");
25
+ }
26
+ const columns = lines[1].split(",");
27
+ const close = Number(columns[6]);
28
+ if (!Number.isFinite(close)) {
29
+ throw new SourceError("stooq", `No data for symbol "${symbol}"`);
30
+ }
31
+ return {
32
+ symbol: symbol.toUpperCase(),
33
+ assetClass: "stock",
34
+ price: close,
35
+ currency: "USD",
36
+ timestamp: Date.now(),
37
+ source: "stooq",
38
+ };
39
+ },
40
+ async getHistorical(symbol, query) {
41
+ const stooqSymbol = toStooqSymbol(symbol);
42
+ const d1 = toDateString(query.from).replace(/-/g, "");
43
+ const d2 = toDateString(query.to).replace(/-/g, "");
44
+ const url = `https://stooq.com/q/d/l/?s=${encodeURIComponent(stooqSymbol)}&d1=${d1}&d2=${d2}&i=d`;
45
+ const response = await fetchImpl(url);
46
+ if (response.status === 429) {
47
+ throw new RateLimitError("stooq");
48
+ }
49
+ if (!response.ok) {
50
+ throw new SourceError("stooq", `HTTP ${response.status}`);
51
+ }
52
+ const csv = (await response.text()).trim();
53
+ const lines = csv.split("\n");
54
+ // Stooq sometimes serves an HTML anti-bot challenge page with HTTP 200
55
+ // instead of real CSV; the real header is a fixed, known string, so anything
56
+ // else means the response isn't the data it looks like at a glance.
57
+ if (lines.length < 2 ||
58
+ !lines[0].toLowerCase().startsWith("date,open,high,low,close,volume") ||
59
+ lines[0].toLowerCase().includes("no data")) {
60
+ throw new SourceError("stooq", `No historical data for symbol "${symbol}"`);
61
+ }
62
+ const candles = lines
63
+ .slice(1)
64
+ .map((line) => {
65
+ const [date, open, high, low, close, volume] = line.split(",");
66
+ return {
67
+ timestamp: Date.parse(`${date}T00:00:00Z`),
68
+ open: Number(open),
69
+ high: Number(high),
70
+ low: Number(low),
71
+ close: Number(close),
72
+ volume: Number(volume),
73
+ };
74
+ })
75
+ .filter((candle) => Number.isFinite(candle.close) && Number.isFinite(candle.timestamp));
76
+ if (candles.length === 0) {
77
+ throw new SourceError("stooq", `No historical data for symbol "${symbol}"`);
78
+ }
79
+ return { symbol: symbol.toUpperCase(), assetClass: "stock", source: "stooq", candles };
80
+ },
81
+ };
82
+ }
83
+ //# sourceMappingURL=stooq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stooq.js","sourceRoot":"","sources":["../../src/sources/stooq.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { FetchLike, QuoteSource } from "../types.js";
2
+ export declare function createTwelveDataSource(fetchImpl?: FetchLike): QuoteSource;
3
+ //# sourceMappingURL=twelveData.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twelveData.d.ts","sourceRoot":"","sources":["../../src/sources/twelveData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAU,MAAM,aAAa,CAAC;AAIlE,wBAAgB,sBAAsB,CAAC,SAAS,GAAE,SAAiB,GAAG,WAAW,CA8EhF"}
@@ -0,0 +1,80 @@
1
+ import { MissingApiKeyError, RateLimitError, SourceError } from "../errors.js";
2
+ import { toDateString } from "../time.js";
3
+ export function createTwelveDataSource(fetchImpl = fetch) {
4
+ return {
5
+ name: "twelvedata",
6
+ assetClasses: ["stock"],
7
+ requiresApiKey: true,
8
+ async getQuote(symbol, apiKey) {
9
+ if (!apiKey) {
10
+ throw new MissingApiKeyError("twelvedata");
11
+ }
12
+ const url = `https://api.twelvedata.com/price?symbol=${encodeURIComponent(symbol)}&apikey=${apiKey}`;
13
+ const response = await fetchImpl(url);
14
+ if (response.status === 429) {
15
+ throw new RateLimitError("twelvedata");
16
+ }
17
+ if (!response.ok) {
18
+ throw new SourceError("twelvedata", `HTTP ${response.status}`);
19
+ }
20
+ const data = await response.json();
21
+ if (data.status === "error") {
22
+ if (data.code === 429) {
23
+ throw new RateLimitError("twelvedata", data.message);
24
+ }
25
+ throw new SourceError("twelvedata", data.message ?? "Unknown error");
26
+ }
27
+ const price = Number(data.price);
28
+ if (!Number.isFinite(price)) {
29
+ throw new SourceError("twelvedata", `No data for symbol "${symbol}"`);
30
+ }
31
+ return {
32
+ symbol: symbol.toUpperCase(),
33
+ assetClass: "stock",
34
+ price,
35
+ currency: "USD",
36
+ timestamp: Date.now(),
37
+ source: "twelvedata",
38
+ };
39
+ },
40
+ async getHistorical(symbol, query, apiKey) {
41
+ if (!apiKey) {
42
+ throw new MissingApiKeyError("twelvedata");
43
+ }
44
+ const startDate = toDateString(query.from);
45
+ const endDate = toDateString(query.to);
46
+ const url = `https://api.twelvedata.com/time_series?symbol=${encodeURIComponent(symbol)}&interval=1day&start_date=${startDate}&end_date=${endDate}&apikey=${apiKey}`;
47
+ const response = await fetchImpl(url);
48
+ if (response.status === 429) {
49
+ throw new RateLimitError("twelvedata");
50
+ }
51
+ if (!response.ok) {
52
+ throw new SourceError("twelvedata", `HTTP ${response.status}`);
53
+ }
54
+ const data = await response.json();
55
+ if (data.status === "error") {
56
+ if (data.code === 429) {
57
+ throw new RateLimitError("twelvedata", data.message);
58
+ }
59
+ throw new SourceError("twelvedata", data.message ?? "Unknown error");
60
+ }
61
+ const values = data.values ?? [];
62
+ if (values.length === 0) {
63
+ throw new SourceError("twelvedata", `No historical data for symbol "${symbol}"`);
64
+ }
65
+ const candles = values
66
+ .map((value) => ({
67
+ timestamp: Date.parse(`${value.datetime}T00:00:00Z`),
68
+ open: Number(value.open),
69
+ high: Number(value.high),
70
+ low: Number(value.low),
71
+ close: Number(value.close),
72
+ volume: Number(value.volume),
73
+ }))
74
+ .filter((candle) => Number.isFinite(candle.close))
75
+ .sort((a, b) => a.timestamp - b.timestamp);
76
+ return { symbol: symbol.toUpperCase(), assetClass: "stock", source: "twelvedata", candles };
77
+ },
78
+ };
79
+ }
80
+ //# sourceMappingURL=twelveData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twelveData.js","sourceRoot":"","sources":["../../src/sources/twelveData.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,UAAU,sBAAsB,CAAC,YAAuB,KAAK;IACjE,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,YAAY,EAAE,CAAC,OAAO,CAAC;QACvB,cAAc,EAAE,IAAI;QACpB,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;YAC3B,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAC7C,CAAC;YACD,MAAM,GAAG,GAAG,2CAA2C,kBAAkB,CAAC,MAAM,CAAC,WAAW,MAAM,EAAE,CAAC;YACrG,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,IAAI,cAAc,CAAC,YAAY,CAAC,CAAC;YACzC,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,IAAI,GAAQ,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;oBACtB,MAAM,IAAI,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvD,CAAC;gBACD,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC;YACvE,CAAC;YACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACjC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,uBAAuB,MAAM,GAAG,CAAC,CAAC;YACxE,CAAC;YACD,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE;gBAC5B,UAAU,EAAE,OAAO;gBACnB,KAAK;gBACL,QAAQ,EAAE,KAAK;gBACf,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;gBACrB,MAAM,EAAE,YAAY;aACrB,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM;YACvC,IAAI,CAAC,MAAM,EAAE,CAAC;gBACZ,MAAM,IAAI,kBAAkB,CAAC,YAAY,CAAC,CAAC;YAC7C,CAAC;YACD,MAAM,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3C,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;YACvC,MAAM,GAAG,GAAG,iDAAiD,kBAAkB,CAC7E,MAAM,CACP,6BAA6B,SAAS,aAAa,OAAO,WAAW,MAAM,EAAE,CAAC;YAC/E,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,CAAC;YACtC,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,IAAI,cAAc,CAAC,YAAY,CAAC,CAAC;YACzC,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,QAAQ,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,IAAI,GAAQ,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,EAAE,CAAC;gBAC5B,IAAI,IAAI,CAAC,IAAI,KAAK,GAAG,EAAE,CAAC;oBACtB,MAAM,IAAI,cAAc,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;gBACvD,CAAC;gBACD,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,IAAI,eAAe,CAAC,CAAC;YACvE,CAAC;YACD,MAAM,MAAM,GAAU,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;YACxC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACxB,MAAM,IAAI,WAAW,CAAC,YAAY,EAAE,kCAAkC,MAAM,GAAG,CAAC,CAAC;YACnF,CAAC;YACD,MAAM,OAAO,GAAa,MAAM;iBAC7B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;gBACf,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,QAAQ,YAAY,CAAC;gBACpD,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBACxB,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBACxB,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;gBACtB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC;gBAC1B,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;aAC7B,CAAC,CAAC;iBACF,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;iBACjD,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC;YAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;QAC9F,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { FetchLike, QuoteSource } from "../types.js";
2
+ export declare function createYahooSource(fetchImpl?: FetchLike): QuoteSource;
3
+ //# sourceMappingURL=yahoo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yahoo.d.ts","sourceRoot":"","sources":["../../src/sources/yahoo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAU,MAAM,aAAa,CAAC;AAIlE,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,SAAiB,GAAG,WAAW,CAiE3E"}
@@ -0,0 +1,65 @@
1
+ import { RateLimitError, SourceError } from "../errors.js";
2
+ import { toSeconds } from "../time.js";
3
+ export function createYahooSource(fetchImpl = fetch) {
4
+ return {
5
+ name: "yahoo",
6
+ assetClasses: ["stock"],
7
+ requiresApiKey: false,
8
+ async getQuote(symbol) {
9
+ const url = `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(symbol)}`;
10
+ const response = await fetchImpl(url);
11
+ if (response.status === 429) {
12
+ throw new RateLimitError("yahoo");
13
+ }
14
+ if (!response.ok) {
15
+ throw new SourceError("yahoo", `HTTP ${response.status}`);
16
+ }
17
+ const data = await response.json();
18
+ const result = data?.chart?.result?.[0];
19
+ const price = result?.meta?.regularMarketPrice;
20
+ const currency = result?.meta?.currency;
21
+ if (typeof price !== "number") {
22
+ throw new SourceError("yahoo", `No data for symbol "${symbol}"`);
23
+ }
24
+ return {
25
+ symbol: symbol.toUpperCase(),
26
+ assetClass: "stock",
27
+ price,
28
+ currency: currency ?? "USD",
29
+ timestamp: Date.now(),
30
+ source: "yahoo",
31
+ };
32
+ },
33
+ async getHistorical(symbol, query) {
34
+ const period1 = toSeconds(query.from);
35
+ const period2 = toSeconds(query.to);
36
+ const url = `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(symbol)}?period1=${period1}&period2=${period2}&interval=1d`;
37
+ const response = await fetchImpl(url);
38
+ if (response.status === 429) {
39
+ throw new RateLimitError("yahoo");
40
+ }
41
+ if (!response.ok) {
42
+ throw new SourceError("yahoo", `HTTP ${response.status}`);
43
+ }
44
+ const data = await response.json();
45
+ const result = data?.chart?.result?.[0];
46
+ const timestamps = result?.timestamp ?? [];
47
+ const quote = result?.indicators?.quote?.[0];
48
+ if (!result || timestamps.length === 0 || !quote) {
49
+ throw new SourceError("yahoo", `No historical data for symbol "${symbol}"`);
50
+ }
51
+ const candles = timestamps
52
+ .map((timestampSeconds, i) => ({
53
+ timestamp: timestampSeconds * 1000,
54
+ open: quote.open?.[i],
55
+ high: quote.high?.[i],
56
+ low: quote.low?.[i],
57
+ close: quote.close?.[i],
58
+ volume: quote.volume?.[i],
59
+ }))
60
+ .filter((candle) => Number.isFinite(candle.close));
61
+ return { symbol: symbol.toUpperCase(), assetClass: "stock", source: "yahoo", candles };
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=yahoo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yahoo.js","sourceRoot":"","sources":["../../src/sources/yahoo.ts"],"names":[],"mappings":"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"}
package/dist/time.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ export declare function toMs(value: Date | number): number;
2
+ export declare function toSeconds(value: Date | number): number;
3
+ export declare function toDateString(value: Date | number): string;
4
+ //# sourceMappingURL=time.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAAA,wBAAgB,IAAI,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAEtD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAEzD"}
package/dist/time.js ADDED
@@ -0,0 +1,10 @@
1
+ export function toMs(value) {
2
+ return value instanceof Date ? value.getTime() : value;
3
+ }
4
+ export function toSeconds(value) {
5
+ return Math.floor(toMs(value) / 1000);
6
+ }
7
+ export function toDateString(value) {
8
+ return new Date(toMs(value)).toISOString().slice(0, 10);
9
+ }
10
+ //# sourceMappingURL=time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time.js","sourceRoot":"","sources":["../src/time.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,IAAI,CAAC,KAAoB;IACvC,OAAO,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAoB;IAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAoB;IAC/C,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,47 @@
1
+ export type AssetClass = "stock" | "crypto";
2
+ export interface Quote {
3
+ symbol: string;
4
+ assetClass: AssetClass;
5
+ price: number;
6
+ currency: string;
7
+ timestamp: number;
8
+ source: string;
9
+ }
10
+ export interface FetchResponse {
11
+ readonly ok: boolean;
12
+ readonly status: number;
13
+ json(): Promise<any>;
14
+ text(): Promise<string>;
15
+ }
16
+ export type FetchLike = (url: string, init?: RequestInit) => Promise<FetchResponse>;
17
+ export interface Candle {
18
+ /** Start of the period, ms since epoch. */
19
+ timestamp: number;
20
+ open: number;
21
+ high: number;
22
+ low: number;
23
+ close: number;
24
+ /** Not every free-tier source exposes volume (e.g. CoinGecko's OHLC endpoint). */
25
+ volume?: number;
26
+ }
27
+ export interface HistoricalQuery {
28
+ /** Inclusive start of the range. */
29
+ from: Date | number;
30
+ /** Inclusive end of the range. */
31
+ to: Date | number;
32
+ }
33
+ export interface HistoricalResult {
34
+ symbol: string;
35
+ assetClass: AssetClass;
36
+ source: string;
37
+ candles: Candle[];
38
+ }
39
+ export interface QuoteSource {
40
+ name: string;
41
+ assetClasses: AssetClass[];
42
+ requiresApiKey: boolean;
43
+ getQuote(symbol: string, apiKey?: string): Promise<Quote>;
44
+ /** Not every source implements this — daily OHLCV history only, for now. */
45
+ getHistorical?(symbol: string, query: HistoricalQuery, apiKey?: string): Promise<HistoricalResult>;
46
+ }
47
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE5C,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IACrB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CACzB;AAED,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;AAEpF,MAAM,WAAW,MAAM;IACrB,2CAA2C;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,oCAAoC;IACpC,IAAI,EAAE,IAAI,GAAG,MAAM,CAAC;IACpB,kCAAkC;IAClC,EAAE,EAAE,IAAI,GAAG,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1D,4EAA4E;IAC5E,aAAa,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACpG"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@lyniaca/lynstock",
3
+ "version": "0.1.0",
4
+ "description": "Normalized, multi-source market data aggregator for stocks and crypto with automatic fallback across upstream providers.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "README.md"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
25
+ "live-check": "npm run build && node scripts/live-check.mjs"
26
+ },
27
+ "keywords": [
28
+ "stocks",
29
+ "crypto",
30
+ "market-data",
31
+ "finance",
32
+ "quotes",
33
+ "historical",
34
+ "ohlcv"
35
+ ],
36
+ "author": "Lyniaca",
37
+ "license": "MIT",
38
+ "devDependencies": {
39
+ "@types/node": "^20.16.11",
40
+ "typescript": "^5.6.3",
41
+ "vitest": "^2.1.4"
42
+ }
43
+ }