Spaces:
Running
Running
File size: 2,626 Bytes
e3fe258 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | /**
* twelveDataAPI.js (SERVER-SIDE ONLY)
*
* Fetches OHLC candles from Twelve Data (OANDA-sourced forex / metals) so the chart
* and scanners match broker / TradingView data instead of Yahoo's coarse FX feed.
*
* API keys are passed in by the caller (read from server environment variables) and
* are NEVER sent to the browser. The browser only ever reads the already-fetched
* candles from the server's /api/market-data cache.
*/
// App symbol -> Twelve Data symbol (forex/metals use slash notation).
const TD_SYMBOL = {
XAUUSD: 'XAU/USD',
GBPUSD: 'GBP/USD',
USDCAD: 'USD/CAD',
BTCUSDT: 'BTC/USD',
};
// App timeframe -> Twelve Data interval.
const TD_INTERVAL = {
'1m': '1min', '5m': '5min', '15m': '15min', '1H': '1h', '4H': '4h', '1D': '1day',
};
/**
* Fetch candles from Twelve Data.
*
* @param {string} symbol App symbol, e.g. 'USDCAD'.
* @param {string} timeframe App timeframe, e.g. '15m'.
* @param {number} outputsize Number of candles to request.
* @param {string} apiKey Twelve Data API key for this symbol.
* @returns {Promise<Array<{time:number,open:number,high:number,low:number,close:number,volume:number}>>}
*/
export async function fetchTwelveCandles(symbol, timeframe = '15m', outputsize = 500, apiKey) {
if (!apiKey) throw new Error(`No Twelve Data API key provided for ${symbol}`);
const tdSymbol = TD_SYMBOL[symbol] || symbol;
const interval = TD_INTERVAL[timeframe] || '15min';
const url = `https://api.twelvedata.com/time_series?symbol=${encodeURIComponent(tdSymbol)}`
+ `&interval=${interval}&outputsize=${outputsize}&timezone=UTC&format=JSON&apikey=${apiKey}`;
const res = await fetch(url);
const data = await res.json();
// Twelve Data signals problems via { status: 'error', message, code }.
if (!data || data.status === 'error' || !Array.isArray(data.values)) {
const msg = data && data.message ? data.message : 'no values returned';
throw new Error(`Twelve Data error for ${symbol}: ${msg}`);
}
// Values are newest-first; convert datetime (UTC) -> unix seconds and reverse.
const candles = data.values.map(v => {
const dt = v.datetime.includes(' ')
? v.datetime.replace(' ', 'T') + 'Z'
: v.datetime + 'T00:00:00Z';
return {
time: Math.floor(new Date(dt).getTime() / 1000),
open: parseFloat(v.open),
high: parseFloat(v.high),
low: parseFloat(v.low),
close: parseFloat(v.close),
volume: parseFloat(v.volume || 0),
};
}).filter(c => Number.isFinite(c.time) && Number.isFinite(c.close)).reverse();
return candles;
}
export default { fetchTwelveCandles };
|