2025-05-19 13:22:46 +09:00
|
|
|
|
using DevExpress.XtraEditors.TextEditController.InputHandler;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
|
using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip;
|
|
|
|
|
|
|
|
|
|
|
|
namespace BitgetApi.IndicatorConditionSetup
|
|
|
|
|
|
{
|
|
|
|
|
|
public class TradingViewConditionSetup
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
// 기본 파라미터
|
|
|
|
|
|
public int LengthMA = 34;
|
|
|
|
|
|
public int LengthSignal = 12;
|
|
|
|
|
|
public int AdxLength = 12;
|
|
|
|
|
|
public double AdxThreshold = 24;
|
2025-05-20 13:42:32 +09:00
|
|
|
|
public double RangeThreshold = 1;
|
|
|
|
|
|
public double RangeThresholdSell = 1;
|
2025-05-19 13:22:46 +09:00
|
|
|
|
|
|
|
|
|
|
// 데이터 입력: High, Low, Close 배열 (최근 것이 맨 끝)
|
|
|
|
|
|
public List<double> High;
|
|
|
|
|
|
public List<double> Low;
|
|
|
|
|
|
public List<double> Close;
|
|
|
|
|
|
|
|
|
|
|
|
// SMA4 계산용
|
|
|
|
|
|
private double[] Sma4;
|
|
|
|
|
|
|
|
|
|
|
|
public void SetData(List<double> high, List<double> low, List<double> close)
|
|
|
|
|
|
{
|
|
|
|
|
|
High = high;
|
|
|
|
|
|
Low = low;
|
|
|
|
|
|
Close = close;
|
|
|
|
|
|
Sma4 = ComputeSma4();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. SMMA 계산
|
|
|
|
|
|
private double CalcSmma(List<double> src, int len)
|
|
|
|
|
|
{
|
|
|
|
|
|
double smma = src.Take(len).Average();
|
|
|
|
|
|
for (int i = len; i < src.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
smma = (smma * (len - 1) + src[i]) / len;
|
|
|
|
|
|
}
|
|
|
|
|
|
return smma;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 2. ZLEMA 계산
|
|
|
|
|
|
private double CalcZlema(List<double> src, int length)
|
|
|
|
|
|
{
|
|
|
|
|
|
double ema1 = Ema(src, length);
|
|
|
|
|
|
double ema2 = Ema(Enumerable.Repeat(ema1, src.Count).ToList(), length);
|
|
|
|
|
|
double d = ema1 - ema2;
|
|
|
|
|
|
return ema1 + d;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. EMA 계산
|
|
|
|
|
|
private double Ema(List<double> src, int length)
|
|
|
|
|
|
{
|
|
|
|
|
|
double alpha = 2.0 / (length + 1);
|
|
|
|
|
|
double ema = src[0];
|
|
|
|
|
|
for (int i = 1; i < src.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
ema = alpha * src[i] + (1 - alpha) * ema;
|
|
|
|
|
|
}
|
|
|
|
|
|
return ema;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. SMA(4) 계산
|
|
|
|
|
|
private double[] ComputeSma4()
|
|
|
|
|
|
{
|
|
|
|
|
|
double[] sma4 = new double[Close.Count];
|
|
|
|
|
|
for (int i = 3; i < Close.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
sma4[i] = (Close[i] + Close[i - 1] + Close[i - 2] + Close[i - 3]) / 4.0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return sma4;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 5. ADX (가장 간단한 형태)
|
|
|
|
|
|
private double ComputeAdx()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 간단화: 실제 구현 시 true range, directional movement 등 추가 필요
|
|
|
|
|
|
return 25.0; // 더미값 또는 타 라이브러리 사용 가능
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 6. Impulse MACD 계산
|
|
|
|
|
|
public double ComputeMd(bool flag = true)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (flag)
|
|
|
|
|
|
{
|
|
|
|
|
|
double hi = CalcSmma(High, LengthMA);
|
|
|
|
|
|
double lo = CalcSmma(Low, LengthMA);
|
|
|
|
|
|
var hlc3 = High.Zip(Low, (h, l) => (h + l + Close.Last()) / 3).ToList();
|
|
|
|
|
|
double mi = CalcZlema(hlc3, LengthMA);
|
|
|
|
|
|
if (mi > hi) return mi - hi;
|
|
|
|
|
|
else if (mi < lo) return mi - lo;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var highTemp = new List<double>();
|
|
|
|
|
|
foreach(var item in High)
|
|
|
|
|
|
highTemp.Add(item);
|
|
|
|
|
|
var lowTemp = new List<double>();
|
|
|
|
|
|
foreach (var item in Low)
|
|
|
|
|
|
lowTemp.Add(item);
|
|
|
|
|
|
var closeTemp = new List<double>();
|
|
|
|
|
|
foreach (var item in Close)
|
|
|
|
|
|
closeTemp.Add(item);
|
|
|
|
|
|
|
|
|
|
|
|
highTemp.RemoveAt(highTemp.Count - 1);
|
|
|
|
|
|
lowTemp.RemoveAt(lowTemp.Count - 1);
|
|
|
|
|
|
closeTemp.RemoveAt(closeTemp.Count - 1);
|
|
|
|
|
|
|
|
|
|
|
|
double hi = CalcSmma(highTemp, LengthMA);
|
|
|
|
|
|
double lo = CalcSmma(lowTemp, LengthMA);
|
|
|
|
|
|
var hlc3 = highTemp.Zip(lowTemp, (h, l) => (h + l + closeTemp.Last()) / 3).ToList();
|
|
|
|
|
|
double mi = CalcZlema(hlc3, LengthMA);
|
|
|
|
|
|
if (mi > hi) return mi - hi;
|
|
|
|
|
|
else if (mi < lo) return mi - lo;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 7. MACD, SignalLine 계산
|
|
|
|
|
|
public (double macdLine, double signalLine) ComputeMacd()
|
|
|
|
|
|
{
|
|
|
|
|
|
double macdLine = Ema(Close, 34) - Ema(Close, 12);
|
|
|
|
|
|
double signalLine = Ema(Enumerable.Repeat(macdLine, Close.Count).ToList(), 12);
|
|
|
|
|
|
return (macdLine, signalLine);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-20 12:34:26 +09:00
|
|
|
|
// 7. MACD, SignalLine 계산(이전꺼)
|
|
|
|
|
|
public (double macdLine, double signalLine) PrevComputeMacd()
|
|
|
|
|
|
{
|
|
|
|
|
|
var temp = new List<double>();
|
|
|
|
|
|
foreach (var item in Close)
|
|
|
|
|
|
temp.Add(item);
|
|
|
|
|
|
temp.RemoveAt(temp.Count - 1);
|
|
|
|
|
|
|
|
|
|
|
|
double macdLine = Ema(temp, 34) - Ema(temp, 12);
|
|
|
|
|
|
double signalLine = Ema(Enumerable.Repeat(macdLine, temp.Count).ToList(), 12);
|
|
|
|
|
|
return (macdLine, signalLine);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-19 13:22:46 +09:00
|
|
|
|
// 8. RangeBound 필터
|
|
|
|
|
|
public bool IsRangeBound()
|
|
|
|
|
|
{
|
|
|
|
|
|
double highestHigh = High.Skip(High.Count - 12).Max();
|
|
|
|
|
|
double lowestLow = Low.Skip(Low.Count - 12).Min();
|
|
|
|
|
|
double range = highestHigh - lowestLow;
|
|
|
|
|
|
return range < (highestHigh * RangeThreshold / 100.0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsRangeBoundSell()
|
|
|
|
|
|
{
|
|
|
|
|
|
double highestHigh = High.Skip(High.Count - 12).Max();
|
|
|
|
|
|
double lowestLow = Low.Skip(Low.Count - 12).Min();
|
|
|
|
|
|
double range = highestHigh - lowestLow;
|
|
|
|
|
|
return range < (highestHigh * RangeThresholdSell / 100.0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 9. 트렌드 여부
|
|
|
|
|
|
public bool IsTrending()
|
|
|
|
|
|
{
|
|
|
|
|
|
return ComputeAdx() > AdxThreshold;
|
|
|
|
|
|
}
|
2025-05-20 13:42:32 +09:00
|
|
|
|
public double GetAvgData(int range)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sma = 0.0;
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < range; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
sma += Close[Close.Count - (i + 1)];
|
|
|
|
|
|
}
|
|
|
|
|
|
return sma / range;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public double GetAvgData(int range, int prev)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sma = 0.0;
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < range; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
sma += Close[Close.Count - (i + 1 + prev)];
|
|
|
|
|
|
}
|
|
|
|
|
|
return sma / range;
|
|
|
|
|
|
}
|
2025-05-19 13:22:46 +09:00
|
|
|
|
|
|
|
|
|
|
// 10. 매수 조건
|
|
|
|
|
|
public bool IsLongSignal()
|
|
|
|
|
|
{
|
|
|
|
|
|
double md = ComputeMd();
|
|
|
|
|
|
double prevMd = ComputeMd(false);
|
|
|
|
|
|
var (macd, signal) = ComputeMacd();
|
2025-05-20 12:34:26 +09:00
|
|
|
|
var (prevMacd, prevSignal) = PrevComputeMacd();
|
|
|
|
|
|
|
2025-05-20 13:42:32 +09:00
|
|
|
|
bool isUptrend = Close.Last() > Sma4.Last();
|
|
|
|
|
|
|
|
|
|
|
|
return ((prevMd == 0 && md > 0) || (prevMacd < prevSignal && macd > prevMacd && macd > signal && md > 0)) &&
|
|
|
|
|
|
IsTrending() && IsSmaMiddle(4, 12, true, GetAvgData(72)) && GetAvgData(4) > GetAvgData(4, 1);
|
2025-05-19 13:22:46 +09:00
|
|
|
|
|
2025-05-20 13:42:32 +09:00
|
|
|
|
//return isUptrend &&
|
|
|
|
|
|
// ((prevMd == 0 && md > 0) || (macd > prevMacd && signal > prevSignal && md > 0 && macd > signal)) &&
|
|
|
|
|
|
// IsTrending() && !IsRangeBound();
|
2025-05-19 13:22:46 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 11. 매도 조건
|
|
|
|
|
|
public bool IsShortSignal()
|
|
|
|
|
|
{
|
|
|
|
|
|
double md = ComputeMd();
|
|
|
|
|
|
double prevMd = ComputeMd(false);
|
|
|
|
|
|
var (macd, signal) = ComputeMacd();
|
2025-05-20 12:34:26 +09:00
|
|
|
|
var (prevMacd, prevSignal) = PrevComputeMacd();
|
|
|
|
|
|
|
2025-05-19 13:22:46 +09:00
|
|
|
|
bool isDowntrend = Close.Last() < Sma4.Last();
|
|
|
|
|
|
|
2025-05-20 13:42:32 +09:00
|
|
|
|
return ((prevMd == 0 && md < 0) || (prevMacd > prevSignal && macd < prevMacd && macd < signal && md < 0)) &&
|
|
|
|
|
|
IsTrending() && IsSmaMiddle(4, 12, false, GetAvgData(72)) && GetAvgData(4) < GetAvgData(4, 1);
|
|
|
|
|
|
|
|
|
|
|
|
//return isDowntrend &&
|
|
|
|
|
|
// ((prevMd == 0 && md < 0) || (macd < prevMacd && signal < prevSignal && md < 0 && macd < signal)) &&
|
|
|
|
|
|
// IsTrending() && !IsRangeBoundSell();
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-05-19 13:22:46 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsSmaHigh(int range, int count)
|
|
|
|
|
|
{
|
|
|
|
|
|
var list = new List<double>();
|
|
|
|
|
|
for (int i = Close.Count - count - 1; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sum = 0.0;
|
|
|
|
|
|
for(int j = 0;j < range; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
sum += Close[i-j];
|
|
|
|
|
|
}
|
|
|
|
|
|
var data = sum / range;
|
|
|
|
|
|
list.Add(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (list.Max() == list[list.Count - 1])
|
|
|
|
|
|
return true;
|
|
|
|
|
|
else
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public bool IsSmaLow(int range, int count)
|
|
|
|
|
|
{
|
|
|
|
|
|
var list = new List<double>();
|
|
|
|
|
|
for (int i = Close.Count - count - 1; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sum = 0.0;
|
|
|
|
|
|
for (int j = 0; j < range; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
sum += Close[i - j];
|
|
|
|
|
|
}
|
|
|
|
|
|
var data = sum / range;
|
|
|
|
|
|
list.Add(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (list.Min() == list[list.Count - 1])
|
|
|
|
|
|
return true;
|
|
|
|
|
|
else
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-05-20 13:42:32 +09:00
|
|
|
|
public bool IsSmaMiddle(int range, int count, bool longFlag, double value) {
|
2025-05-19 13:22:46 +09:00
|
|
|
|
var list = new List<double>();
|
|
|
|
|
|
for (int i = Close.Count - count - 1; i < count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
var sum = 0.0;
|
|
|
|
|
|
for (int j = 0; j < range; j++)
|
|
|
|
|
|
{
|
|
|
|
|
|
sum += Close[i - j];
|
|
|
|
|
|
}
|
|
|
|
|
|
var data = sum / range;
|
|
|
|
|
|
list.Add(data);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (longFlag)
|
|
|
|
|
|
{
|
2025-05-20 13:42:32 +09:00
|
|
|
|
if(value < list.Average())
|
2025-05-19 13:22:46 +09:00
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2025-05-20 13:42:32 +09:00
|
|
|
|
if (value > list.Average())
|
2025-05-19 13:22:46 +09:00
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|