From f70aff1a1ee4e149d18427e2f99f2bc71949d397 Mon Sep 17 00:00:00 2001 From: kyh <1329522@naver.com> Date: Fri, 23 May 2025 12:44:23 +0900 Subject: [PATCH] =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BitgetApi/BitgetApi.csproj | 1 + BitgetApi/CommonProperty.cs | 9 ++ .../IndicatorProcess.cs | 123 ++++++++---------- .../SMABandConditionSetup.cs | 108 +++++++++++++++ 4 files changed, 173 insertions(+), 68 deletions(-) create mode 100644 BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs diff --git a/BitgetApi/BitgetApi.csproj b/BitgetApi/BitgetApi.csproj index 573d09d..7a4bcf9 100644 --- a/BitgetApi/BitgetApi.csproj +++ b/BitgetApi/BitgetApi.csproj @@ -112,6 +112,7 @@ + diff --git a/BitgetApi/CommonProperty.cs b/BitgetApi/CommonProperty.cs index 1c560e8..51eece9 100644 --- a/BitgetApi/CommonProperty.cs +++ b/BitgetApi/CommonProperty.cs @@ -48,6 +48,15 @@ namespace BitgetAPI MARGIN_보류 = 2 } + // SMA Band Type + public enum SMABandType + { + LongBuy = 0, + LongSell = 1, + ShortBuy = 2, + ShortSell = 3 + } + public ExchangePlace selectedChangePlace; public PaymentType selectedPaymentType; public BitgetProductType selectedBitgetProductType; diff --git a/BitgetApi/IndicatorConditionSetup/IndicatorProcess.cs b/BitgetApi/IndicatorConditionSetup/IndicatorProcess.cs index b12d444..0d921f5 100644 --- a/BitgetApi/IndicatorConditionSetup/IndicatorProcess.cs +++ b/BitgetApi/IndicatorConditionSetup/IndicatorProcess.cs @@ -30,7 +30,7 @@ namespace BitgetAPI.IndicatorConditionSetup return options.SelectAPI.AccessKey; } } - // = "bg_f93aa530288b6ec9d62095994c51972a"; + // = "bg_f93aa55288b6ec9d62095994c51972a"; private string YHKIM_SecreteKey { get @@ -133,6 +133,7 @@ namespace BitgetAPI.IndicatorConditionSetup IchimokuConditionSetup ichimokuConditionSetup = new IchimokuConditionSetup(); MovingAverageConditionSetup movingAverageConditionSetup = new MovingAverageConditionSetup(); TradingViewConditionSetup tradingViewConditionSetup = new TradingViewConditionSetup(); + SMABandConditionSetup sMABandConditionSetup = new SMABandConditionSetup(); signals_LU.Clear(); signals_SD.Clear(); signals_LD.Clear(); @@ -305,59 +306,59 @@ namespace BitgetAPI.IndicatorConditionSetup BuyShortStep[market] = 0; BuyLongStep[market] = 0; } - if (!(전저고점.ContainsKey(market + "30m"))) + if (!(전저고점.ContainsKey(market + "5m"))) continue; - #region 30분봉 처리 + #region 5분봉 처리 - var candleDatas_30 = findedMarket.candleDataList[MCandleTimeType.M30]; - var candleDatas_30_정렬 = candleDatas_30.OrderBy(i => i.Argument).ToList(); - var candleDatas_30_TempList = candleDatas_30.OrderBy(i => i.Argument).ToList(); - var candleData_30 = candleDatas_30.Find(i => i.Argument == candleDatas_30_정렬.Last().Argument); + var candleDatas_5 = findedMarket.candleDataList[MCandleTimeType.M5]; + var candleDatas_5_정렬 = candleDatas_5.OrderBy(i => i.Argument).ToList(); + var candleDatas_5_TempList = candleDatas_5.OrderBy(i => i.Argument).ToList(); + var candleData_5 = candleDatas_5.Find(i => i.Argument == candleDatas_5_정렬.Last().Argument); - if (candleData_30 == null) continue; + if (candleData_5 == null) continue; - ichimokuConditionSetup.calIchimokuData(candleDatas_30_정렬, candleData_30); - movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_30_정렬, candleData_30); + ichimokuConditionSetup.calIchimokuData(candleDatas_5_정렬, candleData_5); + movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_5_정렬, candleData_5); - candleDatas_30_정렬.RemoveAt(candleDatas_30_정렬.Count - 1); - var candleData_30_전봉 = candleDatas_30.Find(i => i.Argument == candleDatas_30_정렬.Last().Argument); + candleDatas_5_정렬.RemoveAt(candleDatas_5_정렬.Count - 1); + var candleData_5_전봉 = candleDatas_5.Find(i => i.Argument == candleDatas_5_정렬.Last().Argument); - if (candleData_30_전봉 == null) continue; - ichimokuConditionSetup.calIchimokuData(candleDatas_30_정렬, candleData_30_전봉); - movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_30_정렬, candleData_30_전봉); + if (candleData_5_전봉 == null) continue; + ichimokuConditionSetup.calIchimokuData(candleDatas_5_정렬, candleData_5_전봉); + movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_5_정렬, candleData_5_전봉); - candleDatas_30_정렬.RemoveAt(candleDatas_30_정렬.Count - 1); - var candleData_30_전전봉 = candleDatas_30.Find(i => i.Argument == candleDatas_30_정렬.Last().Argument); + candleDatas_5_정렬.RemoveAt(candleDatas_5_정렬.Count - 1); + var candleData_5_전전봉 = candleDatas_5.Find(i => i.Argument == candleDatas_5_정렬.Last().Argument); - if (candleData_30_전전봉 == null) continue; - ichimokuConditionSetup.calIchimokuData(candleDatas_30_정렬, candleData_30_전전봉); - movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_30_정렬, candleData_30_전전봉); + if (candleData_5_전전봉 == null) continue; + ichimokuConditionSetup.calIchimokuData(candleDatas_5_정렬, candleData_5_전전봉); + movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_5_정렬, candleData_5_전전봉); - candleDatas_30_정렬.RemoveAt(candleDatas_30_정렬.Count - 1); - var candleData_30_전전전봉 = candleDatas_30.Find(i => i.Argument == candleDatas_30_정렬.Last().Argument); + candleDatas_5_정렬.RemoveAt(candleDatas_5_정렬.Count - 1); + var candleData_5_전전전봉 = candleDatas_5.Find(i => i.Argument == candleDatas_5_정렬.Last().Argument); - if (candleData_30_전전전봉 == null) continue; - ichimokuConditionSetup.calIchimokuData(candleDatas_30_정렬, candleData_30_전전전봉); - movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_30_정렬, candleData_30_전전전봉); + if (candleData_5_전전전봉 == null) continue; + ichimokuConditionSetup.calIchimokuData(candleDatas_5_정렬, candleData_5_전전전봉); + movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_5_정렬, candleData_5_전전전봉); - candleDatas_30_정렬.RemoveAt(candleDatas_30_정렬.Count - 1); - var candleData_30_전전전전봉 = candleDatas_30.Find(i => i.Argument == candleDatas_30_정렬.Last().Argument); + candleDatas_5_정렬.RemoveAt(candleDatas_5_정렬.Count - 1); + var candleData_5_전전전전봉 = candleDatas_5.Find(i => i.Argument == candleDatas_5_정렬.Last().Argument); - if (candleData_30_전전전전봉 == null) continue; - ichimokuConditionSetup.calIchimokuData(candleDatas_30_정렬, candleData_30_전전전전봉); - movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_30_정렬, candleData_30_전전전전봉); + if (candleData_5_전전전전봉 == null) continue; + ichimokuConditionSetup.calIchimokuData(candleDatas_5_정렬, candleData_5_전전전전봉); + movingAverageConditionSetup.Cal_YHK_Downpublic(candleDatas_5_정렬, candleData_5_전전전전봉); #endregion if (영호조건식_상향돌파 || 영호조건식_하향돌파) { if (영호조건식_상향돌파) { - var price = candleData_30.Close; - var rate = (candleData_30.Close / candleData_30.Open) * 100 - 100; - var indicatorPrice = candleData_30.Open; - var indicatorRate = (candleData_30.Close / indicatorPrice) * 100 - 100; + var price = candleData_5.Close; + var rate = (candleData_5.Close / candleData_5.Open) * 100 - 100; + var indicatorPrice = candleData_5.Open; + var indicatorRate = (candleData_5.Close / indicatorPrice) * 100 - 100; var description = string.Format("{0} 72,24,4 상승", strInterval); var signalData = signals_SD.Find(i => i.MarketName.Equals(market) && i.Description.Equals(description)); @@ -369,12 +370,7 @@ namespace BitgetAPI.IndicatorConditionSetup //사는 시점 if (!UpBuyList.ContainsKey(market)) { - - var buyFlag = false; - - - - if (buyFlag) + if (sMABandConditionSetup.Calculate(candleDatas_5_TempList, SMABandType.LongBuy)) { BuyLongStep[market] = 0; // 없으면 생성 @@ -399,8 +395,8 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyDate", nowTime }, { "Price", price }, { "MCandleTimeType", (int)interval }, - { "CandleTime", candleData_30.Argument }, - { "Candle", candleData_30 } + { "CandleTime", candleData_5.Argument }, + { "Candle", candleData_5 } }; UpBuyList.Add(market, buyDic); @@ -428,7 +424,7 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyDate", nowTime }, { "Price", price }, { "MCandleTimeType", (int)interval }, - { "Candle", candleData_30 }, + { "Candle", candleData_5 }, { "Size", size }, { "orderId", orderDic["orderId"] } }; @@ -447,7 +443,7 @@ namespace BitgetAPI.IndicatorConditionSetup var flag = (bool)buyItem["Flag"]; if (interval < MCandleTimeType.H1) { - var buyTime = ((DateTime)buyItem["BuyDate"]).AddMinutes(30); + var buyTime = ((DateTime)buyItem["BuyDate"]).AddMinutes(5); if (nowTime < buyTime) continue; } @@ -459,10 +455,7 @@ namespace BitgetAPI.IndicatorConditionSetup continue; } - var sellFlag = false; - - - if (sellFlag) + if (sMABandConditionSetup.Calculate(candleDatas_5_TempList, SMABandType.LongSell)) { double.TryParse(buyItem["Price"].ToString(), out var buyPrice); var resultRate = ((price - buyPrice) / buyPrice) * 100; @@ -477,7 +470,7 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyCandle", buyItem["Candle"] }, { "SellDate", nowTime}, { "SellPrice", price }, - { "SellCandle", candleData_30 }, + { "SellCandle", candleData_5 }, { "Rate", resultRate } }; @@ -518,7 +511,7 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyCandle", buyItem["Candle"] }, { "SellDate", nowTime}, { "SellPrice", price}, - { "SellCandle", candleData_30 }, + { "SellCandle", candleData_5 }, { "Rate", resultRateData } }; ResultList.Add(orderResult); @@ -538,10 +531,10 @@ namespace BitgetAPI.IndicatorConditionSetup } if (영호조건식_하향돌파) { - var price = candleData_30.Close; - var rate = (candleData_30.Close / candleData_30.Open) * 100 - 100; - var indicatorPrice = candleData_30.Open; - var indicatorRate = (candleData_30.Close / indicatorPrice) * 100 - 100; + var price = candleData_5.Close; + var rate = (candleData_5.Close / candleData_5.Open) * 100 - 100; + var indicatorPrice = candleData_5.Open; + var indicatorRate = (candleData_5.Close / indicatorPrice) * 100 - 100; var description = string.Format("{0} 72,24,4 하락", strInterval); var signalData = signals_SD.Find(i => i.MarketName.Equals(market) && i.Description.Equals(description)); @@ -551,11 +544,7 @@ namespace BitgetAPI.IndicatorConditionSetup //사는 시점 if (!DownBuyList.ContainsKey(market)) { - - var buyFlag = false; - - - if (buyFlag) + if (sMABandConditionSetup.Calculate(candleDatas_5_TempList, SMABandType.ShortBuy)) { BuyShortStep[market] = 0; // 없으면 생성 @@ -579,8 +568,8 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyDate", nowTime }, { "Price", price }, { "MCandleTimeType", (int)interval }, - { "CandleTime", candleData_30.Argument }, - { "Candle", candleData_30 } + { "CandleTime", candleData_5.Argument }, + { "Candle", candleData_5 } }; DownBuyList.Add(market, buyDic); @@ -608,7 +597,7 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyDate", nowTime }, { "Price", price }, { "MCandleTimeType", (int)interval }, - { "Candle", candleData_30 }, + { "Candle", candleData_5 }, { "Size", size }, { "orderId", orderDic["orderId"] } }; @@ -627,7 +616,7 @@ namespace BitgetAPI.IndicatorConditionSetup var flag = (bool)buyItem["Flag"]; if (interval < MCandleTimeType.H1) { - var buyTime = ((DateTime)buyItem["BuyDate"]).AddMinutes(30); + var buyTime = ((DateTime)buyItem["BuyDate"]).AddMinutes(5); if (nowTime < buyTime) continue; } @@ -638,10 +627,8 @@ namespace BitgetAPI.IndicatorConditionSetup if (nowTime < buyTime) continue; } - var sellFlag = false; - - if (sellFlag) + if (sMABandConditionSetup.Calculate(candleDatas_5_TempList, SMABandType.ShortSell)) { BuyShortStep[market] = 0; double.TryParse(buyItem["Price"].ToString(), out var buyPrice); @@ -657,7 +644,7 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyCandle", buyItem["Candle"] }, { "SellDate", nowTime}, { "SellPrice", price }, - { "SellCandle", candleData_30 }, + { "SellCandle", candleData_5 }, { "Rate", resultRate } }; @@ -699,7 +686,7 @@ namespace BitgetAPI.IndicatorConditionSetup { "BuyCandle", buyItem["Candle"] }, { "SellDate", nowTime}, { "SellPrice", price }, - { "SellCandle", candleData_30 }, + { "SellCandle", candleData_5 }, { "Rate", resultRateData } }; ResultList.Add(orderResult); diff --git a/BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs b/BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs new file mode 100644 index 0000000..f7b4723 --- /dev/null +++ b/BitgetApi/IndicatorConditionSetup/SMABandConditionSetup.cs @@ -0,0 +1,108 @@ +using BitgetAPI.Data; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static BitgetAPI.CommonProperty; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.ToolTip; + +namespace BitgetApi.IndicatorConditionSetup +{ + public class SMABandConditionSetup + { + private readonly int rangeLookback = 10; + private readonly double rangeThresholdPercent = 1.0; + private readonly int bandLength = 12; + private readonly int recentTouchLookback = 5; + private int? sma4LongTouchBar = null; + private int? sma4ShortTouchBar = null; + + public bool Calculate(List candles, SMABandType type) + { + var currentIndex = candles.Count - 1; + var close = candles.Select(t => t.Close).ToList(); + var high = candles.Select(t=>t.High).ToList(); + var low = candles.Select(t => t.Low).ToList(); + + if (currentIndex < 72 || close.Count <= currentIndex || high.Count <= currentIndex || low.Count <= currentIndex) + return false; + + // === SMA(4) 계산 === + double sma4 = close.GetRange(currentIndex - 3, 4).Average(); + double prevSma4 = new[] { close[currentIndex - 4], close[currentIndex - 1], close[currentIndex - 2], close[currentIndex - 3] }.Average(); + double prev2Sma4 = new[] { close[currentIndex - 4], close[currentIndex - 5], close[currentIndex - 2], close[currentIndex - 3] }.Average(); + + // === SMA(4) 밴드 계산 === + List sma4Series = new List(); + for (int i = currentIndex - bandLength + 1; i <= currentIndex; i++) + { + var s = close.GetRange(i - 3, 4).Average(); + sma4Series.Add(s); + } + double sma4High = sma4Series.Max(); + double sma4Low = sma4Series.Min(); + double sma4Middle = sma4Series.Average(); + + // 이전 SMA4 밴드 + List prevSma4Series = new List(); + for (int i = currentIndex - bandLength + 1; i <= currentIndex; i++) + { + var s = new[] { close[i - 4], close[i - 1], close[i - 2], close[i - 3] }.Average(); + prevSma4Series.Add(s); + } + double prevSma4High = prevSma4Series.Max(); + double prevSma4Low = prevSma4Series.Min(); + double prevSma4Middle = prevSma4Series.Average(); + + List prev2Sma4Series = new List(); + for (int i = currentIndex - bandLength + 1; i <= currentIndex; i++) + { + var s = new[] { close[i - 4], close[i - 5], close[i - 2], close[i - 3] }.Average(); + prev2Sma4Series.Add(s); + } + double prev2Sma4Middle = prev2Sma4Series.Average(); + double prev2Sma4High = prev2Sma4Series.Max(); + double prev2Sma4Low = prev2Sma4Series.Min(); + + + // === 횡보 구간 === + double highestHigh = high.Skip(currentIndex - rangeLookback + 1).Take(rangeLookback).Max(); + double lowestLow = low.Skip(currentIndex - rangeLookback + 1).Take(rangeLookback).Min(); + double priceRange = highestHigh - lowestLow; + bool isSideways = priceRange > (highestHigh * rangeThresholdPercent / 100.0); + + // === 최근 터치 === + if (sma4 == sma4Low) + sma4LongTouchBar = currentIndex; + + bool isLongRecentTouch = sma4LongTouchBar.HasValue && currentIndex - sma4LongTouchBar.Value >= recentTouchLookback; + + if (sma4 == sma4High) + sma4ShortTouchBar = currentIndex; + + bool isShortRecentTouch = sma4ShortTouchBar.HasValue && currentIndex - sma4ShortTouchBar.Value >= recentTouchLookback; + + // === 시그널 계산 === + if (type == SMABandType.LongBuy) + { + return isLongRecentTouch && isSideways && prevSma4 < sma4 && sma4High == sma4 && + prevSma4High > prevSma4 && (prev2Sma4Middle - prevSma4Middle) > (prevSma4Middle - sma4Middle); + } + else if(type == SMABandType.ShortBuy) + { + return isShortRecentTouch && prevSma4 < sma4 && sma4High == sma4 && + prevSma4High > prevSma4 && (prev2Sma4Middle - prevSma4Middle) > (prevSma4Middle - sma4Middle); + } + else if(type == SMABandType.LongSell) + { + return (!isSideways && prevSma4High > prevSma4) || (isSideways && prev2Sma4High > prev2Sma4 && prevSma4High > prevSma4); + } + else if(type == SMABandType.ShortSell) + { + return (!isSideways && prevSma4Low < prevSma4) || (isSideways && prev2Sma4Low < prev2Sma4 && prevSma4Low < prevSma4); + } + return false; + } + } +}