chore: 初始提交 - 移除所有硬编码凭据,统一从环境变量读取
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* FRED (Federal Reserve Economic Data) CSV fetcher via curl.exe
|
||||
*
|
||||
* Why curl.exe instead of axios:
|
||||
* Node.js axios → FRED's Akamai CDN times out on SP500, DGS2, DGS10 series.
|
||||
* curl.exe → same URL returns data in ~1s.
|
||||
*
|
||||
* Why date range params (cosd/coed):
|
||||
* Full DGS2/DGS10 datasets (daily from 1962) are huge → Akamai times out.
|
||||
* Limiting to recent years solves this. All calculations only need ≤3 years.
|
||||
*
|
||||
* Root cause: Akamai CDN differentiates by TLS fingerprint AND URL pattern.
|
||||
*/
|
||||
|
||||
const { execFile } = require('child_process')
|
||||
|
||||
const FRED_BASE = 'https://fred.stlouisfed.org/graph/fredgraph.csv'
|
||||
|
||||
/**
|
||||
* Fetch FRED CSV data using curl.exe with a date range limit
|
||||
* @param {string} seriesId - FRED series ID (e.g. 'SP500', 'FEDFUNDS')
|
||||
* @param {object} [opts]
|
||||
* @param {number} [opts.timeout=20] - curl --max-time seconds
|
||||
* @param {number} [opts.lookbackYears=3] - years to look back from today
|
||||
* @param {string} [opts.coed] - custom observation end date YYYY-MM-DD (default today)
|
||||
* @param {string} [opts.cosd] - custom observation start date YYYY-MM-DD (default coed - lookbackYears)
|
||||
* @returns {Promise<string>} raw CSV text
|
||||
*/
|
||||
const fetchFredCurl = (seriesId, opts = {}) => {
|
||||
const timeout = opts.timeout || 20
|
||||
const lookbackYears = opts.lookbackYears || 3
|
||||
const now = new Date()
|
||||
const coed = opts.coed || now.toISOString().slice(0, 10)
|
||||
const cosd = opts.cosd || (new Date(now.getTime() - lookbackYears * 365 * 24 * 60 * 60 * 1000)).toISOString().slice(0, 10)
|
||||
const url = `${FRED_BASE}?id=${encodeURIComponent(seriesId)}&cosd=${cosd}&coed=${coed}`
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
execFile('curl.exe', ['-s', '--max-time', String(timeout), url], {
|
||||
encoding: 'utf8',
|
||||
timeout: (timeout + 10) * 1000,
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
windowsHide: true
|
||||
}, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
const msg = stderr ? stderr.trim() : err.message
|
||||
return reject(new Error(`curl failed for ${seriesId}: ${msg}`))
|
||||
}
|
||||
resolve(stdout)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { fetchFredCurl }
|
||||
Reference in New Issue
Block a user