picasso improved

This commit is contained in:
Sewmina 2025-01-19 11:45:51 +05:30
parent dbcaf91521
commit 22e80da4a5
4 changed files with 131 additions and 63 deletions

View File

@ -2,9 +2,9 @@ using SignalsTest;
public static class Confirmations
{
public static float GetTWSConfirmation(List<TAReport> reports, int i)
public static int GetTWSConfirmation(List<TAReport> reports, int i)
{
float TWSHeightIncrement = 0;
int TWSHeightIncrement = 0;
if (i > 22)
{
@ -23,8 +23,8 @@ public static class Confirmations
}
}
if (hasCrossedMA) TWSHeightIncrement += 15;
if (hasCrossedST) TWSHeightIncrement += 15;
if (hasCrossedMA) TWSHeightIncrement += 1;
if (hasCrossedST) TWSHeightIncrement += 1;
}
return TWSHeightIncrement;

View File

@ -15,4 +15,8 @@ public static class Extensions{
public static float getCandleLength(this KlineCandleStickResponse candle){
return (float)Math.Abs(candle.Open-candle.Close);
}
public static float getTotalLength(this KlineCandleStickResponse candle){
return (float)Math.Abs(candle.High - candle.Low);
}
}

View File

@ -1,4 +1,3 @@
using BinanceExchange.API.Models.Response;
public static class Patterns{
@ -149,7 +148,53 @@ public static class Patterns{
return true;
}
// public static bool isHammer(KlineCandleStickResponse response, float ratioThreshold = 0.3f){
public static bool isHammer(KlineCandleStickResponse response, float bodyToShadowRatio = 0.3f){
// Check if the candle has a small body compared to total length
float bodyLength = response.getCandleLength();
float totalLength = response.getTotalLength();
// }
// Body should be small compared to total length
if (bodyLength / totalLength > bodyToShadowRatio) {
return false;
}
// Lower shadow should be at least twice the body length
float lowerShadow = (float)Math.Min(response.Open, response.Close) - (float)response.Low;
if (lowerShadow < bodyLength * 2) {
return false;
}
// Upper shadow should be minimal (less than 10% of total length)
float upperShadow = (float)(response.High - Math.Max(response.Open, response.Close));
if (upperShadow > totalLength * 0.1) {
return false;
}
return true;
}
public static bool isInverseHammer(KlineCandleStickResponse response, float bodyToShadowRatio = 0.3f){
// Check if the candle has a small body compared to total length
float bodyLength = response.getCandleLength();
float totalLength = response.getTotalLength();
// Body should be small compared to total length
if (bodyLength / totalLength > bodyToShadowRatio) {
return false;
}
// Upper shadow should be at least twice the body length
float upperShadow = (float)response.High - (float)Math.Max(response.Open, response.Close);
if (upperShadow < bodyLength * 2) {
return false;
}
// Lower shadow should be minimal (less than 10% of total length)
float lowerShadow = (float)Math.Min(response.Open, response.Close) - (float)response.Low;
if (lowerShadow > totalLength * 0.1) {
return false;
}
return true;
}
}

View File

