diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore index 51e3c16..844658c 100644 --- a/.gitignore +++ b/.gitignore @@ -400,5 +400,7 @@ FodyWeavers.xsd *.sln.iml *.png +Charts/* -Secrets.cs \ No newline at end of file +Secrets.cs +.aider* diff --git a/Brian.cs b/Brian.cs index 3b813c7..dd5ecae 100644 --- a/Brian.cs +++ b/Brian.cs @@ -1,4 +1,5 @@ using BinanceExchange.API.Client; +using BinanceExchange.API.Enums; using System; using System.Collections.Generic; using System.Linq; @@ -11,14 +12,49 @@ namespace SignalsTest { private static BinanceClient m_client; - public static BinanceClient Client { get { if(m_client == null) + public static BinanceClient Client + { + get + { + if (m_client == null) { m_client = new BinanceClient(new ClientConfiguration() { ApiKey = Secrets.BINANCE_API_PK, SecretKey = Secrets.BINANCE_API_SK, }); - } return m_client; - } } + } + return m_client; + } + } + + public static List watches = new List(); + + public static CoinWatch GetCoinWatch(string symbol, KlineInterval interval){ + foreach(CoinWatch watch in watches){ + if(watch.pair == symbol && watch.interval == interval){ + return watch; + } + } + + return watches[0]; + } + + public static CoinWatch GetCoinWatchLongest(string symbol){ + CoinWatch match = watches[0]; + foreach(CoinWatch watch in watches){ + if(watch.pair == symbol){ + if(match.pair != watch.pair){ + match = watch; + } + + if(Utils.GetMinutesForInterval(match.interval) < Utils.GetMinutesForInterval(watch.interval)){ + match = watch; + } + } + } + + return match; + } } } diff --git a/CoinWatch.cs b/CoinWatch.cs index b57ae66..d254bea 100644 --- a/CoinWatch.cs +++ b/CoinWatch.cs @@ -24,6 +24,9 @@ namespace SignalsTest public KlineInterval interval = KlineInterval.FifteenMinutes; public List candles=new List(); public List reports = new List(); + public List resistancePoints = new List(); + public List supportPoints = new List(); + public event EventHandler PriceUpdated; bool usingCache; public CoinWatch(string pair,int index, KlineInterval _interval = KlineInterval.FifteenMinutes , bool useCache=false) @@ -98,6 +101,10 @@ namespace SignalsTest reports.Add(report); } + + resistancePoints =Confirmations.GetResistanceLevels(reports); + supportPoints = Confirmations.GetSupportiveLevels(reports); + Console.WriteLine($"Drawing chart for {reports.Count} candles, Coin: {symbol}, ceil:{max}, floor:{min}"); // Console.WriteLine("Picasso did his job"); @@ -133,10 +140,10 @@ namespace SignalsTest } if(triggers!= ""){ - Picasso.DrawChart(reports, max ,min, maxVol, symbol); + Picasso.DrawChart(reports, max ,min, maxVol, symbol, interval); - string message = $"`{symbol}` {triggers} \nCurrent price: {reports[reports.Count-1].Close}"; - Messenger.instance.ScheduleMessage(message, symbol); + string message = $"`{symbol}` [{Utils.GetMinutesForInterval(interval)}m] {triggers} \nCurrent price: {reports[reports.Count-1].Close}"; + Messenger.instance.ScheduleMessage(message, symbol, Utils.GetMinutesForInterval(interval).ToString()); } } diff --git a/CoinsList.cs b/CoinsList.cs index 2905700..17a7019 100644 --- a/CoinsList.cs +++ b/CoinsList.cs @@ -1,39 +1,41 @@ +using BinanceExchange.API.Enums; + public static class CoinsList{ - public static List symbols= [ - "BTCUSDT", - "ADAUSDT", - "AIXBTUSDT", - "XRPUSDT", - "XLMUSDT", - "SOLUSDT", - "AVAXUSDT", - "ENAUSDT", - "HIVEUSDT", - "STEEMUSDT", - "MOVEUSDT", - "DOGEUSDT", - "PEPEUSDT", - "ACTUSDT", - "STGUSDT", - "ONEUSDT", - "LINKUSDT", - "ARUSDT", - "RUNEUSDT", - "USUALUSDT", - "ZKUSDT", - "JUPUSDT", - "LUNAUSDT", - "DUSKUSDT", - "SUIUSDT", - "INJUSDT", - "FILUSDT", - "GRTUSDT", - "HBARUSDT", - "CFXUSDT", - "TLMUSDT", - "NEARUSDT", - "FORTHUSDT", - "ETHUSDT", - "PNUTUSDT" - ]; + public static Dictionary> symbols = new Dictionary> { + { "BTCUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "ADAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "AIXBTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "XRPUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "XLMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "SOLUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "AVAXUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "ENAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "HIVEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "STEEMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "MOVEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "DOGEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "PEPEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "ACTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "STGUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "ONEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "LINKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "ARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "RUNEUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "USUALUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "ZKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "JUPUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "LUNAUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "DUSKUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "SUIUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "INJUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "FILUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "GRTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "HBARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "CFXUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "TLMUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "NEARUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "FORTHUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "ETHUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] }, + { "PNUTUSDT", [KlineInterval.FifteenMinutes, KlineInterval.OneHour] } + }; } \ No newline at end of file diff --git a/CustomTypes.cs b/CustomTypes.cs new file mode 100644 index 0000000..84a1e1a --- /dev/null +++ b/CustomTypes.cs @@ -0,0 +1,7 @@ +public class MessageQueueItem{ + public string symbol; + public string interval; + public string filename=>"Charts/"+symbol+interval+".png"; + + public string text; +} \ No newline at end of file diff --git a/Messenger.cs b/Messenger.cs index 094eac8..7e4af2a 100644 --- a/Messenger.cs +++ b/Messenger.cs @@ -23,10 +23,14 @@ namespace SignalsTest public const string chatId = "@doralockscryptosignals"; public const string chatId_test = "@SignalTestPrivate"; - public Dictionary messagesSchedule = new Dictionary(); + public List messagesSchedule = new List(); - public void ScheduleMessage(string text, string filename=""){ - messagesSchedule.Add(text,filename); + public void ScheduleMessage(string text, string symbol="", string interval=""){ + messagesSchedule.Add(new MessageQueueItem(){ + symbol = symbol, + interval=interval, + text=text + }); } public async void InitializeScheduler(){ @@ -39,25 +43,26 @@ namespace SignalsTest continue; } - string message = messagesSchedule.Keys.ElementAt(0); - string filename = messagesSchedule[message]; - + MessageQueueItem queueItem = messagesSchedule[0]; + string message = queueItem.text; + string filename = queueItem.filename; + string symbol = queueItem.symbol; try{ - if(filename.Length < 2){ + if(symbol.Length < 2){ await botClient.SendTextMessageAsync(_chatId, message); }else{ - FileStream fsSource = new FileStream($"{filename}.png", FileMode.Open, FileAccess.Read); + FileStream fsSource = new FileStream(filename, FileMode.Open, FileAccess.Read); InputFile photo = InputFile.FromStream(fsSource); - string url = $"https://www.tradingview.com/chart/?symbol=BINANCE%3A{filename}"; + string url = $"https://www.tradingview.com/chart/?symbol=BINANCE%3A{symbol}"; // string formattedMessage = message.Replace($"`{filename}`", $"`{filename}`[Trading View]({url})"); string formattedMessage = message.Replace( - $"`{filename}`", - $"{filename}" + $"`{symbol}`", + $"{symbol}" ); await botClient.SendPhotoAsync(_chatId, photo,caption: formattedMessage,parseMode: ParseMode.Html); } - messagesSchedule.Remove(message); + messagesSchedule.Remove(queueItem); }catch(Exception e){ Console.WriteLine($"Error occured while sending {message} to {filename}"); Console.WriteLine(e.ToString()); diff --git a/Picasso.cs b/Picasso.cs index 1541ea2..838ae56 100644 --- a/Picasso.cs +++ b/Picasso.cs @@ -8,12 +8,13 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; using Newtonsoft.Json; using System.Diagnostics; +using BinanceExchange.API.Enums; public class Picasso{ - public static void DrawChart(List reports, decimal max, decimal min, decimal volMax, string filename="test"){ + public static void DrawChart(List reports, decimal max, decimal min, decimal volMax, string filename="test", KlineInterval interval = KlineInterval.FifteenMinutes){ // float height = (float)Math.Ceiling(max - min) * 2f; // float width = ((float)height / 9f) * 16f; decimal heightRange = max-min; @@ -125,15 +126,16 @@ public class Picasso{ #endregion #region LINES - List resistancePoints = Confirmations.GetResistanceLevels(reports); - foreach(decimal point in resistancePoints){ + + CoinWatch longestWatch = Brian.GetCoinWatchLongest(filename); + + foreach(decimal point in longestWatch.resistancePoints){ float yVal = ((float)(point-min) * heightMultiplier) + candlesOffset; // Console.WriteLine($"{filename} res at {yVal/height}"); img.Mutate(ctx=> ctx.DrawLine(Color.Red,1f, [new PointF(0, yVal), new PointF(width, yVal)])); } - List supPoints = Confirmations.GetSupportiveLevels(reports); - foreach(decimal point in supPoints){ + foreach(decimal point in longestWatch.supportPoints){ float yVal = ((float)(point-min) * heightMultiplier) + candlesOffset; // Console.WriteLine($"{filename} sup at {yVal/height}"); img.Mutate(ctx=> ctx.DrawLine(Color.Green,1f, [new PointF(0, yVal), new PointF(width, yVal)])); @@ -150,7 +152,6 @@ public class Picasso{ IPath stochFast = GetPath(reports, "Stochastic", widthMultiplier, (float)volumeHeightMultiplier/100f, 0, 0); IPath stochK3 = GetPath(reports, "StochasticK3", widthMultiplier, (float)volumeHeightMultiplier/100f, 0, 0); - IPath stPath = GetPath(reports, "ST", widthMultiplier,(float)heightMultiplier, (float)min, candlesOffset); #endregion //NewChart @@ -161,6 +162,7 @@ public class Picasso{ throw new Exception($"Couldn't find font {TextFont}"); var font = fontFamily.CreateFont(TextFontSize, FontStyle.Regular); + int intervalInMins = Utils.GetMinutesForInterval(interval); img.Mutate(ctx => ctx.Draw(Color.Yellow, 2, sma20Path). Draw(Color.Purple, 2, sma50Path). @@ -169,9 +171,9 @@ public class Picasso{ Draw(Color.Cyan, 3,stochFast). Draw(Color.Orange, 3, stochK3). Flip(FlipMode.Vertical). - DrawText(filename, font,new Color(Rgba32.ParseHex("#FFFFFF")), new PointF(10,10))); + DrawText($"{filename}- {intervalInMins}m", font,new Color(Rgba32.ParseHex("#FFFFFF")), new PointF(10,10))); - img.Save($"{filename}.png"); + img.Save($"Charts/{filename}{intervalInMins}.png"); } } diff --git a/Program.cs b/Program.cs index e491344..0c5e7d7 100644 --- a/Program.cs +++ b/Program.cs @@ -1,20 +1,31 @@ -using BinanceExchange.API.Models.WebSocket; +using BinanceExchange.API.Enums; +using BinanceExchange.API.Models.WebSocket; using SignalsTest; class Program { static ManualResetEvent _quitEvent = new ManualResetEvent(false); - static List watches = new List(); private static void Main(string[] args) { Console.WriteLine("Initializing Messiah"); Messenger.instance.ScheduleMessage("Rebooted bot"); - for (int i=0; i < CoinsList.symbols.Count; i++){ - CoinWatch btcWatch = new CoinWatch(CoinsList.symbols[i], i); - btcWatch.PriceUpdated += CoinWatch_OnPriceUpdate; + // for (int i=0; i < CoinsList.symbols.Count; i++){ + // CoinWatch btcWatch = new CoinWatch(CoinsList.symbols[i], i); + // btcWatch.PriceUpdated += CoinWatch_OnPriceUpdate; - watches.Add(btcWatch); + // watches.Add(btcWatch); + // } + Brian.watches = new List(); + int i=0; + foreach(KeyValuePair> symbol in CoinsList.symbols){ + foreach(KlineInterval interval in symbol.Value){ + CoinWatch btcWatch = new CoinWatch(symbol.Key, i, interval); + btcWatch.PriceUpdated += CoinWatch_OnPriceUpdate; + + Brian.watches.Add(btcWatch); + i++; + } } CheckSuccess(); _quitEvent.WaitOne(); @@ -22,17 +33,17 @@ class Program async static void CheckSuccess(){ - await Task.Delay(120000); + await Task.Delay(160000); List failedList = new List(); string commasList = ""; - foreach(CoinWatch coin in watches){ + foreach(CoinWatch coin in Brian.watches){ if(!coin.kickstarted){ failedList.Add(coin.pair); - commasList += coin.pair + ", "; + commasList += $"{coin.pair}[{coin.interval}]" + ", "; } } - string msg = $"Reboot complete: {failedList.Count} Failed out of {watches.Count}. Failed list: \n{commasList}"; + string msg = $"Reboot complete: {failedList.Count} Failed out of {Brian.watches.Count}. Failed list: \n{commasList}"; Console.WriteLine(msg); Messenger.instance.ScheduleMessage(msg); }