market-feed 0.5.0 → 0.6.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 +81 -0
- package/README.md +2 -2
- package/dist/cli.js +323 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +220 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +219 -1
- package/dist/index.js.map +1 -1
- package/dist/portfolio.cjs.map +1 -1
- package/dist/portfolio.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,86 @@
|
|
|
1
1
|
# market-feed Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.0 — 2026-03-12
|
|
4
|
+
|
|
5
|
+
### New provider
|
|
6
|
+
|
|
7
|
+
**`TwelveDataProvider`** — Twelve Data (free tier: 800 credits/day, 8 calls/minute).
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { MarketFeed, TwelveDataProvider } from "market-feed";
|
|
11
|
+
|
|
12
|
+
const feed = new MarketFeed([
|
|
13
|
+
new TwelveDataProvider({ apiKey: process.env.TWELVE_DATA_KEY! }),
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const quote = await feed.quote(["AAPL", "BTC/USD", "EUR/USD"]);
|
|
17
|
+
const bars = await feed.historical("AAPL", { interval: "1wk" });
|
|
18
|
+
const results = await feed.search("apple");
|
|
19
|
+
const profile = await feed.company("AAPL");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Supports: `quote`, `historical`, `search`, `company`.
|
|
23
|
+
|
|
24
|
+
Strong coverage for global equities, forex, and crypto pairs. Symbol normalisation handles all common formats:
|
|
25
|
+
- US stocks: `AAPL` (unchanged)
|
|
26
|
+
- Crypto: `BTC-USD` / `BTC/USD` / `X:BTCUSD` → `BTC/USD`
|
|
27
|
+
- Forex: `EURUSD=X` / `C:EURUSD` → `EUR/USD`
|
|
28
|
+
|
|
29
|
+
Free plan sign-up: https://twelvedata.com
|
|
30
|
+
|
|
31
|
+
#### Interval mapping
|
|
32
|
+
|
|
33
|
+
| market-feed | Twelve Data |
|
|
34
|
+
|-------------|-------------|
|
|
35
|
+
| `1m` | `1min` |
|
|
36
|
+
| `5m` | `5min` |
|
|
37
|
+
| `15m` | `15min` |
|
|
38
|
+
| `30m` | `30min` |
|
|
39
|
+
| `1h` | `1h` |
|
|
40
|
+
| `1d` | `1day` |
|
|
41
|
+
| `1wk` | `1week` |
|
|
42
|
+
| `1mo` | `1month` |
|
|
43
|
+
|
|
44
|
+
### New utility
|
|
45
|
+
|
|
46
|
+
**`toTwelveDataSymbol(symbol)`** — exported from main `market-feed` entry point. Converts any supported symbol format (Yahoo/Polygon/standard) to the slash-pair notation that Twelve Data expects for crypto and forex.
|
|
47
|
+
|
|
48
|
+
### CLI
|
|
49
|
+
|
|
50
|
+
`--td-key <key>` flag adds `TwelveDataProvider` to the CLI provider chain.
|
|
51
|
+
|
|
52
|
+
### Breaking changes
|
|
53
|
+
|
|
54
|
+
None. All v0.5.x imports continue to work unchanged.
|
|
55
|
+
|
|
56
|
+
### Other changes
|
|
57
|
+
|
|
58
|
+
- `TwelveDataProvider` and `TwelveDataProviderOptions` exported from main `market-feed` entry point
|
|
59
|
+
- `toTwelveDataSymbol` exported from main `market-feed` entry point
|
|
60
|
+
- 30 new unit tests (456 total across 22 test files)
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 0.5.1 — 2026-03-12
|
|
65
|
+
|
|
66
|
+
### CLI additions
|
|
67
|
+
|
|
68
|
+
Three new commands expose the v0.5.0 corporate-action data from the terminal:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
market-feed earnings AAPL --limit 8
|
|
72
|
+
market-feed dividends AAPL --from 2020-01-01
|
|
73
|
+
market-feed splits AAPL --json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
New flags `--from <date>` and `--to <date>` scope the dividend/split history by date range (ISO 8601). `--limit` and `--json` work on all three commands.
|
|
77
|
+
|
|
78
|
+
### Breaking changes
|
|
79
|
+
|
|
80
|
+
None.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
3
84
|
## 0.5.0 — 2026-03-11
|
|
4
85
|
|
|
5
86
|
### New modules
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# market-feed
|
|
2
2
|
|
|
3
3
|
> Unified TypeScript client for financial market data.
|
|
4
|
-
> Wraps Yahoo Finance, Alpha Vantage, Polygon.io, and
|
|
4
|
+
> Wraps Yahoo Finance, Alpha Vantage, Polygon.io, Finnhub, and Twelve Data under one consistent interface — with caching and automatic fallback built in.
|
|
5
5
|
|
|
6
6
|
[](https://github.com/piyushgupta344/market-feed/actions/workflows/ci.yml)
|
|
7
7
|
[](https://www.npmjs.com/package/market-feed)
|
|
@@ -39,7 +39,7 @@ const quote = await feed.quote("AAPL");
|
|
|
39
39
|
console.log(quote.price); // always a number, always the same key
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
One interface.
|
|
42
|
+
One interface. Five providers. Zero API key required for Yahoo Finance.
|
|
43
43
|
|
|
44
44
|
---
|
|
45
45
|
|
package/dist/cli.js
CHANGED
|
@@ -1585,6 +1585,206 @@ function subtractOneYear3() {
|
|
|
1585
1585
|
return d;
|
|
1586
1586
|
}
|
|
1587
1587
|
|
|
1588
|
+
// src/providers/twelve-data/transform.ts
|
|
1589
|
+
var PROVIDER5 = "twelve-data";
|
|
1590
|
+
function transformQuote5(data, raw) {
|
|
1591
|
+
return {
|
|
1592
|
+
symbol: data.symbol,
|
|
1593
|
+
name: data.name,
|
|
1594
|
+
price: Number(data.close),
|
|
1595
|
+
change: Number(data.change),
|
|
1596
|
+
changePercent: Number(data.percent_change),
|
|
1597
|
+
open: Number(data.open),
|
|
1598
|
+
high: Number(data.high),
|
|
1599
|
+
low: Number(data.low),
|
|
1600
|
+
close: Number(data.close),
|
|
1601
|
+
previousClose: Number(data.previous_close),
|
|
1602
|
+
volume: Number(data.volume),
|
|
1603
|
+
...data.average_volume !== void 0 ? { avgVolume: Number(data.average_volume) } : {},
|
|
1604
|
+
...data.fifty_two_week !== void 0 ? {
|
|
1605
|
+
fiftyTwoWeekHigh: Number(data.fifty_two_week.high),
|
|
1606
|
+
fiftyTwoWeekLow: Number(data.fifty_two_week.low)
|
|
1607
|
+
} : {},
|
|
1608
|
+
currency: data.currency,
|
|
1609
|
+
exchange: data.exchange,
|
|
1610
|
+
timestamp: new Date(data.timestamp * 1e3),
|
|
1611
|
+
provider: PROVIDER5,
|
|
1612
|
+
...raw !== void 0 ? { raw } : {}
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
function transformHistoricalBar2(bar, raw) {
|
|
1616
|
+
return {
|
|
1617
|
+
date: new Date(bar.datetime),
|
|
1618
|
+
open: Number(bar.open),
|
|
1619
|
+
high: Number(bar.high),
|
|
1620
|
+
low: Number(bar.low),
|
|
1621
|
+
close: Number(bar.close),
|
|
1622
|
+
volume: Number(bar.volume),
|
|
1623
|
+
...raw !== void 0 ? { raw } : {}
|
|
1624
|
+
};
|
|
1625
|
+
}
|
|
1626
|
+
function transformSearch5(result, raw) {
|
|
1627
|
+
return {
|
|
1628
|
+
symbol: result.symbol,
|
|
1629
|
+
name: result.instrument_name,
|
|
1630
|
+
type: mapInstrumentType(result.instrument_type),
|
|
1631
|
+
exchange: result.exchange || void 0,
|
|
1632
|
+
provider: PROVIDER5,
|
|
1633
|
+
...raw !== void 0 ? { raw } : {}
|
|
1634
|
+
};
|
|
1635
|
+
}
|
|
1636
|
+
function transformProfile(data, raw) {
|
|
1637
|
+
return {
|
|
1638
|
+
symbol: data.symbol,
|
|
1639
|
+
name: data.name,
|
|
1640
|
+
description: data.description || void 0,
|
|
1641
|
+
sector: data.sector || void 0,
|
|
1642
|
+
industry: data.industry || void 0,
|
|
1643
|
+
country: data.country || void 0,
|
|
1644
|
+
employees: data.employees || void 0,
|
|
1645
|
+
website: data.website || void 0,
|
|
1646
|
+
ceo: data.CEO || void 0,
|
|
1647
|
+
exchange: data.exchange || void 0,
|
|
1648
|
+
provider: PROVIDER5,
|
|
1649
|
+
...raw !== void 0 ? { raw } : {}
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
function mapInstrumentType(type) {
|
|
1653
|
+
const t = type.toLowerCase();
|
|
1654
|
+
if (t.includes("common stock") || t === "cs") return "stock";
|
|
1655
|
+
if (t.includes("etf") || t.includes("exchange traded fund")) return "etf";
|
|
1656
|
+
if (t.includes("crypto")) return "crypto";
|
|
1657
|
+
if (t.includes("forex") || t.includes("currency")) return "forex";
|
|
1658
|
+
if (t.includes("mutual fund") || t.includes("fund")) return "mutual-fund";
|
|
1659
|
+
if (t.includes("future")) return "future";
|
|
1660
|
+
if (t.includes("index")) return "index";
|
|
1661
|
+
return "unknown";
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// src/providers/twelve-data/index.ts
|
|
1665
|
+
var TwelveDataProvider = class {
|
|
1666
|
+
constructor(options) {
|
|
1667
|
+
this.options = options;
|
|
1668
|
+
this.apiKey = options.apiKey;
|
|
1669
|
+
this.http = new HttpClient("twelve-data", {
|
|
1670
|
+
baseUrl: "https://api.twelvedata.com",
|
|
1671
|
+
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
|
|
1672
|
+
...options.retries !== void 0 ? { retries: options.retries } : {}
|
|
1673
|
+
});
|
|
1674
|
+
this.limiter = options.rateLimiter ?? new RateLimiter("twelve-data", 8, 8 / 60);
|
|
1675
|
+
}
|
|
1676
|
+
name = "twelve-data";
|
|
1677
|
+
http;
|
|
1678
|
+
limiter;
|
|
1679
|
+
apiKey;
|
|
1680
|
+
// ---------------------------------------------------------------------------
|
|
1681
|
+
// Quote
|
|
1682
|
+
// ---------------------------------------------------------------------------
|
|
1683
|
+
async quote(symbols, options) {
|
|
1684
|
+
return Promise.all(symbols.map((s) => this.fetchSingleQuote(s, options)));
|
|
1685
|
+
}
|
|
1686
|
+
async fetchSingleQuote(symbol, options) {
|
|
1687
|
+
this.limiter.consume();
|
|
1688
|
+
const s = normalise(symbol);
|
|
1689
|
+
const data = await this.http.get("/quote", {
|
|
1690
|
+
params: { symbol: s, apikey: this.apiKey }
|
|
1691
|
+
});
|
|
1692
|
+
if (data.code !== void 0) {
|
|
1693
|
+
throw new ProviderError(
|
|
1694
|
+
data.message ?? `No quote data for symbol "${s}"`,
|
|
1695
|
+
this.name,
|
|
1696
|
+
data.code
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1699
|
+
return transformQuote5(data, options?.raw ? data : void 0);
|
|
1700
|
+
}
|
|
1701
|
+
// ---------------------------------------------------------------------------
|
|
1702
|
+
// Historical (time series)
|
|
1703
|
+
// ---------------------------------------------------------------------------
|
|
1704
|
+
async historical(symbol, options) {
|
|
1705
|
+
this.limiter.consume();
|
|
1706
|
+
const s = normalise(symbol);
|
|
1707
|
+
const interval = toInterval(options?.interval ?? "1d");
|
|
1708
|
+
const params = {
|
|
1709
|
+
symbol: s,
|
|
1710
|
+
interval,
|
|
1711
|
+
outputsize: 5e3,
|
|
1712
|
+
apikey: this.apiKey
|
|
1713
|
+
};
|
|
1714
|
+
if (options?.period1 !== void 0) {
|
|
1715
|
+
params["start_date"] = typeof options.period1 === "string" ? options.period1 : options.period1.toISOString().slice(0, 10);
|
|
1716
|
+
}
|
|
1717
|
+
if (options?.period2 !== void 0) {
|
|
1718
|
+
params["end_date"] = typeof options.period2 === "string" ? options.period2 : options.period2.toISOString().slice(0, 10);
|
|
1719
|
+
}
|
|
1720
|
+
const data = await this.http.get("/time_series", { params });
|
|
1721
|
+
if (data.code !== void 0 || data.status === "error") {
|
|
1722
|
+
throw new ProviderError(
|
|
1723
|
+
data.message ?? `No historical data for symbol "${s}"`,
|
|
1724
|
+
this.name,
|
|
1725
|
+
data.code
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
if (!Array.isArray(data.values) || data.values.length === 0) {
|
|
1729
|
+
throw new ProviderError(`No historical data for symbol "${s}"`, this.name);
|
|
1730
|
+
}
|
|
1731
|
+
return [...data.values].reverse().map((bar) => transformHistoricalBar2(bar, options?.raw ? bar : void 0));
|
|
1732
|
+
}
|
|
1733
|
+
// ---------------------------------------------------------------------------
|
|
1734
|
+
// Search
|
|
1735
|
+
// ---------------------------------------------------------------------------
|
|
1736
|
+
async search(query, options) {
|
|
1737
|
+
this.limiter.consume();
|
|
1738
|
+
const limit = options?.limit ?? 10;
|
|
1739
|
+
const data = await this.http.get("/symbol_search", {
|
|
1740
|
+
params: { symbol: query, apikey: this.apiKey }
|
|
1741
|
+
});
|
|
1742
|
+
if (data.code !== void 0 || data.status === "error") {
|
|
1743
|
+
return [];
|
|
1744
|
+
}
|
|
1745
|
+
return (data.data ?? []).slice(0, limit).map((r) => transformSearch5(r, options?.raw ? r : void 0));
|
|
1746
|
+
}
|
|
1747
|
+
// ---------------------------------------------------------------------------
|
|
1748
|
+
// Company (profile)
|
|
1749
|
+
// ---------------------------------------------------------------------------
|
|
1750
|
+
async company(symbol, options) {
|
|
1751
|
+
this.limiter.consume();
|
|
1752
|
+
const s = normalise(symbol);
|
|
1753
|
+
const data = await this.http.get("/profile", {
|
|
1754
|
+
params: { symbol: s, apikey: this.apiKey }
|
|
1755
|
+
});
|
|
1756
|
+
if (data.code !== void 0 || !data.name) {
|
|
1757
|
+
throw new ProviderError(
|
|
1758
|
+
data.message ?? `No company data for symbol "${s}"`,
|
|
1759
|
+
this.name,
|
|
1760
|
+
data.code
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
return transformProfile(data, options?.raw ? data : void 0);
|
|
1764
|
+
}
|
|
1765
|
+
};
|
|
1766
|
+
function toInterval(interval) {
|
|
1767
|
+
const map = {
|
|
1768
|
+
"1m": "1min",
|
|
1769
|
+
"2m": "2min",
|
|
1770
|
+
"5m": "5min",
|
|
1771
|
+
"15m": "15min",
|
|
1772
|
+
"30m": "30min",
|
|
1773
|
+
"45m": "45min",
|
|
1774
|
+
"60m": "1h",
|
|
1775
|
+
"90m": "90min",
|
|
1776
|
+
"1h": "1h",
|
|
1777
|
+
"2h": "2h",
|
|
1778
|
+
"4h": "4h",
|
|
1779
|
+
"1d": "1day",
|
|
1780
|
+
"5d": "1week",
|
|
1781
|
+
"1wk": "1week",
|
|
1782
|
+
"1mo": "1month",
|
|
1783
|
+
"3mo": "3month"
|
|
1784
|
+
};
|
|
1785
|
+
return map[interval] ?? "1day";
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1588
1788
|
// src/cli/index.ts
|
|
1589
1789
|
var HELP = `
|
|
1590
1790
|
Usage: market-feed <command> [options]
|
|
@@ -1595,16 +1795,22 @@ Commands:
|
|
|
1595
1795
|
search <query> Search for symbols
|
|
1596
1796
|
company <symbol> Fetch company profile
|
|
1597
1797
|
news <symbol> Fetch recent news
|
|
1798
|
+
earnings <symbol> Fetch EPS history (actuals vs. estimates)
|
|
1799
|
+
dividends <symbol> Fetch cash dividend history
|
|
1800
|
+
splits <symbol> Fetch stock split history
|
|
1598
1801
|
|
|
1599
1802
|
Options:
|
|
1600
1803
|
--av-key <key> Alpha Vantage API key
|
|
1601
1804
|
--polygon-key <key> Polygon.io API key
|
|
1602
1805
|
--finnhub-key <key> Finnhub API key
|
|
1806
|
+
--td-key <key> Twelve Data API key
|
|
1603
1807
|
--json Output raw JSON
|
|
1604
1808
|
--limit <n> Limit results (default: 10)
|
|
1605
1809
|
--interval <i> Historical interval: 1m 5m 15m 30m 1h 1d 1wk 1mo (default: 1d)
|
|
1606
1810
|
--period1 <date> Historical start date (ISO 8601)
|
|
1607
1811
|
--period2 <date> Historical end date (ISO 8601)
|
|
1812
|
+
--from <date> Dividends/splits start date (ISO 8601)
|
|
1813
|
+
--to <date> Dividends/splits end date (ISO 8601)
|
|
1608
1814
|
-h, --help Show this help message
|
|
1609
1815
|
|
|
1610
1816
|
Examples:
|
|
@@ -1613,6 +1819,9 @@ Examples:
|
|
|
1613
1819
|
market-feed search "apple inc"
|
|
1614
1820
|
market-feed company AAPL --json
|
|
1615
1821
|
market-feed news AAPL --limit 5
|
|
1822
|
+
market-feed earnings AAPL --limit 8
|
|
1823
|
+
market-feed dividends AAPL --from 2020-01-01
|
|
1824
|
+
market-feed splits AAPL --json
|
|
1616
1825
|
`.trim();
|
|
1617
1826
|
function parseArgs(argv) {
|
|
1618
1827
|
const args = argv.slice(2);
|
|
@@ -1641,6 +1850,9 @@ function parseArgs(argv) {
|
|
|
1641
1850
|
} else if (arg === "--finnhub-key") {
|
|
1642
1851
|
result.finnhubKey = args[++i];
|
|
1643
1852
|
i++;
|
|
1853
|
+
} else if (arg === "--td-key") {
|
|
1854
|
+
result.tdKey = args[++i];
|
|
1855
|
+
i++;
|
|
1644
1856
|
} else if (arg === "--limit") {
|
|
1645
1857
|
result.limit = Number(args[++i]) || 10;
|
|
1646
1858
|
i++;
|
|
@@ -1653,6 +1865,12 @@ function parseArgs(argv) {
|
|
|
1653
1865
|
} else if (arg === "--period2") {
|
|
1654
1866
|
result.period2 = args[++i];
|
|
1655
1867
|
i++;
|
|
1868
|
+
} else if (arg === "--from") {
|
|
1869
|
+
result.from = args[++i];
|
|
1870
|
+
i++;
|
|
1871
|
+
} else if (arg === "--to") {
|
|
1872
|
+
result.to = args[++i];
|
|
1873
|
+
i++;
|
|
1656
1874
|
} else if (!arg.startsWith("-") && !result.command) {
|
|
1657
1875
|
result.command = arg;
|
|
1658
1876
|
i++;
|
|
@@ -1666,12 +1884,11 @@ function parseArgs(argv) {
|
|
|
1666
1884
|
return result;
|
|
1667
1885
|
}
|
|
1668
1886
|
function buildFeed(args) {
|
|
1669
|
-
const providers = [
|
|
1670
|
-
new YahooProvider()
|
|
1671
|
-
];
|
|
1887
|
+
const providers = [new YahooProvider()];
|
|
1672
1888
|
if (args.avKey) providers.push(new AlphaVantageProvider({ apiKey: args.avKey }));
|
|
1673
1889
|
if (args.polygonKey) providers.push(new PolygonProvider({ apiKey: args.polygonKey }));
|
|
1674
1890
|
if (args.finnhubKey) providers.push(new FinnhubProvider({ apiKey: args.finnhubKey }));
|
|
1891
|
+
if (args.tdKey) providers.push(new TwelveDataProvider({ apiKey: args.tdKey }));
|
|
1675
1892
|
return new MarketFeed({ providers });
|
|
1676
1893
|
}
|
|
1677
1894
|
function pad(s, n) {
|
|
@@ -1771,6 +1988,82 @@ async function runNews(feed, symbol, args) {
|
|
|
1771
1988
|
console.log("");
|
|
1772
1989
|
}
|
|
1773
1990
|
}
|
|
1991
|
+
async function runEarnings(feed, symbol, args) {
|
|
1992
|
+
const events = await feed.earnings(symbol, { limit: args.limit });
|
|
1993
|
+
if (args.json) {
|
|
1994
|
+
console.log(JSON.stringify(events, null, 2));
|
|
1995
|
+
return;
|
|
1996
|
+
}
|
|
1997
|
+
if (events.length === 0) {
|
|
1998
|
+
console.log("No earnings data found.");
|
|
1999
|
+
return;
|
|
2000
|
+
}
|
|
2001
|
+
console.log(
|
|
2002
|
+
`${pad("Date", 12)} ${pad("Period", 12)} ${pad("Actual", 9)} ${pad("Estimate", 9)} ${pad("Surprise%", 10)} Provider`
|
|
2003
|
+
);
|
|
2004
|
+
console.log("-".repeat(70));
|
|
2005
|
+
for (const e of events) {
|
|
2006
|
+
const date = e.date.toISOString().slice(0, 10);
|
|
2007
|
+
const period = e.period ?? "-";
|
|
2008
|
+
const actual = e.epsActual !== void 0 ? fmtNum(e.epsActual, 2) : "-";
|
|
2009
|
+
const estimate = e.epsEstimate !== void 0 ? fmtNum(e.epsEstimate, 2) : "-";
|
|
2010
|
+
const surprise = e.epsSurprisePct !== void 0 ? `${e.epsSurprisePct >= 0 ? "+" : ""}${fmtNum(e.epsSurprisePct, 2)}%` : "-";
|
|
2011
|
+
console.log(
|
|
2012
|
+
`${pad(date, 12)} ${pad(period, 12)} ${pad(actual, 9)} ${pad(estimate, 9)} ${pad(surprise, 10)} ${e.provider}`
|
|
2013
|
+
);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
async function runDividends(feed, symbol, args) {
|
|
2017
|
+
const events = await feed.dividends(symbol, {
|
|
2018
|
+
limit: args.limit,
|
|
2019
|
+
...args.from ? { from: args.from } : {},
|
|
2020
|
+
...args.to ? { to: args.to } : {}
|
|
2021
|
+
});
|
|
2022
|
+
if (args.json) {
|
|
2023
|
+
console.log(JSON.stringify(events, null, 2));
|
|
2024
|
+
return;
|
|
2025
|
+
}
|
|
2026
|
+
if (events.length === 0) {
|
|
2027
|
+
console.log("No dividend data found.");
|
|
2028
|
+
return;
|
|
2029
|
+
}
|
|
2030
|
+
console.log(
|
|
2031
|
+
`${pad("Ex Date", 12)} ${pad("Amount", 9)} ${pad("Frequency", 14)} ${pad("Pay Date", 12)} Provider`
|
|
2032
|
+
);
|
|
2033
|
+
console.log("-".repeat(66));
|
|
2034
|
+
for (const e of events) {
|
|
2035
|
+
const exDate = e.exDate.toISOString().slice(0, 10);
|
|
2036
|
+
const amount = `$${fmtNum(e.amount, 4)}`;
|
|
2037
|
+
const freq = e.frequency ?? "-";
|
|
2038
|
+
const payDate = e.payDate ? e.payDate.toISOString().slice(0, 10) : "-";
|
|
2039
|
+
console.log(
|
|
2040
|
+
`${pad(exDate, 12)} ${pad(amount, 9)} ${pad(freq, 14)} ${pad(payDate, 12)} ${e.provider}`
|
|
2041
|
+
);
|
|
2042
|
+
}
|
|
2043
|
+
}
|
|
2044
|
+
async function runSplits(feed, symbol, args) {
|
|
2045
|
+
const events = await feed.splits(symbol, {
|
|
2046
|
+
limit: args.limit,
|
|
2047
|
+
...args.from ? { from: args.from } : {},
|
|
2048
|
+
...args.to ? { to: args.to } : {}
|
|
2049
|
+
});
|
|
2050
|
+
if (args.json) {
|
|
2051
|
+
console.log(JSON.stringify(events, null, 2));
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
if (events.length === 0) {
|
|
2055
|
+
console.log("No split data found.");
|
|
2056
|
+
return;
|
|
2057
|
+
}
|
|
2058
|
+
console.log(`${pad("Date", 12)} ${pad("Ratio", 8)} ${pad("Description", 14)} Provider`);
|
|
2059
|
+
console.log("-".repeat(50));
|
|
2060
|
+
for (const e of events) {
|
|
2061
|
+
const date = e.date.toISOString().slice(0, 10);
|
|
2062
|
+
const ratio = fmtNum(e.ratio, 4);
|
|
2063
|
+
const desc = e.description ?? "-";
|
|
2064
|
+
console.log(`${pad(date, 12)} ${pad(ratio, 8)} ${pad(desc, 14)} ${e.provider}`);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
1774
2067
|
async function main() {
|
|
1775
2068
|
const args = parseArgs(process.argv);
|
|
1776
2069
|
if (!args.command || args.command === "help") {
|
|
@@ -1825,6 +2118,33 @@ async function main() {
|
|
|
1825
2118
|
await runNews(feed, symbol, args);
|
|
1826
2119
|
break;
|
|
1827
2120
|
}
|
|
2121
|
+
case "earnings": {
|
|
2122
|
+
const [symbol] = args.positionals;
|
|
2123
|
+
if (!symbol) {
|
|
2124
|
+
console.error("Error: earnings requires a symbol\n");
|
|
2125
|
+
process.exit(1);
|
|
2126
|
+
}
|
|
2127
|
+
await runEarnings(feed, symbol, args);
|
|
2128
|
+
break;
|
|
2129
|
+
}
|
|
2130
|
+
case "dividends": {
|
|
2131
|
+
const [symbol] = args.positionals;
|
|
2132
|
+
if (!symbol) {
|
|
2133
|
+
console.error("Error: dividends requires a symbol\n");
|
|
2134
|
+
process.exit(1);
|
|
2135
|
+
}
|
|
2136
|
+
await runDividends(feed, symbol, args);
|
|
2137
|
+
break;
|
|
2138
|
+
}
|
|
2139
|
+
case "splits": {
|
|
2140
|
+
const [symbol] = args.positionals;
|
|
2141
|
+
if (!symbol) {
|
|
2142
|
+
console.error("Error: splits requires a symbol\n");
|
|
2143
|
+
process.exit(1);
|
|
2144
|
+
}
|
|
2145
|
+
await runSplits(feed, symbol, args);
|
|
2146
|
+
break;
|
|
2147
|
+
}
|
|
1828
2148
|
default: {
|
|
1829
2149
|
console.error(`Unknown command: "${args.command}"
|
|
1830
2150
|
`);
|