@ -43,6 +43,15 @@ public class Picasso{
float candlesOffset = 0.3f * height;
float volumeHeightMultiplier = 0.2f * height;
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 titleFont = fontFamily.CreateFont(TextFontSize, FontStyle.Regular);
var indicatorFont = fontFamily.CreateFont(18, FontStyle.Regular);
using (Image img = new Image<Rgba32>((int)width + 100, (int)height, Color.FromRgb(13,18,25))){
#region CANDLES
@ -50,22 +59,22 @@ public class Picasso{
// 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);
float openVal1 = height - ((float)(reports[i].candle.Open - min) * heightMultiplier + candlesOffset);
float closeVal1 = height - ((float)(reports[i].candle.Close - min) * heightMultiplier + candlesOffset);
points[0] = new PointF(i * widthMultiplier, openVal1);
points[1] = new PointF(i * widthMultiplier, closeVal1);
PointF[] rangePoints = new PointF[2];
float highVal = (float)(reports[i].candle.High-min)* heightMultiplier;
float lowVal = (float)(reports[i].candle.Low-min)* heightMultiplier;
float highVal = height - ((float)(reports[i].candle.High-min) * heightMultiplier + candlesOffset);
float lowVal = height - ((float)(reports[i].candle.Low-min) * heightMultiplier + candlesOffset);
rangePoints[0] = new PointF(i * widthMultiplier, highVal+ candlesOffset);
rangePoints[1] = new PointF(i * widthMultiplier, lowVal+ candlesOffset);
rangePoints[0] = new PointF(i * widthMultiplier, highVal);
rangePoints[1] = new PointF(i * widthMultiplier, lowVal);
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) * volumeHeightMultiplier);
volumePoints[0] = new PointF(i * widthMultiplier, height);
volumePoints[1] = new PointF(i * widthMultiplier, height - ((float)reports[i].candle.Volume / (float)volMax) * volumeHeightMultiplier);
Color candleColor = reports[i].candle.Close > reports[i].candle.Open ? Color.Green : Color.Red;
@ -81,12 +90,12 @@ public class Picasso{
float squareOffset = 2;
PointF[] bottomPoints = new PointF[2];
bottomPoints[0] = new PointF(i * widthMultiplier, lowVal-squareOffset+ candlesOffset);
bottomPoints[1] = new PointF(i * widthMultiplier, lowVal- 15+ candlesOffset);
bottomPoints[0] = new PointF(i * widthMultiplier, lowVal-squareOffset);
bottomPoints[1] = new PointF(i * widthMultiplier, lowVal- 15);
PointF[] topPoints = new PointF[2];
topPoints[0] = new PointF(i * widthMultiplier, highVal+squareOffset+ candlesOffset);
topPoints[1] = new PointF(i * widthMultiplier, highVal+15+ candlesOffset);
topPoints[0] = new PointF(i * widthMultiplier, highVal+squareOffset);
topPoints[1] = new PointF(i * widthMultiplier, highVal+15);
#region TWS
if(reports[i].TWS > 1){
@ -98,10 +107,13 @@ public class Picasso{
}
float TWSHeightIncrement = Confirmations.GetTWSConfirmation(reports,i);
PointF[] TWSPoints = bottomPoints;
TWSPoints[1]-= new PointF(0,TWSHeightIncrement);
img.Mutate(ctx=>ctx.DrawLine(TWSColor, 10, TWSPoints));
string strengthText = reports[i].TWS == 3 ? "+2" : "+1";
string text = $"TWS{strengthText}";
// Position text below the candle
PointF textPosition = new PointF(i * widthMultiplier - 15, lowVal + 20);
img.Mutate(ctx => ctx.DrawText(text, indicatorFont, TWSColor, textPosition));
img.Mutate(ctx=>ctx.DrawLine(Color.DarkBlue, 0.5f, [new PointF(i * widthMultiplier, 0), new PointF(i * widthMultiplier, height)])); //Draw Vertical guide
}
#endregion
@ -114,15 +126,34 @@ public class Picasso{
}else if(reports[i].TBC== 3){
TBCColor = Color.White;
}
float TBCHeightIncremenet = Confirmations.GetTBCConfirmation(reports,i);
float TWSHeightIncrement = Confirmations.GetTBCConfirmation(reports,i);
PointF[] TBCPoints = topPoints;
TBCPoints[1]+= new PointF(0,TBCHeightIncremenet);
img.Mutate(ctx=>ctx.DrawLine(TBCColor, 10, TBCPoints));
string strengthText = reports[i].TBC == 3 ? "+2" : "+1";
string text = $"TBC{strengthText}";
// Position text above the candle
PointF textPosition = new PointF(i * widthMultiplier - 15, highVal - 40);
img.Mutate(ctx => ctx.DrawText(text, indicatorFont, TBCColor, textPosition));
img.Mutate(ctx=>ctx.DrawLine(Color.DarkBlue, 0.5f, [new PointF(i * widthMultiplier, 0), new PointF(i * widthMultiplier, height)])); //Draw Vertical guide
}
#endregion
// Update hammer indicator position
if (Patterns.isHammer(reports[i].candle))
{
float squareSize = 10f;
PointF squarePosition = new PointF(i * widthMultiplier - squareSize/2, lowVal - 40);
img.Mutate(ctx => ctx.Fill(Color.White, new RectangleF(squarePosition, new SizeF(squareSize, squareSize))));
}
// Update inverse hammer indicator position
if (Patterns.isInverseHammer(reports[i].candle))
{
float squareSize = 10f;
PointF squarePosition = new PointF(i * widthMultiplier - squareSize/2, highVal + 50);
img.Mutate(ctx => ctx.Fill(Color.White, new RectangleF(squarePosition, new SizeF(squareSize, squareSize))));
}
}
// Console.WriteLine("Getting paths");
// object t = GetPropByName(reports[reports.Count- 1], "SMA7");
@ -132,12 +163,12 @@ public class Picasso{
#region GUIDES
PointF[] Vol20 = new PointF[2];
Vol20[0] = new PointF(0,volumeHeightMultiplier*0.2f);
Vol20[1] = new PointF(width,volumeHeightMultiplier*0.2f);
Vol20[0] = new PointF(0, height - volumeHeightMultiplier*0.2f);
Vol20[1] = new PointF(width, height - volumeHeightMultiplier*0.2f);
PointF[] Vol80 = new PointF[2];
Vol80[0] = new PointF(0, volumeHeightMultiplier*0.8f);
Vol80[1] = new PointF(width, volumeHeightMultiplier * 0.8f);
Vol80[0] = new PointF(0, height - volumeHeightMultiplier*0.8f);
Vol80[1] = new PointF(width, height - volumeHeightMultiplier*0.8f);
img.Mutate(ctx=> ctx.DrawLine(Color.Gray, 1f, Vol20).DrawLine(Color.Gray, 0.5f, Vol80));
@ -152,14 +183,12 @@ public class Picasso{
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}");
float yVal = height - ((float)(point-min) * heightMultiplier + candlesOffset);
img.Mutate(ctx=> ctx.DrawLine(Color.Red,1f, [new PointF(0, yVal), new PointF(width, yVal)]));
}
foreach(decimal point in processedSupPoints){
float yVal = ((float)(point-min) * heightMultiplier) + candlesOffset;
// Console.WriteLine($"{filename} sup at {yVal/height}");
float yVal = height - ((float)(point-min) * heightMultiplier + candlesOffset);
img.Mutate(ctx=> ctx.DrawLine(Color.Green,1f, [new PointF(0, yVal), new PointF(width, yVal)]));
}
#endregion
@ -171,19 +200,13 @@ public class Picasso{
IPath sma50Path = GetPath(reports, "SMA50", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset);
IPath sma200Path = GetPath(reports, "SMA200", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset);
IPath stochFast = GetPath(reports, "Stochastic", widthMultiplier, (float)volumeHeightMultiplier/100f, 0, 0);
IPath stochK3 = GetPath(reports, "StochasticK3", widthMultiplier, (float)volumeHeightMultiplier/100f, 0, 0);
IPath stochFast = GetPath(reports, "Stochastic", widthMultiplier, (float)volumeHeightMultiplier/100f, 0);
IPath stochK3 = GetPath(reports, "StochasticK3", widthMultiplier, (float)volumeHeightMultiplier/100f, 0);
IPath stPath = GetPath(reports, "ST", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset);
#endregion
//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);
int intervalInMins = Utils.GetMinutesForInterval(interval);
string intervalText = intervalInMins > (60 * 23) ? $"{intervalInMins/(60*24)}D" : $"{intervalInMins}m";
@ -193,8 +216,7 @@ public class Picasso{
Draw(Color.Green, 3, stPath).
Draw(Color.Cyan, 3,stochFast).
Draw(Color.Orange, 3, stochK3).
Flip(FlipMode.Vertical).
DrawText($"{filename}- {intervalText}", font,new Color(Rgba32.ParseHex("#FFFFFF")), new PointF(10,10)));
DrawText($"{filename}- {intervalText}", titleFont,new Color(Rgba32.ParseHex("#FFFFFF")), new PointF(10,10)));
img.Save($"Charts/{filename}{intervalInMins}.png");
}
@ -204,17 +226,14 @@ public class Picasso{
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));
float height=1080;
for(int i=0; i < reports.Count; i++){
float newVal = float.Parse(Utils.GetPropByName(reports[i],propName).ToString() ?? "0");
// Console.WriteLine( newVal);
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;
PointF prevPoint = new PointF((i-1)* widthMultiplier, (preValY) +offset);
PointF newPoint = new PointF(i * widthMultiplier, (newValY) + offset);
// Console.WriteLine(newValY);
float preValY = height - ((prevVal - min) * heightMultiplier + offset);
float newValY = height - ((newVal - min) * heightMultiplier + offset);
PointF prevPoint = new PointF((i-1)* widthMultiplier, preValY);
PointF newPoint = new PointF(i * widthMultiplier, newValY);
builder.AddLine(prevPoint, newPoint);
}