Longer time period for logner timeframes, averaged sup and res lines

This commit is contained in:
Sewmina 2025-01-16 11:27:43 +05:30
parent bf83a4b912
commit 77848e3be4
6 changed files with 188 additions and 62 deletions

View File

@ -56,5 +56,27 @@ namespace SignalsTest
return match;
}
public static List<decimal> GetCombinedResPoints(string symbol){
List<decimal> output = new List<decimal>();
foreach(CoinWatch watch in watches){
if(watch.pair == symbol){
output.AddRange(watch.resistancePoints);
}
}
return output;
}
public static List<decimal> GetCombinedSupPoints(string symbol){
List<decimal> output = new List<decimal>();
foreach(CoinWatch watch in watches){
if(watch.pair == symbol){
output.AddRange(watch.supportPoints);
}
}
return output;
}
}
}

View File

@ -22,14 +22,14 @@ namespace SignalsTest
public string pair;
public int index;
public KlineInterval interval = KlineInterval.FifteenMinutes;
public List<KlineCandleStickResponse> candles=new List<KlineCandleStickResponse>();
public List<KlineCandleStickResponse> candles = new List<KlineCandleStickResponse>();
public List<TAReport> reports = new List<TAReport>();
public List<decimal> resistancePoints = new List<decimal>();
public List<decimal> supportPoints = new List<decimal>();
public event EventHandler<BinanceTradeData> PriceUpdated;
bool usingCache;
public CoinWatch(string pair,int index, KlineInterval _interval = KlineInterval.FifteenMinutes , bool useCache=false)
public CoinWatch(string pair, int index, KlineInterval _interval = KlineInterval.FifteenMinutes, bool useCache = false)
{
this.pair = pair;
this.index = index;
@ -40,11 +40,16 @@ namespace SignalsTest
void Init()
{
if(usingCache){
if(File.Exists(AppDomain.CurrentDomain.BaseDirectory+"/"+pair+".txt")){
try{
if (usingCache)
{
if (File.Exists(AppDomain.CurrentDomain.BaseDirectory + "/" + pair + ".txt"))
{
try
{
}catch{
}
catch
{
}
}
@ -59,25 +64,56 @@ namespace SignalsTest
while (true)
{
await Task.Delay(1000);
PriceUpdated?.Invoke(this, new BinanceTradeData() { BestAskPrice=0 });
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 = 150;
candles = new List<KlineCandleStickResponse>();
candles = await Brian.Client.GetKlinesCandlesticks(req);
Console.WriteLine($"Done gettings Kandles for {req.Symbol}, {candles.Count} kandles retreived");
kickstarted = true;
Analyze(req.Symbol);
int rounds = 1;
int candleLimit = 150;
int intervalInMinutes = Utils.GetMinutesForInterval(interval);
if (Utils.GetMinutesForInterval(interval) > 50)
{
rounds = 10;
}
async void Analyze(string symbol){
for (int i = 0; i < rounds; i++)
{
DateTime startTime = DateTime.Now.AddMinutes(-intervalInMinutes * candleLimit * (rounds-i));
DateTime endTime = startTime.AddMinutes(intervalInMinutes * candleLimit);
GetKlinesCandlesticksRequest req = new GetKlinesCandlesticksRequest
{
Interval = interval,
Symbol = pair,
StartTime = startTime,
EndTime = endTime,
Limit = candleLimit
};
// Calculate and set the start time for this request
req.StartTime = req.EndTime.Value.AddMinutes(-intervalInMinutes * candleLimit);
Console.WriteLine($"Fetching kandles for {req.Symbol} (Round {i + 1}/{rounds}): {candles.Count} candles retrieved so far.");
var response = await Brian.Client.GetKlinesCandlesticks(req);
candles.AddRange(response);
// Update endTime for the next round
endTime = req.StartTime.Value;
// Respect the API rate limit
await Task.Delay(1500);
}
Console.WriteLine($"Completed fetching kandles. Total candles retrieved from {candles[0].OpenTime} to {candles[candles.Count-1].OpenTime}");
kickstarted = true;
Analyze(pair);
}
async void Analyze(string symbol)
{
int[] mas = new int[] { 7, 12, 25 };
reports = new List<TAReport>();
decimal max = 0;
@ -85,44 +121,54 @@ namespace SignalsTest
decimal maxVol = 0;
for(int i=0; i < candles.Count; i++){
for (int i = 0; i < candles.Count; i++)
{
TAReport report = TAReport.Generate(pair, Utils.GetMinutesForInterval(interval) ,candles, i,reports);
if(candles[i].Close > max){
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){
if (candles[i].Close < min)
{
min = candles[i].Close;
}
if(candles[i].Volume > maxVol){
if (candles[i].Volume > maxVol)
{
maxVol = candles[i].Volume;
}
reports.Add(report);
}
resistancePoints =Confirmations.GetResistanceLevels(reports);
supportPoints = Confirmations.GetSupportiveLevels(reports);
string lineCalSMA = "SMA20";
if(interval!=KlineInterval.FifteenMinutes){
lineCalSMA= "SMA50";
}
resistancePoints = Confirmations.GetResistanceLevels(reports,SMA:lineCalSMA);
supportPoints = Confirmations.GetSupportiveLevels(reports, SMA:lineCalSMA);
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;
bool isStSwitch = false;
for(int i=0; i< 2; i++){
bool _isStSwitch = reports[reports.Count-1-i].STUp != reports[reports.Count-i-2].STUp;
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;
if (_isStSwitch)
{
isStSwitch = true;
break;
}
}
string triggers = "";
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){
triggers+="test";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
triggers += "test";
}
// if(isStSwitch){
@ -132,17 +178,24 @@ namespace SignalsTest
// triggers += "Price dropped\n";
// }
if(reports[reports.Count-2].TWS > 1){
if (reports[reports.Count - 2].TWS > 1)
{
triggers += $"Three White Soldiers appeared";
}
if(reports[reports.Count-2].TBC > 1){
if (reports[reports.Count - 2].TBC > 1)
{
triggers += $"Three Black Crows appeared";
}
if(triggers!= ""){
Picasso.DrawChart(reports, max ,min, maxVol, symbol, interval);
if (triggers != "")
{
List<TAReport> limitedReports = new List<TAReport>(reports);
if(limitedReports.Count> 200){
limitedReports.RemoveRange(0,limitedReports.Count-150);
}
Picasso.DrawChart(limitedReports, symbol, interval);
string message = $"`{symbol}` [{Utils.GetMinutesForInterval(interval)}m] {triggers} \nCurrent price: {reports[reports.Count-1].Close}";
string message = $"`{symbol}` [{Utils.GetMinutesForInterval(interval)}m] {triggers} \nCurrent price: {reports[reports.Count - 1].Close}";
Messenger.instance.ScheduleMessage(message, symbol, Utils.GetMinutesForInterval(interval).ToString());
}
@ -154,7 +207,7 @@ namespace SignalsTest
await Task.Delay(delay);
UpdateCandles();
while(true)
while (true)
{
await Task.Delay(60000 + delay);
Console.WriteLine($"Loop call, ({index}) , {DateTime.Now.Minute} = {DateTime.Now.Minute % Utils.GetMinutesForInterval(interval)} ");
@ -167,7 +220,8 @@ namespace SignalsTest
var manualWebsocket = new InstanceBinanceWebSocketClient(Brian.Client);
var socketId = manualWebsocket.ConnectToIndividualSymbolTickerWebSocket(pair, data =>
{
if(candles.Count > 0){
if (candles.Count > 0)
{
candles[candles.Count - 1].Close = data.BestAskPrice;
}
});

View File

@ -58,7 +58,7 @@ public static class Confirmations
return HeightIncrement;
}
public static List<decimal> GetSupportiveLevels(List<TAReport> reports, float tolerance = 0.1f, int steps = 5){
public static List<decimal> GetSupportiveLevels(List<TAReport> reports, float tolerance = 0.1f, int steps = 5, string SMA="SMA20"){
List<decimal> allSups = new List<decimal>();
decimal highest = 0;
@ -76,8 +76,8 @@ public static class Confirmations
if(k <steps * 3){continue;} //not enough history
bool MATopNow = reports[k].SMA20 > reports[k].candle.High;
bool MATopBefore = reports[k-1].SMA20 > reports[k-1].candle.High;
bool MATopNow = decimal.Parse(Utils.GetPropByName(reports[k], SMA).ToString() ?? "0") > reports[k].candle.High;
bool MATopBefore = decimal.Parse(Utils.GetPropByName(reports[k-1], SMA).ToString() ?? "0") > reports[k-1].candle.High;
bool MACrossed = MATopBefore!=MATopNow;
if(!MACrossed){continue;}//No cross
@ -108,7 +108,7 @@ public static class Confirmations
return formattedSups;
}
public static List<decimal> GetResistanceLevels(List<TAReport> reports, float tolerance = 0.1f, int steps = 5){
public static List<decimal> GetResistanceLevels(List<TAReport> reports, float tolerance = 0.1f, int steps = 5, string SMA="SMA20"){
List<decimal> allResistances = new List<decimal>();
decimal highest = 0;
@ -126,8 +126,8 @@ public static class Confirmations
if(k <steps * 3){continue;} //not enough history
bool MATopNow = reports[k].SMA20 > reports[k].candle.High;
bool MATopBefore = reports[k-1].SMA20 > reports[k-1].candle.High;
bool MATopNow = decimal.Parse(Utils.GetPropByName(reports[k], SMA).ToString() ?? "0") > reports[k].candle.High;
bool MATopBefore = decimal.Parse(Utils.GetPropByName(reports[k-1], SMA).ToString() ?? "0") > reports[k-1].candle.High;
bool MACrossed = MATopBefore!=MATopNow;
if(!MACrossed){continue;}//No cross

View File

@ -148,4 +148,8 @@ public static class Patterns{
return true;
}
// public static bool isHammer(KlineCandleStickResponse response, float ratioThreshold = 0.3f){
// }
}

View File

@ -14,9 +14,27 @@ public class Picasso{
public static void DrawChart(List<TAReport> reports, decimal max, decimal min, decimal volMax, string filename="test", KlineInterval interval = KlineInterval.FifteenMinutes){
public static void DrawChart(List<TAReport> reports, string filename="test", KlineInterval interval = KlineInterval.FifteenMinutes){
// float height = (float)Math.Ceiling(max - min) * 2f;
// float width = ((float)height / 9f) * 16f;
decimal max = 0;
decimal min = 10000000000000;
decimal volMax =0;
foreach(TAReport report in reports){
if(max < report.High){
max = report.High;
}
if(min > report.Low){
min = report.Low;
}
if(volMax < report.candle.Volume){
volMax = report.candle.Volume;
}
}
decimal heightRange = max-min;
float height=1080;
float width = 1920;
@ -127,15 +145,19 @@ public class Picasso{
#region LINES
CoinWatch longestWatch = Brian.GetCoinWatchLongest(filename);
List<decimal> allResPoints = Brian.GetCombinedResPoints(filename);
List<decimal> allSupPoints = Brian.GetCombinedSupPoints(filename);
foreach(decimal point in longestWatch.resistancePoints){
List<decimal> processedResPoints = Utils.GetAveragePoints(allResPoints, min,max);
List<decimal> processedSupPoints = Utils.GetAveragePoints(allSupPoints,min,max);
foreach(decimal point in processedResPoints){
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)]));
}
foreach(decimal point in longestWatch.supportPoints){
foreach(decimal point in processedSupPoints){
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)]));
@ -182,10 +204,10 @@ public class Picasso{
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");
float newVal = float.Parse(Utils.GetPropByName(reports[i],propName).ToString() ?? "0");
// Console.WriteLine( newVal);
float prevVal = i > 0 ? float.Parse(GetPropByName(reports[i-1],propName).ToString() ?? "0") : 0;
float prevVal = i > 0 ? float.Parse(Utils.GetPropByName(reports[i-1],propName).ToString() ?? "0") : 0;
// Console.WriteLine( prevVal);
float preValY =( prevVal - min) * heightMultiplier ;
float newValY=(newVal - min) * heightMultiplier;
@ -198,12 +220,7 @@ public class Picasso{
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];
}
#endregion
}

View File

@ -1,6 +1,7 @@
using BinanceExchange.API.Enums;
using BinanceExchange.API.Models.Response;
using BinanceExchange.API.Models.WebSocket;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
@ -62,6 +63,34 @@ namespace SignalsTest
return response;
}
public 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];
}
public static List<decimal> GetAveragePoints(List<decimal> input, decimal min, decimal max){
List<decimal> processed = new List<decimal>();
List<decimal> ignoredPoints = new List<decimal>();
for(int x=0; x < input.Count; x++){
if(ignoredPoints.Contains(input[x])){continue;}//Already processed
decimal avg = input[x];
for(int y=0; y < input.Count; y++){
decimal point = input[y];
decimal diff = Math.Abs(avg-point) / (max-min);
if(diff < (decimal)0.05){
avg+=point;
avg/=2;
ignoredPoints.Add(input[y]);
}
}
processed.Add(avg);
}
return processed;
}
}
}