ForexTheFirst/ForexTheFirst.mq5

263 lines
19 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 14:55:54 +02:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| ForexTheFirst.mq5 |
//| Marino Bantli |
//| maban.ch |
//+------------------------------------------------------------------+
//--- input parameters
input double TimeLookback = 7;
input int AverageCandlePriceLookback = 100;
input int TEMAPeriod = 14;
input int DojiProportions = 8;
input int TPOffsetTicks = 1;
input int SLOffsetTicks = 1;
input double MinimumRR = 1.0;
input double MaximumRisk = 0.01;
//+------------------------------------------------------------------+
//| Includes |
//+------------------------------------------------------------------+
#include "..\HighLow\HighLowFinder.mqh"
#include "CandleTools.mqh"
#include "TradeEngine.mqh"
//+------------------------------------------------------------------+
//| Enums |
//+------------------------------------------------------------------+
enum State
{
None,
Ready,
Observing,
OrderPending,
InTrade,
AfterTrade
};
//+------------------------------------------------------------------+
//| Objects |
//+------------------------------------------------------------------+
HighLowFinder* HL_Finder;
CandleTools* CTools;
TradeEngine* trade;
//+------------------------------------------------------------------+
//| Variables |
//+------------------------------------------------------------------+
int handle;
MqlRates bars[];
double indicatorValues[];
uint currentBar = 0;
double pipSize = 0;
double tickSize = 0;
State currentState = None;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
handle = iTEMA(Symbol(), PERIOD_CURRENT, TEMAPeriod, 0, PRICE_CLOSE);
HL_Finder = new HighLowFinder(10, 10, 200, SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE));
currentBar = Bars(Symbol(),PERIOD_CURRENT) - 1;
tickSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE);
pipSize = tickSize * 10;
trade = new TradeEngine(1234);
if(SeriesInfoInteger(Symbol(),Period(),SERIES_SYNCHRONIZED))
{
CopyRates(Symbol(), Period(), TimeCurrent(), Bars(Symbol(),PERIOD_CURRENT), bars);
}
else
{
Alert("Error syncing data!");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
HL_Finder = NULL;
currentState = None;
pipSize = NULL;
tickSize = NULL;
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
uint newBar = Bars(Symbol(),PERIOD_CURRENT);
if(currentBar == newBar - 1) { return; }
CopyBuffer(handle, 0, 0, newBar, indicatorValues);
CopyRates(Symbol(), Period(), TimeCurrent(), newBar, bars);
currentBar = newBar - 1;
if(currentState == None)
{
HL_Finder.Initalize(bars, indicatorValues);
currentState = Ready;
return;
}
Process();
}
void Process()
{
//Keep Indicator up-to-date
if (HL_Finder.Process(bars, indicatorValues))
{
AddPricePoint(HL_Finder.points[HL_Finder.points.Size() - 1].index, HL_Finder.points[HL_Finder.points.Size() - 1].price, HL_Finder.points[HL_Finder.points.Size() - 1].type);
}
MqlTick price;
double sl = NULL;
double tp = NULL;
double lots = NULL;
switch (currentState)
{
case 1: //Ready
currentState = Observing;
break;
case 2: //Observing
if(HL_Finder.points[HL_Finder.points.Size() - 1].index == currentBar - 2)
{
uint lastPointIndex = HL_Finder.points.Size() - 1;
SymbolInfoTick(Symbol() ,price);
if(HL_Finder.points[lastPointIndex].type == "high")
{
tp = HL_Finder.points[lastPointIndex - 1].price - (TPOffsetTicks * tickSize);
sl = HL_Finder.points[lastPointIndex].price + (SLOffsetTicks * tickSize);
if(CheckCriteria(ORDER_TYPE_SELL, price.bid, tp, sl))
{
lots = trade.GetLots(price.bid, sl, MaximumRisk);
trade.NewDirectEntry(ORDER_TYPE_SELL, lots, sl, tp, "BotEntry. RR = 1:" + trade.GetRiskReward(price.bid, tp, sl));
return;
}
}
if(HL_Finder.points[lastPointIndex].type == "low")
{
tp = HL_Finder.points[lastPointIndex - 1].price + (TPOffsetTicks * tickSize);
sl = HL_Finder.points[lastPointIndex].price - (SLOffsetTicks * tickSize);
if(CheckCriteria(ORDER_TYPE_BUY, price.ask, tp, sl))
{
lots = trade.GetLots(price.ask, sl, MaximumRisk);
trade.NewDirectEntry(ORDER_TYPE_BUY, lots, sl, tp, "BotEntry. RR = 1:" + trade.GetRiskReward(price.ask, tp, sl));
return;
}
}
}
break;
}
}
bool CheckCriteria(ENUM_ORDER_TYPE forDirection, double entry, double tp, double sl)
{
double avgBarSize = CTools.GetAverageBarSize(bars, AverageCandlePriceLookback);
double lowestLow = CTools.GetLowestLow(bars, Bars(Symbol(), PERIOD_CURRENT, (TimeCurrent() - (TimeLookback * 86400)), bars[currentBar - 2].time));
double highestHigh = CTools.GetHighestHigh(bars, Bars(Symbol(), PERIOD_CURRENT, (TimeCurrent() - (TimeLookback * 86400)), bars[currentBar - 2].time));
if(!CTools.IsDoji(bars, currentBar - 2, pipSize, DojiProportions)) { return false; }
if(CTools.GetBodySize(bars, currentBar - 3, pipSize) < avgBarSize) { return false; }
if(CTools.GetBodySize(bars, currentBar - 1, pipSize) < avgBarSize) { return false; }
switch(forDirection)
{
case ORDER_TYPE_BUY:
if(HL_Finder.points[HL_Finder.points.Size() - 1].price > lowestLow) { return false; }
if(HL_Finder.points[HL_Finder.points.Size() - 2].price < bars[currentBar].open) { return false; }
if(trade.GetRiskReward(entry, tp, sl) < MinimumRR) { return false; }
break;
case ORDER_TYPE_SELL:
if(HL_Finder.points[HL_Finder.points.Size() - 1].price < highestHigh) { return false; }
if(HL_Finder.points[HL_Finder.points.Size() - 2].price > bars[currentBar].open) { return false; }
if(trade.GetRiskReward(entry, tp, sl) < MinimumRR) { return false; }
break;
}
return true;
}
//+------------------------------------------------------------------+
//| Iterate through prices to find spikes |
//+------------------------------------------------------------------+
void AddPricePoint(uint index, double price, string ptType)
{
if(ptType == "high")
{
CreateText(ChartID(), "Point_" + (ptType + (string)index + (string)price), bars[index].time, price, clrLime, "O", 15, "Arial");
}
if(ptType == "low")
{
CreateText(ChartID(), "Point_" + (ptType + (string)index + (string)price), bars[index].time, price, clrRed, "O", 15, "Arial");
}
}
//+------------------------------------------------------------------+
//| Create text on Chart |
//+------------------------------------------------------------------+
void CreateText(long chart_ID, string name, datetime time=0, double price=0, color clr=clrRed, string text="", int size=10, string font="Cascadia Mono")
{
ResetLastError();
if(!ObjectCreate(chart_ID, name, OBJ_TEXT, 0, time, price))
{
Print(__FUNCTION__, ": failed to create a text! Error code = ", GetLastError());
}
ObjectSetString(chart_ID, name, OBJPROP_TEXT, text);
ObjectSetString(chart_ID, name, OBJPROP_FONT, font);
ObjectSetInteger(chart_ID, name, OBJPROP_FONTSIZE, size);
ObjectSetDouble(chart_ID, name, OBJPROP_ANGLE, 0);
ObjectSetInteger(chart_ID, name, OBJPROP_ANCHOR, ANCHOR_CENTER);
ObjectSetInteger(chart_ID, name, OBJPROP_COLOR, clr);
ObjectSetInteger(chart_ID, name, OBJPROP_FILL, false);
ObjectSetInteger(chart_ID, name, OBJPROP_BACK, true);
ObjectSetInteger(chart_ID, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(chart_ID, name, OBJPROP_SELECTED, false);
ObjectSetInteger(chart_ID, name, OBJPROP_ZORDER, 1);
ObjectSetInteger(chart_ID, name, OBJPROP_HIDDEN, false);
}
//+------------------------------------------------------------------+
//| Delete the text |
//+------------------------------------------------------------------+
void RemoveText(long chart_ID, string name)
{
ResetLastError();
if(!ObjectDelete(chart_ID, name))
{
Print(__FUNCTION__, ": failed to delete text! Error code = ", GetLastError());
}
}