File size: 3,314 Bytes
66b7a93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
 * finnhubAPI.js
 * Fetches historical OHLCV candles and live quotes from Finnhub REST API
 * via Vite dev proxy.
 */

const FINNHUB_BASE = '/api/finnhub/api/v1';
const FINNHUB_API_KEY = 'd7aejfpr01qn9i7kleugd7aejfpr01qn9i7klev0';

/**
 * Maps local symbol names to Finnhub's expected symbol format.
 */
export const FINNHUB_SYMBOL_MAP = {
  XAUUSD: 'OANDA:XAU_USD',
  GBPUSD: 'OANDA:GBP_USD',
  EURUSD: 'OANDA:EUR_USD',
  USDCAD: 'OANDA:USD_CAD',
};

/**
 * Fetch historical OHLCV candles from Finnhub.
 *
 * @param {string} symbol     - Local symbol key, e.g. 'XAUUSD'
 * @param {string} resolution - Candle resolution: '1','5','15','60','240','D'
 * @param {number} from       - Start unix timestamp (seconds)
 * @param {number} to         - End unix timestamp (seconds)
 * @returns {Promise<Array<{time:number, open:number, high:number, low:number, close:number, volume:number}>>}
 */
export async function fetchCandles(symbol, resolution, from, to) {
  const finnhubSymbol = FINNHUB_SYMBOL_MAP[symbol] ?? symbol;

  const params = new URLSearchParams({
    symbol: finnhubSymbol,
    resolution,
    from: String(Math.floor(from)),
    to:   String(Math.floor(to)),
    token: FINNHUB_API_KEY,
  });

  const url = `${FINNHUB_BASE}/forex/candle?${params}`;

  let response;
  try {
    response = await fetch(url);
  } catch (err) {
    console.error(`[finnhubAPI] Network error fetching candles for ${symbol}:`, err);
    throw new Error(`Network error fetching Finnhub candles: ${err.message}`);
  }

  if (!response.ok) {
    const body = await response.text().catch(() => '');
    console.error(`[finnhubAPI] HTTP ${response.status} for ${symbol}:`, body);
    throw new Error(`Finnhub API error ${response.status}: ${body}`);
  }

  const data = await response.json();

  if (data.s === 'no_data' || !data.t || data.t.length === 0) {
    console.warn(`[finnhubAPI] No data returned for ${symbol} (${resolution})`);
    return [];
  }

  // Zip the parallel arrays into an array of candle objects
  return data.t.map((timestamp, i) => ({
    time:   timestamp,
    open:   data.o[i],
    high:   data.h[i],
    low:    data.l[i],
    close:  data.c[i],
    volume: data.v[i],
  }));
}

/**
 * Fetch a real-time quote for the given symbol.
 *
 * @param {string} symbol - Local symbol key, e.g. 'XAUUSD'
 * @returns {Promise<{current:number, high:number, low:number, open:number, prevClose:number}>}
 */
export async function fetchQuote(symbol) {
  const finnhubSymbol = FINNHUB_SYMBOL_MAP[symbol] ?? symbol;

  const params = new URLSearchParams({
    symbol: finnhubSymbol,
    token:  FINNHUB_API_KEY,
  });

  const url = `${FINNHUB_BASE}/quote?${params}`;

  let response;
  try {
    response = await fetch(url);
  } catch (err) {
    console.error(`[finnhubAPI] Network error fetching quote for ${symbol}:`, err);
    throw new Error(`Network error fetching Finnhub quote: ${err.message}`);
  }

  if (!response.ok) {
    const body = await response.text().catch(() => '');
    console.error(`[finnhubAPI] HTTP ${response.status} for quote ${symbol}:`, body);
    throw new Error(`Finnhub quote API error ${response.status}: ${body}`);
  }

  const q = await response.json();

  return {
    current:   q.c,
    high:      q.h,
    low:       q.l,
    open:      q.o,
    prevClose: q.pc,
  };
}