This commit is contained in:
Sewmina 2025-01-09 13:40:55 +05:30
commit 831e17c68b
16 changed files with 1412 additions and 0 deletions

404
.gitignore vendored Normal file
View File

@ -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

26
.vscode/launch.json vendored Normal file
View File

@ -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"
}
]
}

41
.vscode/tasks.json vendored Normal file
View File

@ -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"
}
]
}

24
Brian.cs Normal file
View File

@ -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;
} }
}
}

157
CoinWatch.cs Normal file
View File

@ -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<KlineCandleStickResponse> candles=new List<KlineCandleStickResponse>();
public List<TAReport> reports = new List<TAReport>();
public event EventHandler<BinanceTradeData> 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<TAReport>();
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;
}
});
}
}
}

37
CoinsList.cs Normal file
View File

@ -0,0 +1,37 @@
public static class CoinsList{
public static List<string> 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"
];
}

169
Indicators.cs Normal file
View File

@ -0,0 +1,169 @@
using BinanceExchange.API.Models.Response;
public static class Indicators
{
public static decimal getSMA(List<KlineCandleStickResponse> 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<decimal> 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<KlineCandleStickResponse> 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<decimal> 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<KlineCandleStickResponse> responses, int shortPeriod, int longPeriod, int curIndex)
{
return getEMA(responses, curIndex, curIndex, shortPeriod) - getEMA(responses, curIndex, curIndex, longPeriod);
}
public static decimal getATR(List<KlineCandleStickResponse> responses, int period, int curIndex, List<decimal> 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<KlineCandleStickResponse> 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<KlineCandleStickResponse> 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;
}
}

75
Messenger.cs Normal file
View File

@ -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<string,string> messagesSchedule = new Dictionary<string, string>();
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<UpdateType>() // receive all update types except ChatMember related updates
};
InitializeScheduler();
}
}
}

51
Patterns.cs Normal file
View File

@ -0,0 +1,51 @@
using BinanceExchange.API.Models.Response;
public static class Patterns{
public static bool GetTGOR(List<KlineCandleStickResponse> 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;
}
}

118
Picasso.cs Normal file
View File

@ -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<TAReport> 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<Rgba32>((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<TAReport> 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<string,string> dic = JsonConvert.DeserializeObject<Dictionary<string,string>>(json) ?? new Dictionary<string, string>();
// Console.WriteLine(dic[propName]);
return dic[propName];
}
}

44
Program.cs Normal file
View File

@ -0,0 +1,44 @@
using BinanceExchange.API.Models.WebSocket;
using SignalsTest;
class Program
{
static ManualResetEvent _quitEvent = new ManualResetEvent(false);
static List<CoinWatch> watches = new List<CoinWatch>();
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<string> failedList = new List<string>();
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);
}
}

5
Secrets.cs.bkp Normal file
View File

@ -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 = "";
}

16
SignalsTestCmd.csproj Normal file
View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.0" />
<PackageReference Include="BinanceDotNet" Version="4.12.0" />
<PackageReference Include="Telegram.Bot" Version="19.0.0" />
</ItemGroup>
</Project>

24
SignalsTestCmd.sln Normal file
View File

@ -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

156
TAReport.cs Normal file
View File

@ -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<KlineCandleStickResponse> response, int index, List<TAReport> 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<decimal> MACDHistory = new List<decimal>();
List<decimal> ATR10History = new List<decimal>();
List<decimal> ATR14History = new List<decimal>();
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);
}
}

65
Utils.cs Normal file
View File

@ -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;
}
}
}