mql5/Include/Mql5Book/ExpertAdvisor.mqh
younglubah-gmail 195db6ea13 new files added
2026-03-15 02:48:52 +02:00

321 lines
25 KiB
MQL5

//+------------------------------------------------------------------+
//| ConcreteWebEa.mq5 |
//| Concrete Goonies - https://concrete-goonies.web.app |
//+------------------------------------------------------------------+
#property copyright "Concrete Goonies © 2025"
#property link "https://concrete-goonies.web.app"
#property version "1.00"
#include <Mql5Book\TradeHedge.mqh>
#include <Mql5Book\Price.mqh>
#include <Mql5Book\MoneyManagement.mqh>
#include <Mql5Book\TrailingStops.mqh>
#include <Mql5Book\Timer.mqh>
#include <Mql5Book\Indicators.mqh>
#include <WallPaper.mqh>
#include <Arrays\ArrayObj.mqh>
// Callback for notifications
typedef void (*TradeNotificationCallback)(const string &message);
//+------------------------------------------------------------------+
//| EA Config Structure |
//+------------------------------------------------------------------+
struct EAConfig {
string name;
bool enabled;
int tradeDuration;
int tradeCount;
double lotSize;
int stopLoss;
int takeProfit;
ulong magicNumber;
bool allowBuy;
bool allowSell;
bool useMM;
double riskPercent;
bool hidden;
ENUM_TIMEFRAMES timeframe;
};
//+------------------------------------------------------------------+
//| Expert Advisor Class |
//+------------------------------------------------------------------+
class ExpertAdvisor : public CObject {
protected:
EAConfig cfg;
CBars bar;
CTradeHedge trade;
CPositions positions;
CTrailing trail;
CNewBar newBar;
CTimer Timer;
Signal glSignal;
CWallPaper backgroundImage;
ulong glBuyTicket;
ulong glSellTicket;
void GetTickets(bool isBuy);
bool CanOpenPosition(bool isBuy);
void HandleTrade(bool isBuy, double volume, double sl = 0, double tp = 0, string comment = "Concrete Goonies");
public:
TradeNotificationCallback Notify;
ExpertAdvisor(const EAConfig &config) : cfg(config) {
trade.Deviation(0.3);
trade.MagicNumber(cfg.magicNumber);
glSignal = SIGNAL_NONE;
Notify = NULL;
}
~ExpertAdvisor() {}
string GetSymbol() const {
return cfg.name;
}
ulong GetMagic() const {
return cfg.magicNumber;
}
ENUM_TIMEFRAMES GetTimeframe() const {
return cfg.timeframe;
}
EAConfig Config() const {
return cfg;
}
void SetConfig(const EAConfig &newCfg) {
cfg = newCfg;
}
virtual void OnTick();
virtual int OnInit();
virtual void Release();
bool InitBackGround(const string path, const char visibility = 100);
void Resize();
double GetVolume();
void ClosePositions();
bool OnNewBar();
bool IsBullish(int index);
bool IsBearish(int index);
};
//+------------------------------------------------------------------+
//| OnTick implementation |
//+------------------------------------------------------------------+
void ExpertAdvisor::OnTick() {
bar.Update(GetSymbol(), cfg.timeframe);
double volume = GetVolume();
// Update tickets
GetTickets(true);
GetTickets(false);
// Signal execution
if(cfg.enabled) {
if(OnNewBar()) {
ClosePositions();
if(GetSymbol() == "Boom 1000 Index" || GetSymbol() == "Boom 500 Index" || GetSymbol() == "Boom 300 Index") {
if(cfg.allowSell) {
if(IsBullish(3) && IsBearish(2) && IsBearish(1)) {
glSignal = SIGNAL_SELL;
}
} else if(cfg.allowBuy) {
if(IsBullish(3) && IsBearish(2) && IsBearish(1)) {
glSignal = SIGNAL_BUY;
}
}
} else
if(GetSymbol() == "Crash 1000 Index" || GetSymbol() == "Crash 500 Index" || GetSymbol() == "Crash 300 Index") {
if(cfg.allowSell) {
if(IsBearish(3) && IsBullish(2) && IsBullish(1)) {
glSignal = SIGNAL_SELL;
}
} else if(cfg.allowBuy) {
if(IsBearish(3) && IsBullish(2) && IsBullish(1)) {
glSignal = SIGNAL_BUY;
}
}
}
else {
if(IsBearish(3) && IsBullish(2) && IsBullish(1)) {
glSignal = SIGNAL_BUY;
}
if(IsBullish(3) && IsBearish(2) && IsBearish(1)) {
glSignal = SIGNAL_SELL;
}
}
if(glSignal == SIGNAL_BUY && cfg.allowBuy && CanOpenPosition(true))
HandleTrade(true, volume);
if(glSignal == SIGNAL_SELL && cfg.allowSell && CanOpenPosition(false))
HandleTrade(false, volume);
}
}
glSignal = SIGNAL_NONE;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool ExpertAdvisor::IsBullish(int index) {
return bar.Close(index) > bar.Open(index);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool ExpertAdvisor::IsBearish(int index) {
return bar.Close(index) < bar.Open(index);
}
//+------------------------------------------------------------------+
//| Init |
//+------------------------------------------------------------------+
int ExpertAdvisor::OnInit() {
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Get volume (MM or fixed) |
//+------------------------------------------------------------------+
double ExpertAdvisor::GetVolume() {
return cfg.useMM
? MoneyManagement(cfg.name, cfg.lotSize, cfg.riskPercent, cfg.stopLoss)
: VerifyVolume(cfg.name, cfg.lotSize);
}
//+------------------------------------------------------------------+
//| Unified ticket fetching |
//+------------------------------------------------------------------+
void ExpertAdvisor::GetTickets(bool isBuy) {
ulong tickets[];
if(isBuy)
positions.GetBuyTickets(cfg.magicNumber, tickets);
else
positions.GetSellTickets(cfg.magicNumber, tickets);
if(ArraySize(tickets) > 0)
isBuy ? glBuyTicket = tickets[0] : glSellTicket = tickets[0];
}
//+------------------------------------------------------------------+
//| Check if we can open position |
//+------------------------------------------------------------------+
bool ExpertAdvisor::CanOpenPosition(bool isBuy) {
return isBuy ? positions.Buy(cfg.magicNumber) < cfg.tradeCount
: positions.Sell(cfg.magicNumber) < cfg.tradeCount;
}
//+------------------------------------------------------------------+
//| Unified trade handler |
//+------------------------------------------------------------------+
void ExpertAdvisor::HandleTrade(bool isBuy, double volume, double sl = 0, double tp = 0, string comment = "Concrete Goonies") {
ulong ticket = isBuy ? glBuyTicket : glSellTicket;
ulong opposite = isBuy ? glSellTicket : glBuyTicket; // Close opposite ticket if exists
if(opposite > 0) {
if(trade.Close(opposite) > 0 && Notify != NULL) {
string msg = "Closed " + (isBuy ? "SELL" : "BUY") + " position ticket: " + IntegerToString(opposite); // Open trade
Notify(msg);
}
}
ticket = isBuy ? trade.Buy(cfg.name, volume, 0, 0, "Zane Strategy") : trade.Sell(cfg.name, volume, 0, 0, "Zane Strategy");
if(ticket > 0) {
double openPrice = PositionOpenPrice(ticket);
double stop = isBuy ? BuyStopLoss(cfg.name, cfg.stopLoss, openPrice) : SellStopLoss(cfg.name, cfg.stopLoss, openPrice);
double profit = isBuy ? BuyTakeProfit(cfg.name, cfg.takeProfit, openPrice) : SellTakeProfit(cfg.name, cfg.takeProfit, openPrice);
if(stop > 0 || profit > 0) trade.ModifyPosition(ticket, stop, profit); // Notify trade opened
if(Notify != NULL) {
string msg = "Opened " + (isBuy ? "BUY" : "SELL") + " position ticket: " + IntegerToString(ticket) + " Symbol: " + cfg.name + " Volume: " + DoubleToString(volume, 2);
Notify(msg);
}
}
}
//+------------------------------------------------------------------+
//| Release resources |
//+------------------------------------------------------------------+
void ExpertAdvisor::Release() {
backgroundImage.Destroy();
}
//+------------------------------------------------------------------+
//| Initialize background image |
//+------------------------------------------------------------------+
bool ExpertAdvisor::InitBackGround(const string path, const char visibility) {
return backgroundImage.Init(path, visibility);
}
//+------------------------------------------------------------------+
//| Resize background image |
//+------------------------------------------------------------------+
void ExpertAdvisor::Resize() {
backgroundImage.Resize();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool ExpertAdvisor::OnNewBar() {
return newBar.CheckNewBar(GetSymbol(), GetTimeframe());
}
//+------------------------------------------------------------------+
//| Close positions based on duration |
//+------------------------------------------------------------------+
void ExpertAdvisor::ClosePositions() {
int total = PositionsTotal();
for(int c = 0; c < total; c++) {
ulong ticket = PositionGetTicket(c);
if(PositionSelectByTicket(ticket)) {
long magic = PositionGetInteger(POSITION_MAGIC);
if(magic == cfg.magicNumber) {
datetime open_time = PositionGetInteger(POSITION_TIME);
int bar_count = Bars(_Symbol, cfg.timeframe, open_time, TimeCurrent());
if(bar_count >= cfg.tradeDuration) {
if(trade.Close(ticket) > 0 && Notify != NULL) {
string msg = "Closed position ticket: " + IntegerToString(ticket) +
" Symbol: " + cfg.name;
Notify(msg);
}
}
}
}
}
}
//+------------------------------------------------------------------+
// ---------- helper ----------
string EAConfigToString(const EAConfig & c) {
string s;
s = "name:\"" + c.name + "\"";
s += ", enabled:" + (c.enabled);
s += ", tradeDuration:" + IntegerToString(c.tradeDuration);
s += ", tradeCount:" + IntegerToString(c.tradeCount);
s += ", lotSize:" + DoubleToString(c.lotSize, 5);
s += ", stopLoss:" + IntegerToString(c.stopLoss);
s += ", takeProfit:" + IntegerToString(c.takeProfit);
s += ", magicNumber:" + IntegerToString((int)c.magicNumber);
s += ", allowBuy:" + (c.allowBuy ? "true" : "false");
s += ", allowSell:" + (c.allowSell ? "true" : "false");
s += ", useMM:" + (c.useMM ? "true" : "false");
s += ", riskPercent:" + DoubleToString(c.riskPercent, 2);
s += ", hidden:" + (c.hidden ? "true" : "false");
s += "";
return s;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+