263 lines
No EOL
19 KiB
MQL5
263 lines
No EOL
19 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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());
|
|
}
|
|
} |