maher-tradingview-api
Version:
Tradingview instant stocks API, indicator alerts, trading bot, and more !
536 lines (484 loc) • 15.7 kB
JavaScript
/* eslint-disable linebreak-style */
/* eslint-disable no-restricted-properties */
/* eslint-disable linebreak-style */
/* eslint-disable no-unused-vars */
/* eslint-disable prefer-const */
/* eslint-disable linebreak-style */
/* eslint-disable space-before-function-paren */
/* eslint-disable no-const-assign */
/* eslint-disable arrow-parens */
/* eslint-disable no-use-before-define */
/* eslint-disable comma-dangle */
/* eslint-disable no-plusplus */
/* eslint-disable no-trailing-spaces */
/* eslint-disable object-shorthand */
/* eslint-disable func-names */
/* eslint-disable linebreak-style */
/* eslint-disable no-undef */
/* eslint-disable linebreak-style */
/**
* additional-indicators.js
* مؤشرات فنية إضافية لتطبيق TradingView API
*/
// رسم مؤشر ستوكاستيك
function renderStochasticChart(chartId, data, kPeriod = 14, dPeriod = 3) {
try {
console.log(`renderStochasticChart: رسم مؤشر ستوكاستيك في العنصر ${chartId}`);
if (!data || !Array.isArray(data) || data.length === 0) {
console.error('renderStochasticChart: البيانات غير صالحة أو فارغة', data);
return null;
}
const chartElement = document.getElementById(chartId);
if (!chartElement) {
console.error(`renderStochasticChart: لم يتم العثور على العنصر بالمعرف ${chartId}`);
return null;
}
// حساب مؤشر ستوكاستيك
const stochastic = calculateStochastic(data, kPeriod, dPeriod);
// إنشاء الرسم البياني
const ctx = chartElement.getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: data.slice(kPeriod).map(candle => {
if (typeof candle.time === 'string') {
return candle.time.split(',')[0];
}
return candle.time;
}),
datasets: [
{
label: '%K',
data: stochastic.k,
borderColor: 'rgb(54, 162, 235)',
backgroundColor: 'rgba(54, 162, 235, 0.1)',
borderWidth: 1.5,
tension: 0.1,
pointRadius: 0
},
{
label: '%D',
data: stochastic.d,
borderColor: 'rgb(255, 99, 132)',
backgroundColor: 'rgba(255, 99, 132, 0.1)',
borderWidth: 1.5,
tension: 0.1,
pointRadius: 0
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'bottom'
},
tooltip: {
mode: 'index',
intersect: false
}
},
scales: {
y: {
min: 0,
max: 100,
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
font: {
size: 10
}
}
},
x: {
display: false
}
}
}
});
return chart;
} catch (error) {
console.error('خطأ في رسم مؤشر ستوكاستيك:', error);
return null;
}
}
// حساب مؤشر ستوكاستيك
function calculateStochastic(data, kPeriod = 14, dPeriod = 3) {
// التحقق من البيانات
if (!data || data.length < kPeriod + dPeriod) {
throw new Error('بيانات غير كافية لحساب مؤشر ستوكاستيك');
}
const kValues = [];
// حساب %K
for (let i = kPeriod - 1; i < data.length; i++) {
const periodData = data.slice(i - kPeriod + 1, i + 1);
const highestHigh = Math.max(...periodData.map(candle => candle.high));
const lowestLow = Math.min(...periodData.map(candle => candle.low));
const currentClose = data[i].close;
// معادلة حساب %K
const k = ((currentClose - lowestLow) / (highestHigh - lowestLow)) * 100;
kValues.push(isNaN(k) ? 50 : k); // استخدام 50 كقيمة افتراضية إذا كان الناتج NaN
}
// حساب %D (المتوسط المتحرك البسيط لـ %K)
const dValues = [];
for (let i = dPeriod - 1; i < kValues.length; i++) {
const sum = kValues.slice(i - dPeriod + 1, i + 1).reduce((acc, val) => acc + val, 0);
const d = sum / dPeriod;
dValues.push(d);
}
// إرجاع قيم %K التي تتوافق مع طول قيم %D (يتم تجاهل القيم الأولى من %K)
return {
k: kValues.slice(dPeriod - 1),
d: dValues
};
}
// رسم مؤشر حجم التوازن (OBV)
function renderOBVChart(chartId, data) {
try {
console.log(`renderOBVChart: رسم مؤشر حجم التوازن في العنصر ${chartId}`);
if (!data || !Array.isArray(data) || data.length === 0) {
console.error('renderOBVChart: البيانات غير صالحة أو فارغة', data);
return null;
}
const chartElement = document.getElementById(chartId);
if (!chartElement) {
console.error(`renderOBVChart: لم يتم العثور على العنصر بالمعرف ${chartId}`);
return null;
}
// حساب OBV
const obvValues = calculateOBV(data);
// إنشاء الرسم البياني
const ctx = chartElement.getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: data.slice(1).map(candle => {
if (typeof candle.time === 'string') {
return candle.time.split(',')[0];
}
return candle.time;
}),
datasets: [
{
label: 'OBV',
data: obvValues,
borderColor: 'rgb(153, 102, 255)',
backgroundColor: 'rgba(153, 102, 255, 0.1)',
borderWidth: 2,
tension: 0.1,
fill: true,
pointRadius: 0
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'bottom'
},
tooltip: {
mode: 'index',
intersect: false
}
},
scales: {
y: {
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
font: {
size: 10
}
}
},
x: {
display: false
}
}
}
});
return chart;
} catch (error) {
console.error('خطأ في رسم مؤشر حجم التوازن:', error);
return null;
}
}
// حساب مؤشر حجم التوازن (OBV)
function calculateOBV(data) {
if (!data || data.length <= 1) {
return [];
}
const obvValues = [];
let obv = 0;
for (let i = 1; i < data.length; i++) {
const currentClose = data[i].close;
const previousClose = data[i-1].close;
const currentVolume = data[i].volume || 0;
// حساب OBV
if (currentClose > previousClose) {
obv += currentVolume; // إضافة الحجم إذا كان الإغلاق صاعد
} else if (currentClose < previousClose) {
obv -= currentVolume; // طرح الحجم إذا كان الإغلاق هابط
}
// إذا كان السعر متساوي، لا يتم تعديل OBV
obvValues.push(obv);
}
return obvValues;
}
// رسم مؤشر ADX (المؤشر المتقارب والمتباعد)
function renderADXChart(chartId, data, period = 14) {
try {
console.log(`renderADXChart: رسم مؤشر ADX في العنصر ${chartId}`);
if (!data || !Array.isArray(data) || data.length === 0) {
console.error('renderADXChart: البيانات غير صالحة أو فارغة', data);
return null;
}
const chartElement = document.getElementById(chartId);
if (!chartElement) {
console.error(`renderADXChart: لم يتم العثور على العنصر بالمعرف ${chartId}`);
return null;
}
if (data.length < period * 2) {
// إذا كانت البيانات غير كافية، عرض رسالة خطأ
const ctx = chartElement.getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: ['بيانات غير كافية'],
datasets: [{
label: 'ADX',
data: [],
borderColor: 'rgb(153, 102, 255)',
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
title: {
display: true,
text: 'بيانات غير كافية لحساب مؤشر ADX'
}
}
}
});
return chart;
}
// حساب ADX
try {
const adxResult = calculateADX(data, period);
// إنشاء الرسم البياني
const ctx = chartElement.getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: data.slice(period * 2).map(candle => {
if (typeof candle.time === 'string') {
return candle.time.split(',')[0];
}
return candle.time;
}),
datasets: [
{
label: 'ADX',
data: adxResult.adx,
borderColor: 'rgb(153, 102, 255)',
backgroundColor: 'rgba(153, 102, 255, 0.1)',
borderWidth: 2,
tension: 0.1,
pointRadius: 0
},
{
label: '+DI',
data: adxResult.plusDI,
borderColor: 'rgb(75, 192, 192)',
backgroundColor: 'rgba(75, 192, 192, 0.1)',
borderWidth: 1,
borderDash: [3, 3],
tension: 0.1,
pointRadius: 0
},
{
label: '-DI',
data: adxResult.minusDI,
borderColor: 'rgb(255, 159, 64)',
backgroundColor: 'rgba(255, 159, 64, 0.1)',
borderWidth: 1,
borderDash: [3, 3],
tension: 0.1,
pointRadius: 0
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: true,
position: 'bottom'
},
tooltip: {
mode: 'index',
intersect: false
}
},
scales: {
y: {
min: 0,
max: 100,
grid: {
color: 'rgba(0, 0, 0, 0.05)'
},
ticks: {
font: {
size: 10
}
}
},
x: {
display: false
}
}
}
});
return chart;
} catch (calcError) {
console.error('خطأ في حساب ADX:', calcError);
// إذا فشل الحساب، عرض رسالة خطأ
const ctx = chartElement.getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: ['خطأ'],
datasets: [{
label: 'ADX',
data: [],
borderColor: 'rgb(153, 102, 255)',
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
},
title: {
display: true,
text: 'حدث خطأ في حساب مؤشر ADX'
}
}
}
});
return chart;
}
} catch (error) {
console.error('خطأ في رسم مؤشر ADX:', error);
return null;
}
}
// حساب مؤشر ADX
function calculateADX(data, period = 14) {
if (!data || data.length < period * 2) {
throw new Error('بيانات غير كافية لحساب مؤشر ADX');
}
// حساب +DM و -DM
const plusDM = [];
const minusDM = [];
for (let i = 1; i < data.length; i++) {
const highDiff = data[i].high - data[i-1].high;
const lowDiff = data[i-1].low - data[i].low;
// +DM
if (highDiff > lowDiff && highDiff > 0) {
plusDM.push(highDiff);
} else {
plusDM.push(0);
}
// -DM
if (lowDiff > highDiff && lowDiff > 0) {
minusDM.push(lowDiff);
} else {
minusDM.push(0);
}
}
// حساب النطاق الحقيقي (TR)
const trueRanges = [];
for (let i = 1; i < data.length; i++) {
const tr1 = Math.abs(data[i].high - data[i].low);
const tr2 = Math.abs(data[i].high - data[i-1].close);
const tr3 = Math.abs(data[i].low - data[i-1].close);
const tr = Math.max(tr1, tr2, tr3);
trueRanges.push(tr);
}
// حساب المؤشرات +DI و -DI
const plusDI = [];
const minusDI = [];
const adxValues = [];
// حساب المتوسط الأول
let sumTR = 0;
let sumPlusDM = 0;
let sumMinusDM = 0;
for (let i = 0; i < period; i++) {
sumTR += trueRanges[i];
sumPlusDM += plusDM[i];
sumMinusDM += minusDM[i];
}
let smoothedTR = sumTR;
let smoothedPlusDM = sumPlusDM;
let smoothedMinusDM = sumMinusDM;
// حساب +DI و -DI
for (let i = period; i < trueRanges.length; i++) {
smoothedTR = smoothedTR - (smoothedTR / period) + trueRanges[i];
smoothedPlusDM = smoothedPlusDM - (smoothedPlusDM / period) + plusDM[i];
smoothedMinusDM = smoothedMinusDM - (smoothedMinusDM / period) + minusDM[i];
const plus_di = (smoothedPlusDM / smoothedTR) * 100;
const minus_di = (smoothedMinusDM / smoothedTR) * 100;
plusDI.push(plus_di);
minusDI.push(minus_di);
// حساب DX
const dx = Math.abs(plus_di - minus_di) / (plus_di + minus_di) * 100;
// حساب ADX (متوسط متحرك لـ DX)
if (i >= period * 2 - 1) {
let adx = 0;
if (adxValues.length === 0) {
// المتوسط الأولي
let sumDX = 0;
for (let j = i - period + 1; j <= i; j++) {
const tempPlusDI = plusDI[j - period];
const tempMinusDI = minusDI[j - period];
sumDX += Math.abs(tempPlusDI - tempMinusDI) / (tempPlusDI + tempMinusDI) * 100;
}
adx = sumDX / period;
} else {
// المتوسط المتحرك الأسي
adx = (adxValues[adxValues.length - 1] * (period - 1) + dx) / period;
}
adxValues.push(adx);
}
}
return {
adx: adxValues,
plusDI: plusDI.slice(period - 1),
minusDI: minusDI.slice(period - 1)
};
}
// تصدير الوظائف للاستخدام العالمي
window.AdditionalIndicators = {
renderStochasticChart,
calculateStochastic,
renderOBVChart,
calculateOBV,
renderADXChart,
calculateADX
};