Индекс относительной силы
Я пытаюсь рассчитать индекс относительной силы, RSI, для финансового инструмента. Когда я сравниваю мои расчеты с расчетами, сделанными коммерческим программным обеспечением, они не выглядят одинаково. Я не могу понять, что я делаю не так. Кто-нибудь может помочь?
Формула RSI:
{100} - (100 / (1 + RS)). Где RS - AvgGain(N периодов)/AvgLoss( N периодов)
public DataTable RSI(string instrument, int period, string oper, int entryVal)
{
DataTable dtRSI = new DataTable(); //table to return
dtRSI.Columns.Add("Date");
dtRSI.Columns.Add("Instrument");
dtRSI.Columns.Add("Close");
dtRSI.Columns.Add("RSI");
//Load Datatable from database
DataTable dt = new DataTable();
dt = conn.ExtractDataFromDb(instrument);
int column = 1; //Close price
//variables to RSI formula
Queue<float> avgUp = new Queue<float>();
Queue<float> avgDown = new Queue<float>();
float close1, close2, rsi, rs;
float avgUp1, avgUp2, newAvgUp, avgDown1, avgDown2, newAvgDown;
string[] dateCloseRsi = new string[3]; //row of data to insert into new table
string date; //date of calculation
string[] splitDate = new string[2];
//get first close
close1 = float.Parse(dt.Rows[0][column].ToString());
dt.Rows.RemoveAt(0);
//get close for number of periods into the que-list
for (int i = 1; i <= period; i++)
{
close2 = float.Parse(dt.Rows[0][column].ToString());
//are todays close higher then yesterday?
if (close2 > close1)
{
avgUp.Enqueue(close2 - close1);
avgDown.Enqueue(0);
}
else if (close2<close1)
{
avgUp.Enqueue(0);
avgDown.Enqueue(close1 - close2);
}
else
{
avgUp.Enqueue(0);
avgDown.Enqueue(0);
}
close1 = close2;
dt.Rows.RemoveAt(0);
}
//iterate datatable and calculate RSI
foreach (DataRow rows in dt.Rows)
{
avgUp1 = float.Parse(avgUp.Average().ToString("n2")); //calculate yesterdays avg difference on up days
avgDown1 = float.Parse(avgDown.Average().ToString("n2")); //calculate yesterdays avg difference on down days
avgUp.Dequeue();
avgDown.Dequeue();
close2 = float.Parse(rows[column].ToString()); //todays close
//close today higher then yesterday?
if (close2 > close1)
{
avgUp.Enqueue(close2 - close1);
avgDown.Enqueue(0);
}
else if (close2 < close1)
{
avgDown.Enqueue(close1 - close2);
avgUp.Enqueue(0);
}
else
{
avgUp.Enqueue(0);
avgDown.Enqueue(0);
}
avgUp2 = float.Parse(avgUp.Average().ToString("n2")); //todays avg difference on up days
avgDown2 = float.Parse(avgDown.Average().ToString("n2")); //todays avg difference on down days
newAvgUp = ((avgUp1 * (period - 1)) + avgUp2) / period; //yesterdays and todays avg diff value on up days
newAvgDown = ((avgDown1 * (period - 1)) + avgDown2) / period; //yesterdays and todays avg diff value on down days
newAvgUp = float.Parse(newAvgUp.ToString("n2")); //round to 2 decimals
newAvgDown = float.Parse(newAvgDown.ToString("n2")); //round to 2 decimals
rs = newAvgUp / newAvgDown; //calc Relative Strength
rs = float.Parse(rs.ToString("n2")); //round to 2 decimals
rsi = 100 - (100 / (1 + rs)); //Calc RSI
rsi = float.Parse(rsi.ToString("n2")); //round to 2 decimals
close1 = close2; //todays close become yesterdays close for tomorrow
//remove time from date
date = rows[0].ToString();
splitDate = date.Split(' ');
date = splitDate[0];
//add data to dtRSI
DataRow rsiRow = dtRSI.NewRow();
rsiRow["Date"] = date;
rsiRow["Instrument"] = instrument;
rsiRow["Close"] = rows[column];
rsiRow["RSI"] = rsi;
dtRSI.Rows.Add(rsiRow);
}
return dtRSI; //returns a table with Date, Instrument, Close Price and RSI
}
1 ответ
Здравствуйте, вот протестированный и проверенный класс C#, который генерирует значения RSI со 100% точностью:
using System;
using System.Data;
using System.Globalization;
namespace YourNameSpace
{
class PriceEngine
{
public static DataTable data;
public static double[] positiveChanges;
public static double[] negativeChanges;
public static double[] averageGain;
public static double[] averageLoss;
public static double[] rsi;
public static double CalculateDifference(double current_price, double previous_price)
{
return current_price - previous_price;
}
public static double CalculatePositiveChange(double difference)
{
return difference > 0 ? difference : 0;
}
public static double CalculateNegativeChange(double difference)
{
return difference < 0 ? difference * -1 : 0;
}
public static void CalculateRSI(int rsi_period, int price_index = 5)
{
for(int i = 0; i < PriceEngine.data.Rows.Count; i++)
{
double current_difference = 0.0;
if (i > 0)
{
double previous_close = Convert.ToDouble(PriceEngine.data.Rows[i-1].Field<string>(price_index));
double current_close = Convert.ToDouble(PriceEngine.data.Rows[i].Field<string>(price_index));
current_difference = CalculateDifference(current_close, previous_close);
}
PriceEngine.positiveChanges[i] = CalculatePositiveChange(current_difference);
PriceEngine.negativeChanges[i] = CalculateNegativeChange(current_difference);
if(i == Math.Max(1,rsi_period))
{
double gain_sum = 0.0;
double loss_sum = 0.0;
for(int x = Math.Max(1,rsi_period); x > 0; x--)
{
gain_sum += PriceEngine.positiveChanges[x];
loss_sum += PriceEngine.negativeChanges[x];
}
PriceEngine.averageGain[i] = gain_sum / Math.Max(1,rsi_period);
PriceEngine.averageLoss[i] = loss_sum / Math.Max(1,rsi_period);
}else if (i > Math.Max(1,rsi_period))
{
PriceEngine.averageGain[i] = ( PriceEngine.averageGain[i-1]*(rsi_period-1) + PriceEngine.positiveChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.averageLoss[i] = ( PriceEngine.averageLoss[i-1]*(rsi_period-1) + PriceEngine.negativeChanges[i]) / Math.Max(1, rsi_period);
PriceEngine.rsi[i] = PriceEngine.averageLoss[i] == 0 ? 100 : PriceEngine.averageGain[i] == 0 ? 0 : Math.Round(100 - (100 / (1 + PriceEngine.averageGain[i] / PriceEngine.averageLoss[i])), 5);
}
}
}
public static void Launch()
{
PriceEngine.data = new DataTable();
//load {date, time, open, high, low, close} values in PriceEngine.data (6th column (index #5) = close price) here
positiveChanges = new double[PriceEngine.data.Rows.Count];
negativeChanges = new double[PriceEngine.data.Rows.Count];
averageGain = new double[PriceEngine.data.Rows.Count];
averageLoss = new double[PriceEngine.data.Rows.Count];
rsi = new double[PriceEngine.data.Rows.Count];
CalculateRSI(14);
}
}
}
Пошаговая статья: https://turmanauli.medium.com/a-step-by-step-guide-for-calculating-reliable-rsi-values-programmatically-a6a604a06b77
PS Вам нужны глобальные переменные для хранения предыдущих значений, это не вариант для индикаторов, таких как RSI, простая функция работает только для простых индикаторов, таких как Simple Moving Average. Все сглаженные / взвешенные индикаторы нуждаются в буферах / глобальных массивах для хранения данных.