commit 831e17c68b6932f6c6b6a0150d63dc6cc1836006 Author: Sewmina Date: Thu Jan 9 13:40:55 2025 +0530 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51e3c16 --- /dev/null +++ b/.gitignore @@ -0,0 +1,404 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml + +*.png + +Secrets.cs \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d705226 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/net7.0/SignalsTestCmd.dll", + "args": [], + "cwd": "${workspaceFolder}", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..d0dc2b2 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/SignalsTestCmd.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/SignalsTestCmd.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/SignalsTestCmd.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/Brian.cs b/Brian.cs new file mode 100644 index 0000000..3b813c7 --- /dev/null +++ b/Brian.cs @@ -0,0 +1,24 @@ +using BinanceExchange.API.Client; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SignalsTest +{ + public class Brian + { + private static BinanceClient m_client; + + 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; + } } + } +} diff --git a/CoinWatch.cs b/CoinWatch.cs new file mode 100644 index 0000000..273a168 --- /dev/null +++ b/CoinWatch.cs @@ -0,0 +1,157 @@ +using BinanceExchange.API.Client; +using BinanceExchange.API.Enums; +using BinanceExchange.API.Models.Request; +using BinanceExchange.API.Models.Response; +using BinanceExchange.API.Models.WebSocket; +using BinanceExchange.API.Websockets; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static System.Runtime.InteropServices.JavaScript.JSType; + +namespace SignalsTest +{ + + + public class CoinWatch + { + public bool kickstarted = false; + public string pair; + public int index; + public KlineInterval interval = KlineInterval.FifteenMinutes; + public List candles=new List(); + public List reports = new List(); + public event EventHandler PriceUpdated; + bool usingCache; + public CoinWatch(string pair,int index, KlineInterval _interval = KlineInterval.FifteenMinutes , bool useCache=false) + { + this.pair = pair; + this.index = index; + interval = _interval; + usingCache = useCache; + Init(); + } + + void Init() + { + if(usingCache){ + if(File.Exists(AppDomain.CurrentDomain.BaseDirectory+"/"+pair+".txt")){ + try{ + + }catch{ + + } + } + } + InitLivestream(); + KeepUpdatingCandles(); + // TestCallbacks(); + } + + async void TestCallbacks() + { + while (true) + { + await Task.Delay(1000); + PriceUpdated?.Invoke(this, new BinanceTradeData() { BestAskPrice=0 }); + } + } + + async Task UpdateCandles() + { + GetKlinesCandlesticksRequest req = new GetKlinesCandlesticksRequest(); + req.Interval = interval; + req.Symbol = pair; + req.EndTime = DateTime.Now; + req.Limit = 100; + + candles = await Brian.Client.GetKlinesCandlesticks(req); + Console.WriteLine($"Done gettings Kandles for {req.Symbol}, {candles.Count} kandles retreived"); + kickstarted = true; + Analyze(req.Symbol); + } + + async void Analyze(string symbol){ + int[] mas = new int[] { 7, 12, 25 }; + reports = new List(); + decimal max = 0; + decimal min = 100000000; + decimal maxVol = 0; + + + for(int i=0; i < candles.Count; i++){ + + TAReport report = TAReport.Generate(pair, Utils.GetMinutesForInterval(interval) ,candles, i,reports); + if(candles[i].Close > max){ + max = candles[i].Close; + } + if(candles[i].Close < min){ + min = candles[i].Close; + } + + if(candles[i].Volume > maxVol){ + maxVol = candles[i].Volume; + } + reports.Add(report); + + } + Console.WriteLine($"Drawing chart for {reports.Count} candles, Coin: {symbol}, ceil:{max}, floor:{min}"); + + // Console.WriteLine("Picasso did his job"); + // Console.WriteLine(reports[reports.Count-1].toJson()); + bool isStSwitch=false; + + for(int i=0; i< 2; i++){ + bool _isStSwitch = reports[reports.Count-1-i].STUp != reports[reports.Count-i-2].STUp; + + if(_isStSwitch){ + isStSwitch=true; + break; + } + } + string triggers = ""; + + if(isStSwitch){ + triggers += "Switched SuperTrend\n"; + } + if(reports[reports.Count-1].TGOR && !reports[reports.Count-2].TGOR){//Ignore if two in row + triggers += "Price dropped\n"; + } + + if(triggers!= ""){ + Picasso.DrawChart(reports, max ,min, maxVol, symbol); + + string message = $"`{symbol}` {triggers} \nCurrent price: {reports[reports.Count-1].Close}"; + Messenger.instance.ScheduleMessage(message, symbol); + } + + } + + async void KeepUpdatingCandles() + { + int delay = (index * 1000); + await Task.Delay(delay); + + UpdateCandles(); + while(true) + { + await Task.Delay(60000 + delay); + Console.WriteLine($"Loop call, ({index}) , {DateTime.Now.Minute} = {DateTime.Now.Minute % Utils.GetMinutesForInterval(interval)} "); + if (DateTime.Now.Minute % Utils.GetMinutesForInterval(interval) == 0) { UpdateCandles(); } + } + } + + void InitLivestream() + { + var manualWebsocket = new InstanceBinanceWebSocketClient(Brian.Client); + var socketId = manualWebsocket.ConnectToIndividualSymbolTickerWebSocket(pair, data => + { + if(candles.Count > 0){ + candles[candles.Count - 1].Close = data.BestAskPrice; + } + }); + } + } +} diff --git a/CoinsList.cs b/CoinsList.cs new file mode 100644 index 0000000..08bc6fc --- /dev/null +++ b/CoinsList.cs @@ -0,0 +1,37 @@ +public static class CoinsList{ + public static List symbols= [ + "BTCUSDT", + "ADAUSDT", + "XRPUSDT", + "XLMUSDT", + "SOLUSDT", + "AVAXUSDT", + "ENAUSDT", + "HIVEUSDT", + "STEEMUSDT", + "MOVEUSDT", + "DOGEUSDT", + "PEPEUSDT", + "RLCUSDT", + "ACTUSDT", + "STGUSDT", + "ONEUSDT", + "LINKUSDT", + "ARUSDT", + "RUNEUSDT", + "USUALUSDT", + "ZKUSDT", + "JUPUSDT", + "LUNAUSDT", + "DUSKUSDT", + "SUIUSDT", + "INJUSDT", + "FILUSDT", + "GRTUSDT", + "HBARUSDT", + "CFXUSDT", + "TLMUSDT", + "NEARUSDT", + "FORTHUSDT" + ]; +} \ No newline at end of file diff --git a/Indicators.cs b/Indicators.cs new file mode 100644 index 0000000..2a99b7d --- /dev/null +++ b/Indicators.cs @@ -0,0 +1,169 @@ +using BinanceExchange.API.Models.Response; + +public static class Indicators +{ + + public static decimal getSMA(List responses, int curIndex, int interval, int startIndex = 0) + { + // curIndex = Math.Clamp(curIndex,0, responses.Count); + // decimal total = 0; + // for(int i=curIndex-interval; i < curIndex; i++){ + // total += responses[i].Close; + // } + // // decimal length = (curIndex-startIndex); if(length <= 0){length=1;} + // // decimal N = (length / (decimal)interval); + // Console.WriteLine($"SMA Cacl ({interval}) : {total}/{interval} = {total/interval}"); + + // return total / interval; + + decimal total = 0; + for (int x = (curIndex > 1500) ? 1500 : 0; x < interval; x++) + { + total += (curIndex - x >= 0) ? responses[curIndex - x].Close : (decimal)0; + } + return total / (decimal)interval; + } + + public static decimal getSMA(List responses, int curIndex, int interval, int startIndex = 0) + { + decimal total = 0; + for (int x = (curIndex > 1500) ? 1500 : 0; x < interval; x++) + { + total += (curIndex - x >= 0) ? responses[curIndex - x] : (decimal)0; + } + return total / (decimal)interval; + } + + public static decimal getEMA(List responses, int curIndex, int startIndex, int periods, decimal? prevEma = null) + { + if (curIndex < 1) { return 0; } + if (startIndex - curIndex > 1500) { return 0; }; if (curIndex >= responses.Count) { return 0; }; + + + // decimal N = (decimal)curIndex / (decimal)periods; + + decimal multiplier = (decimal)2 / ((decimal)(periods + 1)); + decimal firstHalf = responses[curIndex].Close * multiplier; //First half of the equation + decimal secondHalf = getEMA(responses, curIndex - 1, startIndex, periods) * (1 - multiplier); //Second half of the equation + // decimal secondHalf = (prevEma ?? getSMA(responses, curIndex-1, periods)) * (1- multiplier); //Second half of the equation + + return firstHalf + secondHalf; + } + + public static decimal getEMA(List responses, int curIndex, int startIndex, int periods) + { + if (curIndex < 1) { return 0; } + if (startIndex - curIndex > 1500) { return 0; }; + if (curIndex >= responses.Count) { return 0; }; + + // decimal N = (decimal)curIndex / (decimal)periods; + + decimal multiplier = (decimal)2 / ((decimal)(periods + 1)); + + decimal firstHalf = responses[curIndex] * multiplier; //First half of the equation + decimal secondHalf = getEMA(responses, curIndex - 1, startIndex, periods) * (1 - multiplier); //Second half of the equation + // decimal secondHalf = (prevEma ?? getSMA(responses, curIndex-1, periods)) * (1- multiplier); //Second half of the equation + + return firstHalf + secondHalf; + } + + public static decimal getMACD(List responses, int shortPeriod, int longPeriod, int curIndex) + { + return getEMA(responses, curIndex, curIndex, shortPeriod) - getEMA(responses, curIndex, curIndex, longPeriod); + } + + public static decimal getATR(List responses, int period, int curIndex, List prevATRs) + { + if (curIndex <= 0 || curIndex > responses.Count) { return 0; } + + if (curIndex >= responses.Count) { return 0; }; + + decimal TR = responses[curIndex].High - responses[curIndex].Low; + if (curIndex > 1) + { + decimal tr2 = responses[curIndex].High - responses[curIndex - 1].Close; + decimal tr3 = responses[curIndex].Low - responses[curIndex - 1].Close; + if (tr2 > TR) + { + TR = tr2; + } + if (tr3 > TR) + { + TR = tr3; + } + } + + if (curIndex < period) + { + //get just the TR + return TR; + } + decimal prevATR = 0; + if (prevATRs.Count > 0) + { + // int start = curIndex - period; + // for (int i = start; i < curIndex; i++) + // { + // prevATR += prevATRs[i]; + // } + + // prevATR = (prevATR) / (decimal)(curIndex-start); + prevATR = prevATRs[curIndex - 1]; + } + decimal ATR = (decimal)((prevATR * (decimal)(period - 1)) + TR) / (decimal)period; + 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; + + decimal f =(1.0m+(avgGain/(avgLoss==0 ? 1: avgLoss))); //Avoiding dividing by 0 + if(f<=0){return 0;} + return 100.0m - (100.0m/f); + } + + public static bool didMACDLineCrossSignalLine(decimal[] MACDs, decimal[] Signals, int index, int period = 7, decimal threshold = (decimal)0.1) + { + int counter = 0; + if (index >= MACDs.Length || index >= Signals.Length) { return false; } + if (index - period < 0) { return false; }; + for (int i = index; i > index - period; i--) + { + if (MACDs[i] < Signals[i]) + { + counter++; + } + } + bool wasMACDUnder = counter > ((float)period / 2f); + return wasMACDUnder && MACDs[index] > Signals[index]; + } + + public static decimal getAvgDiff(List responses, int i) + { + decimal avgDiff = 0; + + if (i > 10) + { + for (int k = 1; k < 6; k++) + { + avgDiff += responses[i - k].Close; + } + + avgDiff = avgDiff / (decimal)5; + avgDiff = responses[i].Close - avgDiff; + } + + return avgDiff; + } +} \ No newline at end of file diff --git a/Messenger.cs b/Messenger.cs new file mode 100644 index 0000000..e577aed --- /dev/null +++ b/Messenger.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Telegram.Bot; +using Telegram.Bot.Exceptions; +using Telegram.Bot.Polling; +using Telegram.Bot.Types; +using Telegram.Bot.Types.Enums; +namespace SignalsTest +{ + public class Messenger + { + + TelegramBotClient botClient; + + private static Messenger m_instance; + public static Messenger instance { get { if (m_instance == null) { m_instance = new Messenger(); } return m_instance; } } + + public const string chatId = "@doralockscryptosignals"; + public const string chatId_test = "@SignalTestPrivate"; + + public Dictionary messagesSchedule = new Dictionary(); + + public void ScheduleMessage(string text, string filename=""){ + messagesSchedule.Add(text,filename); + } + + public async void InitializeScheduler(){ + string _chatId = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? chatId_test : chatId; + + while(true){ + await Task.Delay(3500); + if(messagesSchedule.Count <=0){ + continue; + } + + string message = messagesSchedule.Keys.ElementAt(0); + string filename = messagesSchedule[message]; + + try{ + if(filename.Length < 2){ + await botClient.SendTextMessageAsync(_chatId, message); + }else{ + FileStream fsSource = new FileStream($"{filename}.png", FileMode.Open, FileAccess.Read); + InputFile photo = InputFile.FromStream(fsSource); + await botClient.SendPhotoAsync(_chatId, photo,caption: message); + } + + messagesSchedule.Remove(message); + }catch(Exception e){ + Console.WriteLine($"Error occured while sending {message} to {filename}"); + Console.WriteLine(e.ToString()); + } + + } + } + + public Messenger() + { + botClient = new TelegramBotClient(Secrets.TELEGRAM_BOT_TOKEN); + using CancellationTokenSource cts = new(); + + // StartReceiving does not block the caller thread. Receiving is done on the ThreadPool. + ReceiverOptions receiverOptions = new() + { + AllowedUpdates = Array.Empty() // receive all update types except ChatMember related updates + }; + + InitializeScheduler(); + } + } +} diff --git a/Patterns.cs b/Patterns.cs new file mode 100644 index 0000000..793e987 --- /dev/null +++ b/Patterns.cs @@ -0,0 +1,51 @@ + +using BinanceExchange.API.Models.Response; + +public static class Patterns{ + public static bool GetTGOR(List responses, int curIndex){ + if(curIndex < 10){ + return false; + } + + + int greenCount =0; + + for(int i=curIndex-4; i <= curIndex; i++){ + + if(responses[i].Close < responses[i].Open){ + if(greenCount > 2){ + //Red after 3 greens + int bullRunFlag = 0; + //This is an abomniation + if(responses[i-1].Close > responses[i-2].Close){ + bullRunFlag++; + } + if(responses[i-2].Close > responses[i-3].Close){ + bullRunFlag++; + } + if(responses[i-3].Close > responses[i-4].Close){ + bullRunFlag++; + } + if(responses[i-4].Close > responses[i-5].Close){ + bullRunFlag++; + } + + if(responses[i-5].Close > responses[i-6].Close){ + bullRunFlag++; + } + + if(bullRunFlag > 2 && responses[i-1].Close > responses[i-1].Open){//It was a bull run + return true; + } + + } + greenCount =0; + }else{ + greenCount++; + } + } + + + return false; + } +} \ No newline at end of file diff --git a/Picasso.cs b/Picasso.cs new file mode 100644 index 0000000..1a3723b --- /dev/null +++ b/Picasso.cs @@ -0,0 +1,118 @@ +using SignalsTest; +using System; +using SixLabors.Fonts; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Newtonsoft.Json; + +public class Picasso{ + public static void DrawChart(List reports, decimal max, decimal min, decimal volMax, string filename="test"){ + // float height = (float)Math.Ceiling(max - min) * 2f; + // float width = ((float)height / 9f) * 16f; + decimal heightRange = max-min; + float height=1080; + float width = 1920; + float widthMultiplier = width / (float)reports.Count; + float heightMultiplier = (height/ (float)heightRange) * 0.6f; + + float candlesOffset = 0.3f * height; + float volumeSize = 0.2f * height; + using (Image img = new Image((int)width + 100, (int)height)){ + + //Draw Candles + for(int i=0; i < reports.Count; i++){ + // img.Mutate(ctx=> ctx.DrawLine())) + PointF[] points = new PointF[2]; + + float openVal1 = (float)(reports[i].candle.Open - min) * heightMultiplier; + float closeVal1 = (float)(reports[i].candle.Close - min)* heightMultiplier; + points[0] =new PointF(i * widthMultiplier, openVal1 + candlesOffset); + points[1] =new PointF(i * widthMultiplier, closeVal1+ candlesOffset); + + PointF[] rangePoints = new PointF[2]; + + float highVal = (float)(reports[i].candle.High-min)* heightMultiplier; + float lowVal = (float)(reports[i].candle.Low-min)* heightMultiplier; + + rangePoints[0] = new PointF(i * widthMultiplier, highVal+ candlesOffset); + rangePoints[1] = new PointF(i * widthMultiplier, lowVal+ candlesOffset); + + PointF[] volumePoints = new PointF[2]; + volumePoints[0] = new PointF(i * widthMultiplier, 0); + volumePoints[1] = new PointF(i * widthMultiplier, ((float)reports[i].candle.Volume / (float)volMax) * volumeSize); + + Color candleColor = reports[i].candle.Close > reports[i].candle.Open ? Color.Green : Color.Red; + + img.Mutate(ctx=> ctx. + DrawLine(candleColor, 10, points). + DrawLine(candleColor, 3, rangePoints). + DrawLine(candleColor, 10, volumePoints) + ); + + if(reports[i].TGOR){ + PointF[] tgorPoints = new PointF[2]; + tgorPoints[0] = new PointF(i * widthMultiplier, lowVal+ candlesOffset); + tgorPoints[1] = new PointF(i * widthMultiplier, lowVal- 15+ candlesOffset); + img.Mutate(ctx=>ctx.DrawLine(Color.Blue, 15, tgorPoints)); + } + + } + // Console.WriteLine("Getting paths"); + // object t = GetPropByName(reports[reports.Count- 1], "SMA7"); + // Console.WriteLine(t); + // Console.WriteLine(float.Parse(t.ToString())); + + //Overlay + IPath sma7Path = GetPath(reports, "SMA7", widthMultiplier,(float)heightMultiplier, (float)min, candlesOffset); + IPath sma25Path = GetPath(reports, "SMA25", widthMultiplier,(float)heightMultiplier, (float)min, candlesOffset); + IPath stPath = GetPath(reports, "ST", widthMultiplier,(float)heightMultiplier, (float)min, candlesOffset); + + //NewChart + FontFamily fontFamily; + float TextFontSize = 64f; + string TextFont = "Noto Sans Mono"; + if (!SystemFonts.TryGet(TextFont, out fontFamily)) + throw new Exception($"Couldn't find font {TextFont}"); + + var font = fontFamily.CreateFont(TextFontSize, FontStyle.Regular); + + img.Mutate(ctx => ctx.Draw(Color.Yellow, 2, sma7Path). + Draw(Color.Purple, 2, sma25Path). + Draw(Color.Green, 3, stPath). + Flip(FlipMode.Vertical). + DrawText(filename, font,new Color(Rgba32.ParseHex("#000000FF")), new PointF(10,10))); + + img.Save($"{filename}.png"); + } + } + + static IPath GetPath(List reports, string propName, float widthMultiplier,float heightMultiplier, float min, float offset =0){ + PathBuilder builder = new PathBuilder(); + builder.SetOrigin(new PointF(0,0)); + for(int i=0; i < reports.Count; i++){ + float newVal = float.Parse(GetPropByName(reports[i],propName).ToString() ?? "0"); + // Console.WriteLine( newVal); + + float prevVal = i > 0 ? float.Parse(GetPropByName(reports[i-1],propName).ToString() ?? "0") : 0; + // Console.WriteLine( prevVal); + float preValY =( prevVal - min) * heightMultiplier ; + float newValY=(newVal - min) * heightMultiplier; + PointF prevPoint = new PointF((i-1)* widthMultiplier, (preValY) +offset); + PointF newPoint = new PointF(i * widthMultiplier, (newValY) + offset); + // Console.WriteLine(newValY); + builder.AddLine(prevPoint, newPoint); + } + + return builder.Build(); + } + + static object GetPropByName(TAReport report, string propName){ + string json = report.toJson(); + Dictionary dic = JsonConvert.DeserializeObject>(json) ?? new Dictionary(); + // Console.WriteLine(dic[propName]); + return dic[propName]; + } +} \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..5c1926a --- /dev/null +++ b/Program.cs @@ -0,0 +1,44 @@ +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; + + watches.Add(btcWatch); + } + CheckSuccess(); + _quitEvent.WaitOne(); + } + + + async static void CheckSuccess(){ + await Task.Delay(120000); + + List failedList = new List(); + string commasList = ""; + foreach(CoinWatch coin in watches){ + if(!coin.kickstarted){ + failedList.Add(coin.pair); + commasList += coin.pair + ", "; + } + } + string msg = $"{failedList.Count} Failed out of {watches.Count}. Failed list: \n{commasList}"; + Console.WriteLine(msg); + Messenger.instance.ScheduleMessage(msg); + } + + private static void CoinWatch_OnPriceUpdate(Object? sender,BinanceTradeData data) + { + // Console.WriteLine(data.BestAskPrice); + } +} \ No newline at end of file diff --git a/Secrets.cs.bkp b/Secrets.cs.bkp new file mode 100644 index 0000000..679a006 --- /dev/null +++ b/Secrets.cs.bkp @@ -0,0 +1,5 @@ +public static class Secrets{ + public const string BINANCE_API_PK = ""; + public const string BINANCE_API_SK = ""; + public const string TELEGRAM_BOT_TOKEN = ""; +} \ No newline at end of file diff --git a/SignalsTestCmd.csproj b/SignalsTestCmd.csproj new file mode 100644 index 0000000..07c93d2 --- /dev/null +++ b/SignalsTestCmd.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + diff --git a/SignalsTestCmd.sln b/SignalsTestCmd.sln new file mode 100644 index 0000000..d4dbadb --- /dev/null +++ b/SignalsTestCmd.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SignalsTestCmd", "SignalsTestCmd.csproj", "{D2CBB26B-D565-FEEC-1B33-96397A5C8843}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D2CBB26B-D565-FEEC-1B33-96397A5C8843}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D2CBB26B-D565-FEEC-1B33-96397A5C8843}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D2CBB26B-D565-FEEC-1B33-96397A5C8843}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D2CBB26B-D565-FEEC-1B33-96397A5C8843}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {4DB50F9B-E9B0-4716-B6ED-52066149F1F8} + EndGlobalSection +EndGlobal diff --git a/TAReport.cs b/TAReport.cs new file mode 100644 index 0000000..2f6d120 --- /dev/null +++ b/TAReport.cs @@ -0,0 +1,156 @@ +using BinanceExchange.API.Models.Response; +using Newtonsoft.Json; +using SignalsTest; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SignalsTest +{ + public class TAReport + { + public string pair; + public int interval; + [JsonIgnore] + public KlineCandleStickResponse candle; + public decimal Close; + public decimal Open; + public decimal High; + public decimal Low; + public DateTime CloseTime; + public decimal SMA7; + public decimal SMA25; + public decimal SMA99; + + public decimal EMA7, EMA25, EMA99; + + public decimal MACD = 0; + public decimal EMACD = 0; + + public decimal ATR14; + public decimal ATR10; + public decimal AVGDiff = 0; + + public decimal STHigh; + public decimal STLow; + public decimal ST; + + public decimal RSI = 0; + + public bool STUp = false; + public bool TGOR=false; + public static TAReport Generate(string _pair, int _interval, List response, int index, List history) + { + TAReport report = new TAReport(); + report.pair = _pair; + report.interval = _interval; + report.candle = response[index]; + report.Open = response[index].Open; + report.Close = response[index].Close; + report.High = response[index].High; + report.Low = response[index].Low; + report.CloseTime = response[index].CloseTime; + // foreach(int sma in m_SMA) + // { + // report.SMAs.Add(sma, Indicators.getSMA(response, index, sma)); + // } + + // foreach (int ema in m_EMA) + // { + // report.SMAs.Add(ema, Indicators.getEMA(response, index, 0, ema)); + // } + report.SMA7 = Indicators.getSMA(response, index, 7); + report.SMA25 = Indicators.getSMA(response, index, 25); + report.SMA99 = Indicators.getSMA(response, index, 99); + + report.EMA7 = Indicators.getEMA(response, index, 0, 7); + report.EMA25 = Indicators.getEMA(response, index, 0, 25); + report.EMA99 = Indicators.getEMA(response, index, 0, 99); + report.MACD = Indicators.getMACD(response, 12, 26, index); + + report.RSI = Indicators.getRSI(response, index); + + if (history == null) + { + return report; + } + //MACD Signal + List MACDHistory = new List(); + List ATR10History = new List(); + List ATR14History = new List(); + + foreach (TAReport item in history) + { + MACDHistory.Add(item.MACD); + ATR10History.Add(item.ATR10); + ATR14History.Add(item.ATR14); + } + report.EMACD = Indicators.getEMA(MACDHistory, index, 0, 9); + + report.ATR10 = Indicators.getATR(response, 10, index, ATR10History); + report.ATR14 = Indicators.getATR(response, 14, index, ATR14History); + + // SuperTrend Multiplier + decimal multiplier = 3; + + // Basic Upper and Lower Bands + decimal basicUpperBand = (response[index].High + response[index].Low) / 2 + multiplier * report.ATR14; + decimal basicLowerBand = (response[index].High + response[index].Low) / 2 - multiplier * report.ATR10; + + // Initialize Final Upper and Lower Bands + decimal finalUpperBand = basicUpperBand; + decimal finalLowerBand = basicLowerBand; + + // Adjust Final Upper and Lower Bands based on previous history + if (index > 0) + { + finalUpperBand = (history[index - 1].Close <= history[index - 1].STHigh) + ? Math.Min(basicUpperBand, history[index - 1].STHigh) + : basicUpperBand; + + finalLowerBand = (history[index - 1].Close >= history[index - 1].STLow) + ? Math.Max(basicLowerBand, history[index - 1].STLow) + : basicLowerBand; + } + + bool currentTrendUp = true; + + if (index > 0) + { + if (history[index - 1].STUp) // If the previous trend was up + { + currentTrendUp = (response[index].Close > finalLowerBand); // Price > Final Lower Band => Stay up + } + else // If the previous trend was down + { + currentTrendUp = (response[index].Close >= finalUpperBand); // Price >= Final Upper Band => Flip to up + } + } + + + // Assign SuperTrend values to the report + report.STHigh = finalUpperBand; + report.STLow = finalLowerBand; + report.STUp = currentTrendUp; + report.ST = currentTrendUp ? finalLowerBand : finalUpperBand; + + report.TGOR= Patterns.GetTGOR(response, index); + + return report; + } + + + } +} + + +public static class CustomExtensions +{ + + public static string toJson(this TAReport report) + { + return JsonConvert.SerializeObject(report, Formatting.Indented); + } +} \ No newline at end of file diff --git a/Utils.cs b/Utils.cs new file mode 100644 index 0000000..353ec2e --- /dev/null +++ b/Utils.cs @@ -0,0 +1,65 @@ +using BinanceExchange.API.Enums; +using BinanceExchange.API.Models.Response; +using BinanceExchange.API.Models.WebSocket; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SignalsTest +{ + public class Utils + { + public static int GetMinutesForInterval(KlineInterval interval) + + { + switch (interval) + { + case KlineInterval.OneMinute: + return 1; + case KlineInterval.ThreeMinutes: + return 3; + case KlineInterval.FiveMinutes: + return 5; + case KlineInterval.FifteenMinutes: + return 15; + case KlineInterval.ThirtyMinutes: + return 30; + case KlineInterval.OneHour: + return 60; + case KlineInterval.TwoHours: + return 120; + case KlineInterval.FourHours: + return 240; + case KlineInterval.EightHours: + return 60 * 8; + case KlineInterval.TwelveHours: + return 60 * 12; + case KlineInterval.SixHours: + return 60 * 6; + case KlineInterval.OneDay: + return 60 * 24; + case KlineInterval.ThreeDays: + return 60 * 24 * 3; + + default: + return 0; + } + } + + public static KlineCandleStickResponse KlineToResponse(BinanceKlineData data) + { + KlineCandleStickResponse response = new KlineCandleStickResponse(); + response.Open = data.Kline.Open; + response.Close = data.Kline.Close; + response.CloseTime = data.Kline.EndTime; + response.OpenTime = data.Kline.StartTime; + response.High = data.Kline.High; + response.Low = data.Kline.Low; + response.Volume = data.Kline.Volume; + + return response; + } + } +}