diff --git a/CoinWatch.cs b/CoinWatch.cs index decdcbe..ca16dd1 100644 --- a/CoinWatch.cs +++ b/CoinWatch.cs @@ -78,6 +78,8 @@ namespace SignalsTest if (Utils.GetMinutesForInterval(interval) > 50) { rounds = 10; + }else if(Utils.GetMinutesForInterval(interval) > 5){ + rounds = 5; } for (int i = 0; i < rounds; i++) @@ -148,6 +150,11 @@ namespace SignalsTest resistancePoints = Confirmations.GetResistanceLevels(reports,SMA:lineCalSMA); supportPoints = Confirmations.GetSupportiveLevels(reports, SMA:lineCalSMA); + for(int i=0; i < reports.Count;i++){ + reports[i].isBullFlag = Confirmations.IsBullFlag(reports, i); + reports[i].RelativeVolumeIndex = Confirmations.GetRelativeVolumeIndex(reports,i); + } + Console.WriteLine($"Drawing chart for {reports.Count} candles, Coin: {symbol}, ceil:{max}, floor:{min}"); // Console.WriteLine("Picasso did his job"); @@ -192,7 +199,9 @@ namespace SignalsTest if(reports[reports.Count-2].isInverseHammer){ triggers+=$"Inverse Hammer candle appeared\n"; } - + // if(reports[reports.Count-2].isBullFlag){ + // triggers+=$"Possible Bull Flag\n"; + // } if (triggers != "") { List limitedReports = new List(reports); diff --git a/CoinsList.cs b/CoinsList.cs index 3b61403..2065bdd 100644 --- a/CoinsList.cs +++ b/CoinsList.cs @@ -5,37 +5,37 @@ public static class CoinsList{ { "BTCUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, { "ADAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, { "AIXBTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "XRPUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "XLMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "SOLUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "AVAXUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "ENAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "HIVEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "STEEMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "MOVEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "DOGEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "PEPEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "ACTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "STGUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "ONEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "LINKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "ARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "RUNEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "USUALUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "ZKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "JUPUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "LUNAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "DUSKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "SUIUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "INJUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "FILUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "GRTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "HBARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "CFXUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "TLMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "NEARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "FORTHUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "ETHUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, - { "PNUTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] } + // { "XRPUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "XLMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "SOLUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "AVAXUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "ENAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "HIVEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "STEEMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "MOVEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "DOGEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "PEPEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "ACTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "STGUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "ONEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "LINKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "ARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "RUNEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "USUALUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "ZKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "JUPUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "LUNAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "DUSKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "SUIUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "INJUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "FILUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "GRTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "HBARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "CFXUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "TLMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "NEARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "FORTHUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "ETHUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] }, + // { "PNUTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour, KlineInterval.OneDay, KlineInterval.OneWeek] } }; } \ No newline at end of file diff --git a/Confirmations.cs b/Confirmations.cs index 0a18088..de2f856 100644 --- a/Confirmations.cs +++ b/Confirmations.cs @@ -156,4 +156,59 @@ public static class Confirmations return formattedResistances; } + + public static float GetRelativeVolumeIndex(List reports, int currentIndex){ + decimal totalVolume = 0; + foreach(TAReport report in reports){ + totalVolume+=report.candle.Volume; + } + + float avgVol = (float)totalVolume/(float)reports.Count; + + return (float)reports[currentIndex].candle.Volume / avgVol; + } + + public static bool IsBullFlag(List reports, int currentIndex, int lookbackPeriod = 20) + { + if (currentIndex < lookbackPeriod) return false; + + // Find the flagpole + decimal highestPoint = 0; + decimal lowestPoint = decimal.MaxValue; + int highestPointIndex = 0; + + // Look for a sharp upward move (flagpole) + for (int i = currentIndex - lookbackPeriod; i <= currentIndex; i++) + { + if (reports[i].candle.High > highestPoint) + { + highestPoint = reports[i].candle.High; + highestPointIndex = i; + } + if (reports[i].candle.Low < lowestPoint) + { + lowestPoint = reports[i].candle.Low; + } + } + + // Calculate flagpole height + decimal flagpoleHeight = highestPoint - lowestPoint; + if (flagpoleHeight / lowestPoint < 0.03m) return false; // Minimum 3% move + + // Check for consolidation pattern after flagpole + decimal upperTrendStart = highestPoint; + decimal lowerTrendStart = reports[highestPointIndex].candle.Low; + + decimal upperTrendEnd = reports[currentIndex].candle.High; + decimal lowerTrendEnd = reports[currentIndex].candle.Low; + + // Verify parallel downward sloping trend lines + bool isDownwardSloping = upperTrendEnd < upperTrendStart && lowerTrendEnd < lowerTrendStart; + bool isParallel = Math.Abs((upperTrendEnd - upperTrendStart) - (lowerTrendEnd - lowerTrendStart)) / flagpoleHeight < 0.2m; + + // Volume should typically decrease during flag formation + bool isVolumeDecreasing = reports[currentIndex].candle.Volume < reports[highestPointIndex].candle.Volume; + + return isDownwardSloping && isParallel && isVolumeDecreasing; + } } \ No newline at end of file diff --git a/Indicators.cs b/Indicators.cs index f2d23b0..f1af322 100644 --- a/Indicators.cs +++ b/Indicators.cs @@ -1,4 +1,5 @@ -using BinanceExchange.API.Models.Response; +using System.Diagnostics; +using BinanceExchange.API.Models.Response; public static class Indicators { @@ -114,24 +115,75 @@ public static class Indicators return ATR; } - public static decimal getRSI(List responses, int index, int period = 14){ - decimal avgGain = 0; - decimal avgLoss =0; - if(index < 2){return 0;} - int length = 0; - for(int i=index; i > index - period && i > 0; i--){ - length++; - avgGain += responses[i].Close - responses[i-1].Close; - avgLoss += responses[i-1].Close - responses[i].Close; - } - if(length <=0){return 0;} - avgGain /= length; - avgLoss /= length; + public static decimal getVwapWeekly(List responses, int index){ + if(index < 15){return 0;} - decimal f =(1.0m+(avgGain/(avgLoss==0 ? 1: avgLoss))); //Avoiding dividing by 0 - if(f<=0){return 0;} - return 100.0m - (100.0m/f); + decimal cumPrice = 0; + decimal cumVolume = 0; + for(int i=index; i > 0; i--){ + decimal typicalPrice = (responses[i].High + responses[i].Low + responses[i].Close) / 3; + cumPrice += typicalPrice * responses[i].Volume; + cumVolume += responses[i].Volume; + if(responses[i].CloseTime.DayOfWeek == DayOfWeek.Sunday && responses[i].CloseTime.Hour == 23 && responses[i].CloseTime.Minute == 59){ + break; + } + } + + return cumPrice/ cumVolume; } + public static decimal getVwapMonthly(List responses, int index){ + if(index < 15){return 0;} + + decimal cumPrice = 0; + decimal cumVolume = 0; + for(int i=index; i > 0; i--){ + decimal typicalPrice = (responses[i].High + responses[i].Low + responses[i].Close) / 3; + cumPrice += typicalPrice * responses[i].Volume; + cumVolume += responses[i].Volume; + if(responses[i].CloseTime.Date.Day == 1 && responses[i].OpenTime.Hour == 0 && responses[i].OpenTime.Minute == 0){ + break; + } + } + + return cumPrice/ cumVolume; + } + + public static decimal getRSI(List responses, int index, int period = 14) +{ + decimal avgGain = 0; + decimal avgLoss = 0; + + if (index < period) + { + return 0; + } + + for (int i = index; i > index - period; i--) + { + decimal change = responses[i].Close - responses[i - 1].Close; + if (change > 0) + { + avgGain += change; + } + else + { + avgLoss += Math.Abs(change); + } + } + + avgGain /= period; + avgLoss /= period; + + if (avgLoss == 0) + { + return 100; // If there's no loss, RSI is 100 + } + + decimal rs = avgGain / avgLoss; + decimal rsi = 100 - (100 / (1 + rs)); + + return rsi; +} public static bool didMACDLineCrossSignalLine(decimal[] MACDs, decimal[] Signals, int index, int period = 7, decimal threshold = (decimal)0.1) { diff --git a/Picasso.cs b/Picasso.cs index 82095c9..88426da 100644 --- a/Picasso.cs +++ b/Picasso.cs @@ -76,12 +76,15 @@ public class Picasso{ volumePoints[0] = new PointF(i * widthMultiplier, height); volumePoints[1] = new PointF(i * widthMultiplier, height - ((float)reports[i].candle.Volume / (float)volMax) * volumeHeightMultiplier); - Color candleColor = reports[i].candle.Close > reports[i].candle.Open ? Color.Green : Color.Red; + Color candleColor = reports[i].candle.Close > reports[i].candle.Open ? Color.Green : Color.Red; // Traditional Bar + Color rsiColor = reports[i].RSI50 > 50 ? Color.Green : Color.Red; // RSI Bar + // Console.WriteLine(reports[i].RSI); img.Mutate(ctx=> ctx. - DrawLine(candleColor, 10, points). DrawLine(candleColor, 3, rangePoints). - DrawLine(candleColor, 10, volumePoints) + DrawLine(candleColor, 10, points). + DrawLine(rsiColor, 8, points). + DrawLine(candleColor, (reports[i].RelativeVolumeIndex > 1 ? 12: 5), volumePoints) ); #endregion @@ -156,6 +159,34 @@ public class Picasso{ PointF squarePosition = new PointF(i * widthMultiplier - squareSize/2, highVal + 50); img.Mutate(ctx => ctx.Fill(Color.White, new RectangleF(squarePosition, new SizeF(squareSize, squareSize)))); } + + // if (reports[i].isBullFlag) + // { + // // Draw a flag symbol + // float flagWidth = 10f; + // float flagHeight = 15f; + // float poleHeight = 25f; + + // // Position flag below the candle + // PointF flagBasePosition = new PointF(i * widthMultiplier, lowVal + 10); + + // // Draw flag pole + // img.Mutate(ctx => ctx.DrawLine( + // Color.White, + // 1f, + // new PointF(flagBasePosition.X, flagBasePosition.Y), + // new PointF(flagBasePosition.X, flagBasePosition.Y - poleHeight) + // )); + + // // Draw flag triangle + // PointF[] flagPoints = new PointF[] { + // new PointF(flagBasePosition.X, flagBasePosition.Y - poleHeight), + // new PointF(flagBasePosition.X + flagWidth, flagBasePosition.Y - poleHeight + flagHeight/2), + // new PointF(flagBasePosition.X, flagBasePosition.Y - poleHeight + flagHeight) + // }; + + // img.Mutate(ctx => ctx.DrawPolygon(Color.White, 1f, flagPoints)); + // } } // Console.WriteLine("Getting paths"); // object t = GetPropByName(reports[reports.Count- 1], "SMA7"); @@ -201,6 +232,8 @@ public class Picasso{ IPath sma20Path = GetPath(reports, "SMA20", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset); IPath sma50Path = GetPath(reports, "SMA50", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset); IPath sma200Path = GetPath(reports, "SMA200", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset); + IPath vWapWeeklyPath = GetPath(reports, (Utils.GetMinutesForInterval(interval) > 60? "VwapMonthly" : "VwapWeekly") , widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset); + IPath stochFast = GetPath(reports, "Stochastic", widthMultiplier, (float)volumeHeightMultiplier/100f, 0); IPath stochK3 = GetPath(reports, "StochasticK3", widthMultiplier, (float)volumeHeightMultiplier/100f, 0); @@ -212,10 +245,12 @@ public class Picasso{ int intervalInMins = Utils.GetMinutesForInterval(interval); string intervalText = intervalInMins > (60 * 23) ? $"{intervalInMins/(60*24)}D" : $"{intervalInMins}m"; - img.Mutate(ctx => ctx.Draw(Color.Yellow, 2, sma20Path). - Draw(Color.Purple, 2, sma50Path). - Draw(Color.Cyan, 2, sma200Path). - Draw(Color.Green, 3, stPath). + img.Mutate(ctx => ctx. + // Draw(Color.Yellow, 2, sma20Path). + // Draw(Color.Purple, 2, sma50Path). + // Draw(Color.Cyan, 2, sma200Path). + Draw(Color.DarkRed,5, vWapWeeklyPath). + // Draw(Color.Green, 3, stPath). Draw(Color.Cyan, 3,stochFast). Draw(Color.Orange, 3, stochK3). DrawText($"{filename}- {intervalText}", titleFont,new Color(Rgba32.ParseHex("#FFFFFF")), new PointF(10,10))); diff --git a/TAReport.cs b/TAReport.cs index 320e614..7c06886 100644 --- a/TAReport.cs +++ b/TAReport.cs @@ -33,19 +33,27 @@ namespace SignalsTest public decimal ATR10; public decimal AVGDiff = 0; + + public decimal VwapWeekly=0; + public decimal VwapMonthly = 0; + public decimal STHigh; public decimal STLow; public decimal ST; public decimal RSI = 0; + public decimal RSI50 = 0; + public int TWS =0; public int TBC = 0; public bool isHammer,isInverseHammer = false; + public bool isBullFlag =false; public decimal Stochastic = 0; public decimal StochasticK3 = 0; + public float RelativeVolumeIndex = 0; public bool STUp = false; public bool TGOR=false; @@ -79,6 +87,7 @@ namespace SignalsTest report.MACD = Indicators.getMACD(response, 12, 26, index); report.RSI = Indicators.getRSI(response, index); + report.RSI50 = Indicators.getRSI(response, index, 50); if (history == null) { @@ -100,6 +109,9 @@ namespace SignalsTest report.ATR10 = Indicators.getATR(response, 10, index, ATR10History); report.ATR14 = Indicators.getATR(response, 14, index, ATR14History); + report.VwapWeekly = Indicators.getVwapWeekly(response, index); + report.VwapMonthly = Indicators.getVwapMonthly(response, index); + // SuperTrend Multiplier decimal multiplier = 3;