picasso improved
This commit is contained in:
parent
dbcaf91521
commit
22e80da4a5
|
|
@ -2,9 +2,9 @@ using SignalsTest;
|
||||||
|
|
||||||
public static class Confirmations
|
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)
|
if (i > 22)
|
||||||
{
|
{
|
||||||
|
|
@ -23,8 +23,8 @@ public static class Confirmations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCrossedMA) TWSHeightIncrement += 15;
|
if (hasCrossedMA) TWSHeightIncrement += 1;
|
||||||
if (hasCrossedST) TWSHeightIncrement += 15;
|
if (hasCrossedST) TWSHeightIncrement += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return TWSHeightIncrement;
|
return TWSHeightIncrement;
|
||||||
|
|
|
||||||
|
|
@ -15,4 +15,8 @@ public static class Extensions{
|
||||||
public static float getCandleLength(this KlineCandleStickResponse candle){
|
public static float getCandleLength(this KlineCandleStickResponse candle){
|
||||||
return (float)Math.Abs(candle.Open-candle.Close);
|
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;
|
using BinanceExchange.API.Models.Response;
|
||||||
|
|
||||||
public static class Patterns{
|
public static class Patterns{
|
||||||
|
|
@ -149,7 +148,53 @@ public static class Patterns{
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
131
Picasso.cs
131
Picasso.cs
|
|
@ -43,6 +43,15 @@ public class Picasso{
|
||||||
|
|
||||||
float candlesOffset = 0.3f * height;
|
float candlesOffset = 0.3f * height;
|
||||||
float volumeHeightMultiplier = 0.2f * 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))){
|
using (Image img = new Image<Rgba32>((int)width + 100, (int)height, Color.FromRgb(13,18,25))){
|
||||||
|
|
||||||
#region CANDLES
|
#region CANDLES
|
||||||
|
|
@ -50,22 +59,22 @@ public class Picasso{
|
||||||
// img.Mutate(ctx=> ctx.DrawLine()))
|
// img.Mutate(ctx=> ctx.DrawLine()))
|
||||||
PointF[] points = new PointF[2];
|
PointF[] points = new PointF[2];
|
||||||
|
|
||||||
float openVal1 = (float)(reports[i].candle.Open - min) * heightMultiplier;
|
float openVal1 = height - ((float)(reports[i].candle.Open - min) * heightMultiplier + candlesOffset);
|
||||||
float closeVal1 = (float)(reports[i].candle.Close - min)* heightMultiplier;
|
float closeVal1 = height - ((float)(reports[i].candle.Close - min) * heightMultiplier + candlesOffset);
|
||||||
points[0] =new PointF(i * widthMultiplier, openVal1 + candlesOffset);
|
points[0] = new PointF(i * widthMultiplier, openVal1);
|
||||||
points[1] =new PointF(i * widthMultiplier, closeVal1+ candlesOffset);
|
points[1] = new PointF(i * widthMultiplier, closeVal1);
|
||||||
|
|
||||||
PointF[] rangePoints = new PointF[2];
|
PointF[] rangePoints = new PointF[2];
|
||||||
|
|
||||||
float highVal = (float)(reports[i].candle.High-min)* heightMultiplier;
|
float highVal = height - ((float)(reports[i].candle.High-min) * heightMultiplier + candlesOffset);
|
||||||
float lowVal = (float)(reports[i].candle.Low-min)* heightMultiplier;
|
float lowVal = height - ((float)(reports[i].candle.Low-min) * heightMultiplier + candlesOffset);
|
||||||
|
|
||||||
rangePoints[0] = new PointF(i * widthMultiplier, highVal+ candlesOffset);
|
rangePoints[0] = new PointF(i * widthMultiplier, highVal);
|
||||||
rangePoints[1] = new PointF(i * widthMultiplier, lowVal+ candlesOffset);
|
rangePoints[1] = new PointF(i * widthMultiplier, lowVal);
|
||||||
|
|
||||||
PointF[] volumePoints = new PointF[2];
|
PointF[] volumePoints = new PointF[2];
|
||||||
volumePoints[0] = new PointF(i * widthMultiplier, 0);
|
volumePoints[0] = new PointF(i * widthMultiplier, height);
|
||||||
volumePoints[1] = new PointF(i * widthMultiplier, ((float)reports[i].candle.Volume / (float)volMax) * volumeHeightMultiplier);
|
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;
|
Color candleColor = reports[i].candle.Close > reports[i].candle.Open ? Color.Green : Color.Red;
|
||||||
|
|
||||||
|
|
@ -81,12 +90,12 @@ public class Picasso{
|
||||||
float squareOffset = 2;
|
float squareOffset = 2;
|
||||||
|
|
||||||
PointF[] bottomPoints = new PointF[2];
|
PointF[] bottomPoints = new PointF[2];
|
||||||
bottomPoints[0] = new PointF(i * widthMultiplier, lowVal-squareOffset+ candlesOffset);
|
bottomPoints[0] = new PointF(i * widthMultiplier, lowVal-squareOffset);
|
||||||
bottomPoints[1] = new PointF(i * widthMultiplier, lowVal- 15+ candlesOffset);
|
bottomPoints[1] = new PointF(i * widthMultiplier, lowVal- 15);
|
||||||
|
|
||||||
PointF[] topPoints = new PointF[2];
|
PointF[] topPoints = new PointF[2];
|
||||||
topPoints[0] = new PointF(i * widthMultiplier, highVal+squareOffset+ candlesOffset);
|
topPoints[0] = new PointF(i * widthMultiplier, highVal+squareOffset);
|
||||||
topPoints[1] = new PointF(i * widthMultiplier, highVal+15+ candlesOffset);
|
topPoints[1] = new PointF(i * widthMultiplier, highVal+15);
|
||||||
|
|
||||||
#region TWS
|
#region TWS
|
||||||
if(reports[i].TWS > 1){
|
if(reports[i].TWS > 1){
|
||||||
|
|
@ -98,11 +107,14 @@ public class Picasso{
|
||||||
}
|
}
|
||||||
float TWSHeightIncrement = Confirmations.GetTWSConfirmation(reports,i);
|
float TWSHeightIncrement = Confirmations.GetTWSConfirmation(reports,i);
|
||||||
|
|
||||||
PointF[] TWSPoints = bottomPoints;
|
string strengthText = reports[i].TWS == 3 ? "+2" : "+1";
|
||||||
TWSPoints[1]-= new PointF(0,TWSHeightIncrement);
|
string text = $"TWS{strengthText}";
|
||||||
img.Mutate(ctx=>ctx.DrawLine(TWSColor, 10, TWSPoints));
|
|
||||||
|
// Position text below the candle
|
||||||
img.Mutate(ctx=>ctx.DrawLine(Color.DarkBlue , 0.5f, [new PointF(i * widthMultiplier, 0),new PointF(i * widthMultiplier, height)])); //Draw Vertical guide
|
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
|
#endregion
|
||||||
|
|
||||||
|
|
@ -114,15 +126,34 @@ public class Picasso{
|
||||||
}else if(reports[i].TBC== 3){
|
}else if(reports[i].TBC== 3){
|
||||||
TBCColor = Color.White;
|
TBCColor = Color.White;
|
||||||
}
|
}
|
||||||
float TBCHeightIncremenet = Confirmations.GetTBCConfirmation(reports,i);
|
float TWSHeightIncrement = Confirmations.GetTBCConfirmation(reports,i);
|
||||||
|
|
||||||
PointF[] TBCPoints = topPoints;
|
string strengthText = reports[i].TBC == 3 ? "+2" : "+1";
|
||||||
TBCPoints[1]+= new PointF(0,TBCHeightIncremenet);
|
string text = $"TBC{strengthText}";
|
||||||
img.Mutate(ctx=>ctx.DrawLine(TBCColor, 10, TBCPoints));
|
|
||||||
|
// Position text above the candle
|
||||||
img.Mutate(ctx=>ctx.DrawLine(Color.DarkBlue , 0.5f, [new PointF(i * widthMultiplier, 0),new PointF(i * widthMultiplier, height)])); //Draw Vertical guide
|
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
|
#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");
|
// Console.WriteLine("Getting paths");
|
||||||
// object t = GetPropByName(reports[reports.Count- 1], "SMA7");
|
// object t = GetPropByName(reports[reports.Count- 1], "SMA7");
|
||||||
|
|
@ -132,12 +163,12 @@ public class Picasso{
|
||||||
#region GUIDES
|
#region GUIDES
|
||||||
|
|
||||||
PointF[] Vol20 = new PointF[2];
|
PointF[] Vol20 = new PointF[2];
|
||||||
Vol20[0] = new PointF(0,volumeHeightMultiplier*0.2f);
|
Vol20[0] = new PointF(0, height - volumeHeightMultiplier*0.2f);
|
||||||
Vol20[1] = new PointF(width,volumeHeightMultiplier*0.2f);
|
Vol20[1] = new PointF(width, height - volumeHeightMultiplier*0.2f);
|
||||||
|
|
||||||
PointF[] Vol80 = new PointF[2];
|
PointF[] Vol80 = new PointF[2];
|
||||||
Vol80[0] = new PointF(0, volumeHeightMultiplier*0.8f);
|
Vol80[0] = new PointF(0, height - volumeHeightMultiplier*0.8f);
|
||||||
Vol80[1] = new PointF(width, 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));
|
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);
|
List<decimal> processedSupPoints = Utils.GetAveragePoints(allSupPoints,min,max);
|
||||||
|
|
||||||
foreach(decimal point in processedResPoints){
|
foreach(decimal point in processedResPoints){
|
||||||
float yVal = ((float)(point-min) * heightMultiplier) + candlesOffset;
|
float yVal = height - ((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)]));
|
img.Mutate(ctx=> ctx.DrawLine(Color.Red,1f, [new PointF(0, yVal), new PointF(width, yVal)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach(decimal point in processedSupPoints){
|
foreach(decimal point in processedSupPoints){
|
||||||
float yVal = ((float)(point-min) * heightMultiplier) + candlesOffset;
|
float yVal = height - ((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)]));
|
img.Mutate(ctx=> ctx.DrawLine(Color.Green,1f, [new PointF(0, yVal), new PointF(width, yVal)]));
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
@ -167,23 +196,17 @@ public class Picasso{
|
||||||
|
|
||||||
#region OVERLAY LINES
|
#region OVERLAY LINES
|
||||||
//Overlay - Candles
|
//Overlay - Candles
|
||||||
IPath sma20Path = GetPath(reports, "SMA20", widthMultiplier,(float)heightMultiplier, (float)min, candlesOffset);
|
IPath sma20Path = GetPath(reports, "SMA20", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset);
|
||||||
IPath sma50Path = GetPath(reports, "SMA50", widthMultiplier,(float)heightMultiplier, (float)min, candlesOffset);
|
IPath sma50Path = GetPath(reports, "SMA50", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset);
|
||||||
IPath sma200Path = GetPath(reports, "SMA200", 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 stochFast = GetPath(reports, "Stochastic", widthMultiplier, (float)volumeHeightMultiplier/100f, 0);
|
||||||
IPath stochK3 = GetPath(reports, "StochasticK3", widthMultiplier, (float)volumeHeightMultiplier/100f, 0, 0);
|
IPath stochK3 = GetPath(reports, "StochasticK3", widthMultiplier, (float)volumeHeightMultiplier/100f, 0);
|
||||||
|
|
||||||
IPath stPath = GetPath(reports, "ST", widthMultiplier,(float)heightMultiplier, (float)min, candlesOffset);
|
IPath stPath = GetPath(reports, "ST", widthMultiplier, (float)heightMultiplier, (float)min, candlesOffset);
|
||||||
#endregion
|
#endregion
|
||||||
//NewChart
|
//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);
|
int intervalInMins = Utils.GetMinutesForInterval(interval);
|
||||||
string intervalText = intervalInMins > (60 * 23) ? $"{intervalInMins/(60*24)}D" : $"{intervalInMins}m";
|
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.Green, 3, stPath).
|
||||||
Draw(Color.Cyan, 3,stochFast).
|
Draw(Color.Cyan, 3,stochFast).
|
||||||
Draw(Color.Orange, 3, stochK3).
|
Draw(Color.Orange, 3, stochK3).
|
||||||
Flip(FlipMode.Vertical).
|
DrawText($"{filename}- {intervalText}", titleFont,new Color(Rgba32.ParseHex("#FFFFFF")), new PointF(10,10)));
|
||||||
DrawText($"{filename}- {intervalText}", font,new Color(Rgba32.ParseHex("#FFFFFF")), new PointF(10,10)));
|
|
||||||
|
|
||||||
img.Save($"Charts/{filename}{intervalInMins}.png");
|
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){
|
static IPath GetPath(List<TAReport> reports, string propName, float widthMultiplier,float heightMultiplier, float min, float offset =0){
|
||||||
PathBuilder builder = new PathBuilder();
|
PathBuilder builder = new PathBuilder();
|
||||||
builder.SetOrigin(new PointF(0,0));
|
builder.SetOrigin(new PointF(0,0));
|
||||||
|
float height=1080;
|
||||||
for(int i=0; i < reports.Count; i++){
|
for(int i=0; i < reports.Count; i++){
|
||||||
float newVal = float.Parse(Utils.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(Utils.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 = height - ((prevVal - min) * heightMultiplier + offset);
|
||||||
float preValY =( prevVal - min) * heightMultiplier ;
|
float newValY = height - ((newVal - min) * heightMultiplier + offset);
|
||||||
float newValY=(newVal - min) * heightMultiplier;
|
PointF prevPoint = new PointF((i-1)* widthMultiplier, preValY);
|
||||||
PointF prevPoint = new PointF((i-1)* widthMultiplier, (preValY) +offset);
|
PointF newPoint = new PointF(i * widthMultiplier, newValY);
|
||||||
PointF newPoint = new PointF(i * widthMultiplier, (newValY) + offset);
|
|
||||||
// Console.WriteLine(newValY);
|
|
||||||
builder.AddLine(prevPoint, newPoint);
|
builder.AddLine(prevPoint, newPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user