Bot & Automation
Treding Forex Ai
/root/hermes-projects/Treding Forex Ai
dist/src/services/market-data/marketDataService.js
text
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMarketDataService = createMarketDataService;
const markets_1 = require("../../constants/markets");
function createMarketDataService(provider, twelveDataApiKey) {
const demoService = new DemoMarketDataService();
if (provider === "twelvedata") {
return new FallbackMarketDataService(new TwelveDataMarketDataService(twelveDataApiKey), demoService);
}
return demoService;
}
class FallbackMarketDataService {
primary;
fallback;
constructor(primary, fallback) {
this.primary = primary;
this.fallback = fallback;
}
async getCandles(symbol, timeframe, limit) {
try {
return await this.primary.getCandles(symbol, timeframe, limit);
}
catch (error) {
const fallbackResult = await this.fallback.getCandles(symbol, timeframe, limit);
const message = error instanceof Error ? error.message : "market data provider failed";
return {
...fallbackResult,
warning: `Provider realtime gagal, memakai demo data. Detail aman: ${message}`
};
}
}
}
class DemoMarketDataService {
async getCandles(symbol, timeframe, limit) {
return {
candles: buildDemoCandles(symbol, timeframe, limit),
source: "demo"
};
}
}
class TwelveDataMarketDataService {
apiKey;
constructor(apiKey) {
this.apiKey = apiKey;
}
async getCandles(symbol, timeframe, limit) {
if (!this.apiKey) {
throw new Error("TWELVEDATA_API_KEY kosong");
}
const query = new URLSearchParams({
symbol: toTwelveDataSymbol(symbol),
interval: toTwelveDataInterval(timeframe),
outputsize: String(limit),
apikey: this.apiKey
});
const response = await fetch(`https://api.twelvedata.com/time_series?${query.toString()}`);
const payload = (await response.json());
if (!response.ok || payload.status === "error" || !Array.isArray(payload.values)) {
throw new Error(payload.message ?? "Twelve Data response tidak valid");
}
const candles = payload.values
.map((value) => ({
time: value.datetime,
open: Number(value.open),
high: Number(value.high),
low: Number(value.low),
close: Number(value.close),
volume: Number(value.volume ?? 0)
}))
.filter((candle) => Number.isFinite(candle.close))
.reverse();
return {
candles,
source: "twelvedata"
};
}
}
function buildDemoCandles(symbol, timeframe, limit) {
const basePrice = getBasePrice(symbol.code);
const volatility = symbol.code === "XAUUSD" ? 2.8 : symbol.pipSize * 18;
const trendBias = getStableBias(symbol.code, timeframe) * symbol.pipSize * (symbol.code === "XAUUSD" ? 8 : 1);
const start = Date.now() - limit * timeframeMinutes(timeframe) * 60_000;
const candles = [];
let previousClose = basePrice;
for (let index = 0; index < limit; index += 1) {
const wave = Math.sin(index / 5) * volatility;
const microWave = Math.cos(index / 11) * volatility * 0.4;
const close = basePrice + trendBias * index + wave + microWave;
const open = previousClose;
const high = Math.max(open, close) + volatility * 0.45;
const low = Math.min(open, close) - volatility * 0.45;
candles.push({
time: new Date(start + index * timeframeMinutes(timeframe) * 60_000).toISOString(),
open,
high,
low,
close,
volume: 1000 + index * 7 + Math.abs(Math.round(wave * 100))
});
previousClose = close;
}
return candles;
}
function getBasePrice(symbolCode) {
const basePrices = {
EURUSD: 1.085,
GBPUSD: 1.271,
USDJPY: 157.2,
GBPJPY: 199.4,
AUDUSD: 0.665,
XAUUSD: 2360,
BTCUSD: 60000
};
return basePrices[symbolCode] ?? 1;
}
function getStableBias(symbolCode, timeframe) {
const value = `${symbolCode}-${timeframe}`
.split("")
.reduce((sum, character) => sum + character.charCodeAt(0), 0);
return (value % 7) - 3;
}
function timeframeMinutes(timeframe) {
const values = {
M1: 1,
M5: 5,
M15: 15,
H1: 60,
H4: 240,
D1: 1440,
W1: 10080
};
return values[timeframe];
}
function toTwelveDataInterval(timeframe) {
const values = {
M1: "1min",
M5: "5min",
M15: "15min",
H1: "1h",
H4: "4h",
D1: "1day",
W1: "1week"
};
return values[timeframe];
}
function toTwelveDataSymbol(symbol) {
if (symbol.code === "XAUUSD") {
return "XAU/USD";
}
if (symbol.code === "BTCUSD") {
return "BTC/USD";
}
const knownSymbol = markets_1.MARKET_SYMBOLS.find((item) => item.code === symbol.code);
return knownSymbol?.label ?? symbol.label;
}