AssetManager.API/AssetManager.Infrastructure/StrategyEngine/TechnicalIndicators.cs

233 lines
6.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

namespace AssetManager.Infrastructure.StrategyEngine;
/// <summary>
/// 技术指标计算库
/// </summary>
public static class TechnicalIndicators
{
/// <summary>
/// 计算简单移动平均 (SMA)
/// </summary>
/// <param name="values">值列表</param>
/// <param name="period">周期</param>
/// <returns>SMA 列表(长度与输入一致,前 period-1 个为 null</returns>
public static List<decimal?> CalculateSMA(List<decimal> values, int period)
{
var result = new List<decimal?>();
if (values.Count < period)
{
result.AddRange(values.Select(_ => (decimal?)null));
return result;
}
// 前 period-1 个为 null
for (int i = 0; i < period - 1; i++)
{
result.Add(null);
}
// 计算第一个 SMA
decimal sum = 0;
for (int i = 0; i < period; i++)
{
sum += values[i];
}
result.Add(sum / period);
// 滑动窗口计算后续 SMA
for (int i = period; i < values.Count; i++)
{
sum = sum - values[i - period] + values[i];
result.Add(sum / period);
}
return result;
}
/// <summary>
/// 计算指数移动平均 (EMA)
/// </summary>
/// <param name="values">值列表</param>
/// <param name="period">周期</param>
/// <returns>EMA 列表(长度与输入一致,前 period-1 个为 null</returns>
public static List<decimal?> CalculateEMA(List<decimal> values, int period)
{
var result = new List<decimal?>();
if (values.Count < period)
{
result.AddRange(values.Select(_ => (decimal?)null));
return result;
}
// 前 period-1 个为 null
for (int i = 0; i < period - 1; i++)
{
result.Add(null);
}
// 第一个 EMA 使用 SMA
decimal sma = 0;
for (int i = 0; i < period; i++)
{
sma += values[i];
}
sma /= period;
result.Add(sma);
// 计算后续 EMAEMA = (当前值 - 前一个 EMA) * 乘数 + 前一个 EMA
decimal multiplier = 2.0m / (period + 1);
decimal prevEma = sma;
for (int i = period; i < values.Count; i++)
{
decimal ema = (values[i] - prevEma) * multiplier + prevEma;
result.Add(ema);
prevEma = ema;
}
return result;
}
/// <summary>
/// 计算真实波幅 (TR)
/// </summary>
/// <param name="highs">最高价列表</param>
/// <param name="lows">最低价列表</param>
/// <param name="closes">收盘价列表</param>
/// <returns>TR 列表</returns>
public static List<decimal> CalculateTR(List<decimal> highs, List<decimal> lows, List<decimal> closes)
{
var trList = new List<decimal>();
if (highs.Count == 0) return trList;
// 第一个 TRHigh - Low
trList.Add(highs[0] - lows[0]);
// 后续 TRMax(High-Low, High-PrevClose, PrevClose-Low)
for (int i = 1; i < highs.Count; i++)
{
decimal prevClose = closes[i - 1];
decimal tr1 = highs[i] - lows[i];
decimal tr2 = Math.Abs(highs[i] - prevClose);
decimal tr3 = Math.Abs(prevClose - lows[i]);
trList.Add(Math.Max(Math.Max(tr1, tr2), tr3));
}
return trList;
}
/// <summary>
/// 计算平均真实波幅 (ATR)
/// </summary>
/// <param name="highs">最高价列表</param>
/// <param name="lows">最低价列表</param>
/// <param name="closes">收盘价列表</param>
/// <param name="period">周期(默认 14</param>
/// <returns>ATR 列表(长度与输入一致,前 period-1 个为 null</returns>
public static List<decimal?> CalculateATR(List<decimal> highs, List<decimal> lows, List<decimal> closes, int period = 14)
{
var result = new List<decimal?>();
var trList = CalculateTR(highs, lows, closes);
if (trList.Count < period)
{
result.AddRange(trList.Select(_ => (decimal?)null));
return result;
}
// 前 period-1 个为 null
for (int i = 0; i < period - 1; i++)
{
result.Add(null);
}
// 第一个 ATRTR 的简单平均
decimal sum = 0;
for (int i = 0; i < period; i++)
{
sum += trList[i];
}
decimal atr = sum / period;
result.Add(atr);
// 后续 ATR(前一个 ATR * (period-1) + 当前 TR) / period
for (int i = period; i < trList.Count; i++)
{
atr = (atr * (period - 1) + trList[i]) / period;
result.Add(atr);
}
return result;
}
/// <summary>
/// 计算滚动窗口最高价
/// </summary>
/// <param name="values">值列表</param>
/// <param name="period">周期</param>
/// <returns>滚动最高价列表(长度与输入一致,前 period-1 个为 null</returns>
public static List<decimal?> CalculateHighestHigh(List<decimal> values, int period)
{
var result = new List<decimal?>();
if (values.Count < period)
{
result.AddRange(values.Select(_ => (decimal?)null));
return result;
}
// 前 period-1 个为 null
for (int i = 0; i < period - 1; i++)
{
result.Add(null);
}
// 滑动窗口计算
for (int i = period - 1; i < values.Count; i++)
{
decimal max = decimal.MinValue;
for (int j = i - period + 1; j <= i; j++)
{
if (values[j] > max) max = values[j];
}
result.Add(max);
}
return result;
}
/// <summary>
/// 计算滚动窗口最低价
/// </summary>
/// <param name="values">值列表</param>
/// <param name="period">周期</param>
/// <returns>滚动最低价列表(长度与输入一致,前 period-1 个为 null</returns>
public static List<decimal?> CalculateLowestLow(List<decimal> values, int period)
{
var result = new List<decimal?>();
if (values.Count < period)
{
result.AddRange(values.Select(_ => (decimal?)null));
return result;
}
// 前 period-1 个为 null
for (int i = 0; i < period - 1; i++)
{
result.Add(null);
}
// 滑动窗口计算
for (int i = period - 1; i < values.Count; i++)
{
decimal min = decimal.MaxValue;
for (int j = i - period + 1; j <= i; j++)
{
if (values[j] < min) min = values[j];
}
result.Add(min);
}
return result;
}
}