From 5c797e2b118983b92f28bf38870de618f1fb9ef9 Mon Sep 17 00:00:00 2001 From: kyh <1329522@naver.com> Date: Thu, 29 May 2025 13:40:56 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B5=AC=EB=A6=84=EB=96=BC=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SMABandConditionSetup.cs | 90 +++++++++++++++++-- 1 file changed, 81 insertions(+), 9 deletions(-) diff --git a/BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs b/BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs index dc9bc39..a89035e 100644 --- a/BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs +++ b/BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs @@ -1,4 +1,6 @@ using BitgetAPI.Data; +using BitgetAPI.Data.IndicatorData; +using DevExpress.XtraBars.Ribbon.Gallery; using DevExpress.XtraBars.ViewInfo; using System; using System.Collections.Generic; @@ -6,10 +8,22 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using static BitgetAPI.CommonProperty; +using static DevExpress.Utils.Diagnostics.GUIResources; using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip; namespace BitgetApi.IndicatorConditionSetup { + public class IchimokuResult + { + public DateTime Time { get; set; } + public double TenkanSen { get; set; } // 전환선 + public double KijunSen { get; set; } // 기준선 + public double SenkouSpanA { get; set; } // 선행스팬1 + public double SenkouSpanB { get; set; } // 선행스팬2 + public double ChikouSpan { get; set; } // 후행스팬 + } + + public class SMABandConditionSetup { private readonly int rangeLookback = 12; @@ -56,14 +70,10 @@ namespace BitgetApi.IndicatorConditionSetup bool isSideways = IsSideways(high, low, i); - //구름떼 - double conversionLine = (high.Skip(i - 8).Take(9).Max() + low.Skip(i - 8).Take(9).Min()) / 2.0; - double baseLine = (high.Skip(i - 25).Take(26).Max() + low.Skip(i - 25).Take(26).Min()) / 2.0; - double leadingSpan1 = (conversionLine + baseLine) / 2.0; - double leadingSpan2 = (high.Skip(i - 51).Take(52).Max() + low.Skip(i - 51).Take(52).Min()) / 2.0; + var ichmok = GetIchimokuCloud(candles, i); - double cloudTopNow = Math.Max(leadingSpan1, leadingSpan2); - double cloudBottomNow = Math.Min(leadingSpan1, leadingSpan2); + var cloudTopNow = Math.Max(ichmok.SenkouSpanA, ichmok.SenkouSpanB); + var cloudBottomNow = Math.Min(ichmok.SenkouSpanA, ichmok.SenkouSpanB); bool isSma4BelowCloud = sma4 < cloudBottomNow; bool isSma4AboveCloud = sma4 > cloudTopNow; @@ -86,12 +96,12 @@ namespace BitgetApi.IndicatorConditionSetup // 진입/청산 조건 bool rawNewLong = isSideways && isSma4AboveCloud && (sma4 == sma4High && prevSma4 <= prevSma4High && prev2Sma4 <= prev2Sma4High) && (sma4 >= prevSma4 && prevSma4 >= prev2Sma4 && prev2Sma4 >= prev3Sma4); - bool rawLongSell = (sma4 <= prevSma4 && prevSma4 <= prev2Sma4 && prev2Sma4 <= prev3Sma4 && sma4 == sma4Low); + bool rawLongSell = (sma4 <= prevSma4 && prevSma4 <= prev2Sma4 && sma4 < sma4Middle); bool rawNewShort = allowNewShortEntry && isSideways && isSma4BelowCloud && (sma4 == sma4Low && prevSma4 >= prevSma4Low && prev2Sma4 >= prev2Sma4Low) && (sma4 <= prevSma4 && prevSma4 <= prev2Sma4 && prev2Sma4 <= prev3Sma4); - bool rawShortSell = (sma4 >= prevSma4 && prevSma4 >= prev2Sma4 && prev2Sma4 >= prev3Sma4 && sma4 == sma4High); + bool rawShortSell = (sma4 >= prevSma4 && prevSma4 >= prev2Sma4 && sma4 > sma4Middle); // === 시그널 계산 === if (type == SMABandType.LongBuy) @@ -149,5 +159,67 @@ namespace BitgetApi.IndicatorConditionSetup double range = highestHigh - lowestLow; return range > (highestHigh * rangeThresholdPercent / 100.0); } + + private IchimokuResult GetIchimokuCloud(List candles, int i) + { + int lastIndex = i; + + int tenkanPeriod = 9; + int kijunPeriod = 26; + int senkouBPeriod = 52; + int displacement = 26; + + var result = new IchimokuResult(); + + // 전환선 계산 (현재봉 기준 9봉 범위) + if (lastIndex + 1 >= tenkanPeriod) + { + var tenkanHigh = candles.GetRange(lastIndex + 1 - tenkanPeriod, tenkanPeriod).Max(c => c.High); + var tenkanLow = candles.GetRange(lastIndex + 1 - tenkanPeriod, tenkanPeriod).Min(c => c.Low); + result.TenkanSen = (tenkanHigh + tenkanLow) / 2; + } + + // 기준선 계산 (현재봉 기준 26봉 범위) + if (lastIndex + 1 >= kijunPeriod) + { + var kijunHigh = candles.GetRange(lastIndex + 1 - kijunPeriod, kijunPeriod).Max(c => c.High); + var kijunLow = candles.GetRange(lastIndex + 1 - kijunPeriod, kijunPeriod).Min(c => c.Low); + result.KijunSen = (kijunHigh + kijunLow) / 2; + } + + // 선행스팬 A 계산: '현재봉 시점'에 표시하려면 26봉 전 값(미래 26봉이 아니라)으로 계산 + if (lastIndex + 1 >= kijunPeriod && lastIndex + 1 >= tenkanPeriod && lastIndex - displacement >= 0) + { + // 26봉 전(과거) 봉 기준으로 전환선과 기준선 다시 계산 + var spanIndex = lastIndex - displacement; + + var spanTenkanHigh = candles.GetRange(spanIndex + 1 - tenkanPeriod, tenkanPeriod).Max(c => c.High); + var spanTenkanLow = candles.GetRange(spanIndex + 1 - tenkanPeriod, tenkanPeriod).Min(c => c.Low); + var spanTenkan = (spanTenkanHigh + spanTenkanLow) / 2; + + var spanKijunHigh = candles.GetRange(spanIndex + 1 - kijunPeriod, kijunPeriod).Max(c => c.High); + var spanKijunLow = candles.GetRange(spanIndex + 1 - kijunPeriod, kijunPeriod).Min(c => c.Low); + var spanKijun = (spanKijunHigh + spanKijunLow) / 2; + + result.SenkouSpanA = (spanTenkan + spanKijun) / 2; + } + + // 선행스팬 B 계산 (26봉 전 기준 52봉 범위) + if (lastIndex + 1 >= senkouBPeriod && lastIndex - displacement >= senkouBPeriod - 1) + { + int spanIndex = lastIndex - displacement; + var spanBHigh = candles.GetRange(spanIndex + 1 - senkouBPeriod, senkouBPeriod).Max(c => c.High); + var spanBLow = candles.GetRange(spanIndex + 1 - senkouBPeriod, senkouBPeriod).Min(c => c.Low); + result.SenkouSpanB = (spanBHigh + spanBLow) / 2; + } + + // 후행스팬은 26봉 전 종가 (시간 그대로) + if (lastIndex - displacement >= 0) + { + result.ChikouSpan = candles[lastIndex - displacement].Close; + } + + return result; + } } }