325 lines
11 KiB
C#
325 lines
11 KiB
C#
using BinanceExchange.API.Models.Response;
|
|
using SignalsTest;
|
|
|
|
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;
|
|
}
|
|
|
|
public static int GetThreeWhiteSoldiers(List<KlineCandleStickResponse> responses, int curIndex){
|
|
if(curIndex < 10){
|
|
return 0;
|
|
}
|
|
|
|
KlineCandleStickResponse candle1 = responses[curIndex];
|
|
KlineCandleStickResponse candle2 = responses[curIndex-1];
|
|
KlineCandleStickResponse candle3 = responses[curIndex-2];
|
|
|
|
bool isAllGreen = candle1.isGreen() && candle2.isGreen() && candle3.isGreen();
|
|
|
|
//LP:using average shadow is a bad idea, check each shadow
|
|
// float averageShadowRatio = (candle1.getShadowRatio() + candle2.getShadowRatio() + candle3.getShadowRatio())/3f;
|
|
|
|
|
|
bool areSoldiers = AreAllSolid([candle1,candle2, candle3],0.6f);
|
|
bool areSoldiersThin = AreAllSolid([candle1,candle2, candle3], 0.4f);
|
|
|
|
|
|
bool areSameSize = AreSameCandleSizes([candle1,candle2,candle3]);
|
|
|
|
if(isAllGreen && areSoldiers && areSameSize){
|
|
//Best
|
|
return 3;
|
|
}else if(isAllGreen&& areSoldiersThin && areSameSize){
|
|
//Not strong
|
|
return 2;
|
|
}else if(isAllGreen){
|
|
//Meh
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
public static int GetThreeBlackCrows(List<KlineCandleStickResponse> responses, int curIndex){
|
|
if(curIndex < 10){
|
|
return 0;
|
|
}
|
|
|
|
KlineCandleStickResponse candle1 = responses[curIndex];
|
|
KlineCandleStickResponse candle2 = responses[curIndex-1];
|
|
KlineCandleStickResponse candle3 = responses[curIndex-2];
|
|
|
|
bool isAllRed = !candle1.isGreen() && !candle2.isGreen() && !candle3.isGreen();
|
|
|
|
//LP:using average shadow is a bad idea, check each shadow
|
|
// float averageShadowRatio = (candle1.getShadowRatio() + candle2.getShadowRatio() + candle3.getShadowRatio())/3f;
|
|
|
|
|
|
bool areSoldiers = AreAllSolid([candle1,candle2, candle3],0.6f);
|
|
bool areSoldiersThin = AreAllSolid([candle1,candle2, candle3], 0.4f);
|
|
|
|
|
|
bool areSameSize = AreSameCandleSizes([candle1,candle2,candle3]);
|
|
|
|
if(isAllRed && areSoldiers && areSameSize){
|
|
//Best
|
|
return 3;
|
|
}else if(isAllRed&& areSoldiersThin && areSameSize){
|
|
//Not strong
|
|
return 2;
|
|
}else if(isAllRed){
|
|
//Meh
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
public static bool AreSameCandleSizes(List<KlineCandleStickResponse> responses, float tolerance= 0.5f){
|
|
float totalHeight = 0;
|
|
foreach(KlineCandleStickResponse response in responses){
|
|
totalHeight+= response.getCandleLength();
|
|
}
|
|
|
|
float avgHeight = totalHeight / (float)responses.Count;
|
|
|
|
foreach(KlineCandleStickResponse response in responses){
|
|
float diff = Math.Abs(avgHeight - response.getCandleLength());
|
|
|
|
if(diff > tolerance){
|
|
return false; //One failed to match size
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool AreAllSolid(List<KlineCandleStickResponse> responses, float tolerance= 0.75f){
|
|
foreach(KlineCandleStickResponse response in responses){
|
|
if(response.getShadowRatio() < tolerance){
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static bool IsSolid(KlineCandleStickResponse candle, float tolerance = 0.75f){
|
|
return candle.getShadowRatio() > tolerance;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
public static bool isVwapShort(List<TAReport> responses, int index, int length = 10){
|
|
if(index < length * 2){
|
|
return false;
|
|
}
|
|
|
|
float lowCount =0;
|
|
float highCount =0;
|
|
decimal hh= 0;
|
|
decimal ll = 10000000000;
|
|
|
|
float avgSize = 0;
|
|
int candlesSinceLastHigh = 0;
|
|
int greenCandlesAmount =0;
|
|
for(int i=index; i > index-length; i--){
|
|
candlesSinceLastHigh++;
|
|
|
|
if(responses[i].RSI50 > 50){
|
|
greenCandlesAmount++;
|
|
}
|
|
if(responses[i].candle.High > responses[i].VwapWeekly){
|
|
candlesSinceLastHigh=0;
|
|
highCount++;
|
|
|
|
if(hh < responses[i].High){
|
|
hh = responses[i].High;
|
|
}
|
|
}else if(responses[i].candle.Low < responses[i].VwapWeekly){
|
|
lowCount++;
|
|
|
|
if(ll > responses[i].Low){
|
|
ll = responses[i].Low;
|
|
}
|
|
|
|
}
|
|
|
|
avgSize += responses[i].candle.getCandleLength();
|
|
}
|
|
avgSize /= length;
|
|
|
|
TAReport curReport = responses[index];
|
|
bool isBelowVwap = curReport.Open < curReport.VwapWeekly || curReport.Close < curReport.VwapWeekly;
|
|
bool isSolid = IsSolid(curReport.candle) ;
|
|
bool isRed = curReport.RSI50 <= 50;
|
|
bool beenAcrossVwap = (highCount / lowCount) > 0;
|
|
bool mostlyGreen = greenCandlesAmount > length /3f;
|
|
|
|
//These did not matter
|
|
bool closeToVwap = curReport.Open < hh && curReport.Open > ll;
|
|
bool mostlyBelowVwap = (highCount / lowCount) < 0.3f;
|
|
bool crossedRecently = candlesSinceLastHigh < (length/3);
|
|
bool isLarge = curReport.candle.getCandleLength() > avgSize;
|
|
|
|
bool final = isRed && isSolid && isBelowVwap && beenAcrossVwap && mostlyGreen;
|
|
|
|
if(final){
|
|
Console.WriteLine($"Vwap(S) signal on {curReport.pair}({curReport.interval}m) : {highCount} / {lowCount} = {highCount/lowCount} , {candlesSinceLastHigh} last High");
|
|
}
|
|
return final;
|
|
}
|
|
|
|
public static bool isVwapLong (List<TAReport> responses, int index, int length = 10){
|
|
if(index < length * 2){
|
|
return false;
|
|
}
|
|
|
|
float lowCount =0;
|
|
float highCount =0;
|
|
decimal hh= 0;
|
|
decimal ll = 10000000000;
|
|
|
|
float avgSize = 0;
|
|
int candlesSinceLastHigh = 0;
|
|
int greenCandlesAmount =0;
|
|
for(int i=index; i > index-length; i--){
|
|
candlesSinceLastHigh++;
|
|
|
|
if(responses[i].RSI50 > 50){
|
|
greenCandlesAmount++;
|
|
}
|
|
if(responses[i].candle.High > responses[i].VwapWeekly){
|
|
candlesSinceLastHigh=0;
|
|
highCount++;
|
|
|
|
if(hh < responses[i].High){
|
|
hh = responses[i].High;
|
|
}
|
|
}else if(responses[i].candle.Low < responses[i].VwapWeekly){
|
|
lowCount++;
|
|
|
|
if(ll > responses[i].Low){
|
|
ll = responses[i].Low;
|
|
}
|
|
}
|
|
|
|
avgSize += responses[i].candle.getCandleLength();
|
|
}
|
|
avgSize /= length;
|
|
|
|
TAReport curReport = responses[index];
|
|
bool isAboveVwap = curReport.Open > curReport.VwapWeekly || curReport.Close > curReport.VwapWeekly;
|
|
bool isSolid = IsSolid(curReport.candle) ;
|
|
bool isGreen = curReport.RSI50 >= 50;
|
|
bool beenAcrossVwap = (highCount / lowCount) > 0;
|
|
bool mostylRed = greenCandlesAmount < length /3f;
|
|
|
|
//These did not matter
|
|
bool closeToVwap = curReport.Open < hh && curReport.Open > ll;
|
|
bool mostlyBelowVwap = (highCount / lowCount) < 0.3f;
|
|
bool crossedRecently = candlesSinceLastHigh < (length/3);
|
|
bool isLarge = curReport.candle.getCandleLength() > avgSize;
|
|
|
|
bool final = isGreen && isSolid && isAboveVwap && beenAcrossVwap && mostylRed;
|
|
|
|
if(final){
|
|
Console.WriteLine($"Vwap(L) signal on {curReport.pair}({curReport.interval}m) : {highCount} / {lowCount} = {highCount/lowCount} , {candlesSinceLastHigh} last High");
|
|
}
|
|
return final;
|
|
}
|
|
} |