bb_trader2/bb_trader2.mq5

679 lines
45 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 14:43:19 +02:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| bb_trader.mq5 |
//| Copyright 2024, Competent Dilettante |
//| https://www.youtube.com/@competentdilettante |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Competent Dilettante"
#property link "https://www.youtube.com/@competentdilettante"
#property version "1.00"
#define STRUCT_FILENAME "bb_files/struct.dat"
#define SET_FILENAME "bb_files/set_struct.dat"
enum ENUM_LIFETIME_SIGNAL{
LIFETIME_SIGNAL_DAY = 0, //@5<O 687=8 A83=0;0 - 45=L
LIFETIME_SIGNAL_WEEK, //@5<O 687=8 A83=0;0 - =545;O
LIFETIME_SIGNAL_TWO_WEEKS, //@5<O 687=8 A83=0;0 - 2 =545;8
LIFETIME_SIGNAL_MONTH //@5<O 687=8 A830;0 - <5AOF
};
struct SSettings{
bool allowTrade;
double outComm;
int slipage;
ENUM_TIMEFRAMES slowTf;
int slowBBPeriod;
int slowBBShift;
int slowMAPeriod;
ENUM_TIMEFRAMES fastTf;
int fastBBPeriod;
int fastBBShift;
int fastAO_MA;
int slowAO_MA;
int fastMACD;
int slowMACD;
int smaMACD;
ENUM_LIFETIME_SIGNAL inLifeTime;
int inFiltr;
double inStopLevel;
double close50Percent;
double close30Percent;
double close20Percent;
double inRisk;
bool operator !=(const SSettings &other){
if(allowTrade == other.allowTrade
&& (int)(outComm * 100) == (int)(other.outComm * 100)
&& slipage == other.slipage
&& slowTf == other.slowTf
&& slowBBPeriod == other.slowBBPeriod
&& slowBBShift == other.slowBBShift
&& slowMAPeriod == other.slowMAPeriod
&& fastTf == other.fastTf
&& fastBBPeriod == other.fastBBPeriod
&& fastBBShift == other.fastBBShift
&& fastAO_MA == other.fastAO_MA
&& slowAO_MA == other.slowAO_MA
&& fastMACD == other.fastMACD
&& slowMACD == other.slowMACD
&& smaMACD == other.smaMACD
&& inLifeTime == other.inLifeTime
&& inFiltr == other.inFiltr
&& (int)(inStopLevel * 1000) == (int)(other.inStopLevel * 1000)
&& (int)(close50Percent * 1000) == (int)(other.close50Percent * 1000)
&& (int)(close30Percent * 1000) == (int)(other.close30Percent * 1000)
&& (int)(close20Percent * 1000) == (int)(other.close20Percent * 1000)
&& (int)(inRisk * 100) == (int)(other.close20Percent * 100)
) return false;
return true;
}
};
struct SFibo {
double zeroPoint;
double onePoint;
SFibo()
{
zeroPoint = 0;
onePoint = 0;
}
};
#include "MyExpertFunctions.mqh"
#include "AbsorbingFinder.mqh"
input group "1I85 =0AB@>9:8"
input uint inMagic = 556; //45=B8D8:0B>@ ?>78F89 M:A?5@B0
input bool inAllowTrade = true; // 07@5H5=85 =0 >B:@KB85 =>2KE ?>78F89
input double inOutComm = 0.0;
input int inSlipage = 10;
input group "0AB@>9:8 AB0@H53> "$"
input ENUM_TIMEFRAMES slowTf = PERIOD_H4;
input int slowBBPeriod = 21;
input int slowBBShift = 1;
input int slowMAPeriod = 120;
input group "0AB@>9:8 <;04H53> "$"
input ENUM_TIMEFRAMES fastTf = PERIOD_M15;
input int fastBBPeriod = 21;
input int fastBBShift = 1;
input int fastAO_MA = 1;
input int slowAO_MA = 7;
input int fastMACD = 12;
input int slowMACD = 30;
input int smaMACD = 9;
input ENUM_LIFETIME_SIGNAL inLifeTime = LIFETIME_SIGNAL_WEEK; //@5<O 687=8 A83=0;0
input group "0AB@>9:8 B>@3>2;8"
input int inFiltr = 50;
input double inStopLevel = 0.306;
input double close50Percent = 1.618;
input double close30Percent = 2.618;
input double close20Percent = 4.236;
input double inRisk = 1.5; //Risk on one deal (%)
//Handle values
int slowMAHandle, slowBBHandle;
int fastBBHandle, AOHandle, fastAO_MAHandle,slowAO_MAHandle,MACDHandle;
//Position struct
MqlParam posParams[2];
//Signal flags
bool isSignal = false;
int signalFlag{0};
int globalFlag{0};
//Time flags
static datetime t{0},slowT{0},fastT{0};
//Fibo struct
SFibo points;
//Levels array
const double levelaArray[] {1.768,1.88,2.786,2.88,4.786,4.88};
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//---
MqlParam params[];
::ArrayResize(params,4);
params[0].type = TYPE_INT;
params[0].integer_value = slowMAPeriod;
params[1].type = TYPE_INT;
params[1].integer_value = 0;
params[2].type = TYPE_INT;
params[2].integer_value = MODE_SMA;
params[3].type = TYPE_INT;
params[3].integer_value = PRICE_CLOSE;
::ResetLastError();
slowAO_MAHandle = std::GetHandle(_Symbol,slowTf,IND_MA,params);
if(slowAO_MAHandle == INVALID_HANDLE) {
::Print("MA on slow TF handlig error ",::GetLastError());
return INIT_FAILED;
}
params[0].integer_value = slowBBPeriod;
params[1].integer_value = slowBBShift;
params[2].type = TYPE_DOUBLE;
params[2].double_value = 2.0;
slowBBHandle = std::GetHandle(_Symbol,slowTf,IND_BANDS,params);
if(slowBBHandle == INVALID_HANDLE) {
::Print("BB on slow TF handlig error ",::GetLastError());
return INIT_FAILED;
}
params[0].integer_value = fastBBPeriod;
params[1].integer_value = fastBBShift;
fastBBHandle = std::GetHandle(_Symbol,fastTf,IND_BANDS,params);
if(fastBBHandle == INVALID_HANDLE) {
::Print("BB on fast TF handlig error ",::GetLastError());
return INIT_FAILED;
}
MqlParam array[];
AOHandle = std::GetHandle(_Symbol,fastTf,IND_AO,array);
if(AOHandle == INVALID_HANDLE) {
::Print("AO on fast TF handlig error ",::GetLastError());
return INIT_FAILED;
}
params[0].integer_value = fastAO_MA;
params[1].integer_value = 0;
params[2].type = TYPE_INT;
params[2].integer_value = MODE_SMA;
params[3].integer_value = AOHandle;
fastAO_MAHandle = std::GetHandle(_Symbol,fastTf,IND_MA,params);
if(fastAO_MAHandle == INVALID_HANDLE) {
::Print("Fast MA from AO on fast TF handlig error ",::GetLastError());
return INIT_FAILED;
}
params[0].integer_value = slowAO_MA;
slowAO_MAHandle = std::GetHandle(_Symbol,fastTf,IND_MA,params);
if(slowAO_MAHandle == INVALID_HANDLE) {
::Print("Slow MA from AO on fast TF handlig error ",::GetLastError());
return INIT_FAILED;
}
params[0].integer_value = fastMACD;
params[1].integer_value = slowMACD;
params[2].type = TYPE_INT;
params[2].integer_value = smaMACD;
params[3].integer_value = PRICE_CLOSE;
MACDHandle = std::GetHandle(_Symbol,fastTf,IND_MACD,params);
if(MACDHandle == INVALID_HANDLE) {
::Print("MACD on fast TF handlig error ",::GetLastError());
return INIT_FAILED;
}
if(!::FileIsExist(STRUCT_FILENAME)) {
::Print("File ",STRUCT_FILENAME," not found! Creating new file");
if(!std::fileWriteBinStuct(STRUCT_FILENAME,points)) {
::Print("Creating file ",STRUCT_FILENAME," error #",::GetLastError());
return INIT_FAILED;
}
}
::Print("File ",STRUCT_FILENAME," is in system");
if(!::FileIsExist(SET_FILENAME)){
SSettings sets;
setupSetStruct(sets);
if(!std::fileWriteBinStuct(SET_FILENAME,points)) {
::Print("Creating file ",SET_FILENAME," error #",::GetLastError());
return INIT_FAILED;
}
}
else{
::Print("File ",SET_FILENAME," is in system");
SSettings curSets,saveSets;
setupSetStruct(curSets);
std::fileReadBinStuct(SET_FILENAME,saveSets);
if(curSets != saveSets){
resetStructure();
std::fileWriteBinStuct(SET_FILENAME,curSets);
}
}
posParams[0].type = TYPE_STRING;
posParams[0].string_value = _Symbol;
posParams[1].type = TYPE_UINT;
posParams[1].integer_value = inMagic;
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
std::fileWriteBinStuct(STRUCT_FILENAME,points);
SSettings sets;
setupSetStruct(sets);
std::fileWriteBinStuct(SET_FILENAME,sets);
//---
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
//Block open position tracking
if(t != ::iTime(_Symbol,PERIOD_M1,0)) {
t = ::iTime(_Symbol,PERIOD_M1,0);
if(std::CountPositions(posParams) <= 0) {
points.onePoint = 0;
points.zeroPoint = 0;
} else {
if(!::PositionSelect(_Symbol)) {
t = 0;
}
double pos_sl = ::PositionGetDouble(POSITION_SL);
ulong pos_id = ::PositionGetInteger(POSITION_IDENTIFIER);
ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)::PositionGetInteger(POSITION_TYPE);
double pos_volume = ::PositionGetDouble(POSITION_VOLUME);
if(pos_sl <= 0)
SetStop(::PositionGetInteger(POSITION_IDENTIFIER));
double l1{0.0},l2{0.0},l3{0.0};
int fibo = std::DistPrices(points.zeroPoint,points.onePoint,_Symbol);
if(pos_type == POSITION_TYPE_SELL) {
l1 = points.zeroPoint - LevelDist(fibo,close50Percent) * _Point;
l2 = points.zeroPoint - LevelDist(fibo,close30Percent) * _Point;
l3 = points.zeroPoint - LevelDist(fibo,close20Percent) * _Point;
} else {
l1 = points.zeroPoint + LevelDist(fibo,close50Percent) * _Point;
l2 = points.zeroPoint + LevelDist(fibo,close30Percent) * _Point;
l3 = points.zeroPoint + LevelDist(fibo,close20Percent) * _Point;
}
double close_level{0.0};
int count = GetClosedParts(pos_id,pos_volume);
if(count == 0) {
::Print("Error history access. Error ",::GetLastError());
t = 0;
return;
} else if(count == 1)
close_level = l1;
else if(close_level == 2)
close_level = l2;
else
close_level = l3;
if(pos_type == POSITION_TYPE_BUY) {
if(::iClose(_Symbol,PERIOD_M1,1) >= close_level) {
double lot{0.0};
if(close_level == l1)
lot = std::LotNormalize(pos_volume * 0.5,_Symbol);
else if(close_level == l2)
lot = std::LotNormalize(pos_volume * 0.3,_Symbol);
else
lot = pos_volume;
if(!std::Close_PartPosition(pos_id,lot,inSlipage)) {
t = 0;
return;
}
}
} else {
if(::iClose(_Symbol,PERIOD_M1,1) <= close_level) {
double lot{0.0};
if(close_level == l1)
lot = std::LotNormalize(pos_volume * 0.5,_Symbol);
else if(close_level == l2)
lot = std::LotNormalize(pos_volume * 0.3,_Symbol);
else
lot = pos_volume;
if(!std::Close_PartPosition(pos_id,lot,inSlipage)) {
t = 0;
return;
}
}
}
}
}
if(slowT != ::iTime(_Symbol,slowTf,0)) {
slowT = ::iTime(_Symbol,slowTf,0);
double BBarrayUp[],BBarrayDn[];
if(!::CopyBuffer(slowBBHandle,1,0,2,BBarrayUp) || !::CopyBuffer(slowBBHandle,2,0,2,BBarrayDn)) {
slowT = 0;
return;
}
::ArraySetAsSeries(BBarrayUp,true);
::ArraySetAsSeries(BBarrayDn,true);
double MAarray[];
if(!::CopyBuffer(slowMAHandle,0,0,2,MAarray)) {
slowT = 0;
return;
}
::ArraySetAsSeries(MAarray,true);
if(::iClose(_Symbol,slowTf,1) > MAarray[1])
signalFlag = 1;
else if(::iClose(_Symbol,slowTf,1) < MAarray[1])
signalFlag = 2;
else
signalFlag = 0;
if(signalFlag == 1) {
if(::iClose(_Symbol,slowTf,1) < BBarrayDn[1])
isSignal = true;
} else if(signalFlag == 2) {
if(::iClose(_Symbol,slowTf,1) > BBarrayUp[1])
isSignal = true;
}
if(signalFlag > 0 && isSignal && globalFlag <= 0){
if(IsAbsorbingSignal(_Symbol,1)){
if(signalFlag == 1)
globalFlag = 1;
if(signalFlag == 2)
globalFlag = 2;
}
}
}
if(fastT != ::iTime(_Symbol,fastTf,0)) {
fastT = ::iTime(_Symbol,fastTf,0);
if(!inAllowTrade)
return;
if(std::CountPositions(posParams) > 0) {
ResetSignal();
return;
}
if(signalFlag <= 0 || !isSignal || globalFlag <= 0)
return;
double AOarray[],MACDarray[];
if(!::CopyBuffer(AOHandle,0,0,2,AOarray)) {
fastT = 0;
return;
}
::ArraySetAsSeries(AOarray,true);
if(!::CopyBuffer(MACDHandle,1,0,2,MACDarray)) {
fastT = 0;
return;
}
::ArraySetAsSeries(MACDarray,true);
if(isSignal && signalFlag == 1) {
if(AOarray[1] > 0 && MACDarray[1] > 50) {
ResetSignal();
return;
}
double s{0},z{0};
if(IsAbsorbingUp(_Symbol,fastTf,1,s,z)
|| IsDoubleAbsorbingUp(_Symbol,fastTf,1,s,z)
|| IsTripleAbsorbingUp(_Symbol,fastTf,1,s,z)) {
::Print("=== Absorbing up find ===");
::Print("Zero point = ",::DoubleToString(z,_Digits),"\tEnd point = ",::DoubleToString(s,_Digits));
if(AOarray[1] < 0 && MACDarray[1] < 50) {
points.zeroPoint = z;
points.onePoint = s;
int fibo = std::DistPrices(z,s,_Symbol);
int sl_dist = LevelDist(fibo,inStopLevel);
double sl = points.onePoint - sl_dist * _Point - inFiltr * _Point;
double cur = ::SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double acc = ::AccountInfoDouble(ACCOUNT_BALANCE);
double lot = std::LotSize(_Symbol,ORDER_TYPE_BUY,cur,sl,acc,inRisk);
if(lot <= 0)
lot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
ulong pos = std::Open_Buy_Pos(_Symbol,lot,inSlipage,inMagic);
if(pos <= 0) {
fastT = 0;
return;
}
if(!std::Position_SLTP(pos,sl,0.0)) {
fastT = 0;
return;
}
ResetSignal();
}
}
} else if(isSignal && signalFlag == 2) {
if(AOarray[1] < 0 && MACDarray[1] < 50) {
ResetSignal();
return;
}
double s{0},z{0};
if(IsAbsorbingDn(_Symbol,fastTf,1,s,z)
|| IsDoubleAbsorbingDn(_Symbol,fastTf,1,s,z)
|| IsTripleAbsorbingDn(_Symbol,fastTf,1,s,z)) {
::Print("=== Absorbing down find ===");
::Print("Zero point = ",::DoubleToString(z,_Digits),"\tEnd point = ",::DoubleToString(s,_Digits));
if(AOarray[1] > 0 && MACDarray[1] > 50) {
points.zeroPoint = z;
points.onePoint = s;
int fibo = std::DistPrices(z,s,_Symbol);
int sl_dist = LevelDist(fibo,inStopLevel);
double sl = points.onePoint + sl_dist * _Point + inFiltr * _Point;
double cur = ::SymbolInfoDouble(_Symbol,SYMBOL_BID);
double acc = ::AccountInfoDouble(ACCOUNT_BALANCE);
double lot = std::LotSize(_Symbol,ORDER_TYPE_SELL,cur,sl,acc,inRisk);
if(lot <= 0)
lot = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
ulong pos = std::Open_Sell_Pos(_Symbol,lot,inSlipage,inMagic);
if(pos <= 0) {
fastT = 0;
return;
}
if(!std::Position_SLTP(pos,sl,0.0)) {
fastT = 0;
return;
}
ResetSignal();
}
}
}
}
//---
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void SetStop(const ulong pos)
{
::ResetLastError();
if(!::PositionSelectByTicket(pos))
return;
int fibo_dist = std::DistPrices(points.onePoint,points.zeroPoint,_Symbol);
int sl_points = (int)::ceil(fibo_dist * inStopLevel);
if(!std::Position_SLTP(pos,sl_points,0)) {
::Print("Stop-loss set up failed. Error ",::GetLastError());
return;
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ResetSignal()
{
//points.onePoint = 0;
//points.zeroPoint = 0;
signalFlag = 0;
isSignal = false;
globalFlag = 0;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int LevelDist(const int size,const double dec)
{
return ((int)(::ceil(size * dec)));
}
//+------------------------------------------------------------------+
void resetStructure(){
points.onePoint = 0;
points.zeroPoint = 0;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
int GetClosedParts(const ulong position,double &volume)
{
if(!::HistorySelectByPosition(position))
return 0;
int count = ::HistoryDealsTotal();
if(count == 2) {
for(int i = 0;i < count;i++) {
ulong deal = ::HistoryDealGetTicket(i);
if(::HistoryDealGetInteger(deal,DEAL_ENTRY) == DEAL_ENTRY_IN) {
volume = ::HistoryDealGetDouble(deal,DEAL_VOLUME);
break;
}
}
}
return count;
}
//+------------------------------------------------------------------+
bool IsAbsorbingSignal(const string symbol,const int index){
if(signalFlag <= 0)
return false;
double open = ::iOpen(symbol,slowTf,1);
double close = ::iClose(symbol,slowTf,1);
if(signalFlag == 1 && open > close)
return false;
if(signalFlag == 2 && open < close)
return false;
if(signalFlag > 0){
int history = 0;
switch(inLifeTime){
case LIFETIME_SIGNAL_DAY:
history = 6;
break;
case LIFETIME_SIGNAL_WEEK:
history = 30;
break;
case LIFETIME_SIGNAL_TWO_WEEKS:
history = 60;
break;
case LIFETIME_SIGNAL_MONTH:
history = 120;
break;
}
double buffer[];
int ctrl_candle = 0;
if(signalFlag == 1){
if(!::CopyBuffer(slowBBHandle,2,0,history,buffer))
return false;
for(int i = 1;i < history;i++){
if(::iClose(symbol,slowTf,i) > buffer[i])
continue;
if(iOpen(symbol,slowTf,i) > ::iClose(symbol,slowTf,i)){
ctrl_candle = i;
break;
}
}
if(close > ::iOpen(symbol,slowTf,ctrl_candle))
return true;
}
else{
if(!::CopyBuffer(slowBBHandle,1,0,history,buffer))
return false;
for(int i = 1;i < history;i++){
if(::iClose(symbol,slowTf,i) < buffer[i])
continue;
if(iOpen(symbol,slowTf,i) < ::iClose(symbol,slowTf,i)){
ctrl_candle = i;
break;
}
}
if(close < ::iOpen(symbol,slowTf,ctrl_candle))
return true;
}
}
return false;
}
//+------------------------------------------------------------------+
void setupSetStruct(SSettings &sets){
sets.allowTrade = inAllowTrade;
sets.fastAO_MA = fastAO_MA;
sets.fastBBPeriod = fastBBPeriod;
sets.fastBBShift = fastBBShift;
sets.fastMACD = fastMACD;
sets.fastTf = fastTf;
sets.slowBBPeriod = slowBBPeriod;
sets.slowBBShift = slowBBShift;
sets.slowMACD = slowMACD;
sets.slowMAPeriod = slowMAPeriod;
sets.slowAO_MA = slowAO_MA;
sets.slowTf = slowTf;
sets.slipage = inSlipage;
sets.inLifeTime = inLifeTime;
sets.inFiltr = inFiltr;
sets.inStopLevel = inStopLevel;
sets.smaMACD = smaMACD;
sets.close20Percent = close20Percent;
sets.close30Percent = close30Percent;
sets.close50Percent = close50Percent;
sets.outComm = inOutComm;
sets.inRisk = inRisk;
}
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,const MqlTradeRequest &request,const MqlTradeResult &result){
if(trans.type == TRADE_TRANSACTION_DEAL_ADD){
ulong deal = trans.deal;
::HistoryDealSelect(deal);
string symbol = ::HistoryDealGetString(deal,DEAL_SYMBOL);
uint magic = (uint)::HistoryDealGetInteger(deal,DEAL_MAGIC);
if(symbol == _Symbol && magic == inMagic){
if(::HistoryDealGetInteger(deal,DEAL_REASON) == DEAL_REASON_SL){
ResetSignal();
resetStructure();
}
}
}
}