picasso improved
This commit is contained in:
parent
dbcaf91521
commit
22e80da4a5
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
51
Patterns.cs
51
Patterns.cs
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
113
Picasso.cs
113
Picasso.cs
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user