//+------------------------------------------------------------------+
//|                                                     HMA Trend EA |
//|                                                         Scorpion |
//|                                              www.fxfisherman.com |
//+------------------------------------------------------------------+
#property copyright "Scorpion"
#property link      "http://www.fxfisherman.com"
#include <stdlib.mqh>

#define TS_MODE_DISABLE 0
#define TS_MODE_FIXED_SL 1
#define TS_MODE_ATR 2
#define TS_MODE_HALF_VOLATILITY 3
#define TS_MODE_BREAKOUT 4
#define TS_MODE_BREAKEVEN 5

//---- input parameters
extern string    _______Position_______;
extern double    Lots=0.01;
double    Lots_PCT=10;
bool      Use_Lots_PCT=false;
extern int       TP=0;
extern int       SL=30;
extern int       SL_Mode=0;
extern int       Breakeven_Pips=0;

extern string    ______TrailingStop_____;
extern int       TS_Mode=0;       // 0 = disabled, 1 = Fixed SL, 2 = ATR, 3 = Half Volatility, 4 = Breakout Yesterday Hi/Lo
extern int       TS_Trigger=30;
int       TS_Sensitivity=3;
extern double    TS_DynamicFactor=0.5;   // applied only if TrailingStopMode = 2 or 3
int       Evaluate_Interval=-1; // -1 chart, 0 tick, > 0 specified min

extern string    _______Indicators______;
int       Extra_Pips=10;
extern int       HMA_Period=21;
extern int       HMA_Mode=3;
extern int FasterMode=1; //0=sma, 1=ema, 2=smma, 3=lwma
extern int FasterMA=  5;
extern int SlowerMode=1; //0=sma, 1=ema, 2=smma, 3=lwma
extern int SlowerMA=  7;


string    _______Session_______;
bool Filter_Session=false;
int From_Hour=0;
int From_Min=0;
int To_Hour=24;
int To_Min=0;

string expert_name = "HMA Trend";
int main_magic;
int open_slippage=5;
int close_slippage=10;

int orderType;
double orderPrice;
datetime timeNextEval;

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   main_magic = 2000000 + GetTimeframeConstant(Period()) + GetSymbolConstant(Symbol());
   watermark();
   return(0);
  }
  
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
   return(0);
  }

//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
{
  // set breakeven and trailing stops 
  int ticket = OrderTicketByMagicNum(main_magic);
  if (ticket > 0 && Breakeven_Pips > 0)
    ControlTrailingStop(ticket, SL, TS_MODE_BREAKEVEN, Breakeven_Pips, TS_DynamicFactor, TS_Sensitivity);
  if (ticket > 0 && TS_Mode > 0)
    ControlTrailingStop(ticket, SL, TS_Mode, TS_Trigger, TS_DynamicFactor, TS_Sensitivity);
  
  // analyse now?
  bool isAnalyseNow=false;
  datetime timeNow = CurTime();
  int intervalEval = Evaluate_Interval;
  if (Evaluate_Interval == -1) intervalEval = Period();
  if (!(intervalEval > 0 && timeNow < timeNextEval)) isAnalyseNow=true;

  // analyse chart
  static bool isBuy, isSell, isCloseBought, isCloseSold;
  

bool up = ((iMA(NULL, 0, FasterMA, 0, FasterMode, PRICE_CLOSE, 1) > iMA(NULL, 0, SlowerMA, 0, SlowerMode, PRICE_OPEN, 1)) && (iMA(NULL, 0, FasterMA, 0, FasterMode, PRICE_CLOSE, 2) <= iMA(NULL, 0, SlowerMA, 0, SlowerMode, PRICE_OPEN, 2)));

bool dn = ((iMA(NULL, 0, FasterMA, 0, FasterMode, PRICE_CLOSE, 1) < iMA(NULL, 0, SlowerMA, 0, SlowerMode, PRICE_OPEN, 1)) && (iMA(NULL, 0, FasterMA, 0, FasterMode, PRICE_CLOSE, 2) >= iMA(NULL, 0, SlowerMA, 0, SlowerMode, PRICE_OPEN, 2)));
   
  
  if (isAnalyseNow)
  {
    timeNextEval = timeNow - (timeNow % (intervalEval*60)) + (intervalEval*60);
    ticket = OrderTicketByMagicNum(main_magic);
    //double hma0 = iCustom(Symbol(), Period(), "HMA", HMA_Period, HMA_Mode, 0, 2, 0);
    //double hma1 = iCustom(Symbol(), Period(), "HMA", HMA_Period, HMA_Mode, 0, 2, 1);
    //double hma2 = iCustom(Symbol(), Period(), "HMA", HMA_Period, HMA_Mode, 0, 2, 2);
    
    double pipsExtra = Extra_Pips * Point;
  
    isBuy         = up;      //(hma0 > hma1 && hma1 > hma2);
    isSell        = dn;      //(hma0 < hma1 && hma1 < hma2);
    isCloseBought = dn;      //(hma0 < hma1 && hma1 < hma2);
    isCloseSold   = up;      //(hma0 > hma1 && hma1 > hma2);
    
    // filter out entries if not in trading session
    if (Filter_Session)
    {
      double From_Time = From_Hour + (From_Min/60);
      double To_Time = To_Hour + (To_Min/60);
      double Cur_Time = Hour() + (Minute()/60);
      if (!(From_Time <= Cur_Time && Cur_Time <= To_Time))
      {
        isBuy = False;
        isSell = False;
      }
    }
  }

  // close orders
  ticket = OrderTicketByMagicNum(main_magic);
  if (ticket > 0 && (isCloseBought || isCloseSold)){
    if (OrderSelectEx(ticket,SELECT_BY_TICKET,MODE_TRADES)==false) return(0);  
    if (OrderType() == OP_BUY && isCloseBought){
      CloseNow(ticket);
    }else if (OrderType() == OP_SELL && isCloseSold){
      CloseNow(ticket);
    }
  }
  
  // enter orders
  ticket = OrderTicketByMagicNum(main_magic);
  if (ticket==0){
    static int lastType;
    static int lastDay;
    if (isBuy && !isSell && !(lastType == 1 && lastDay == Today())){
      if (BuyNow()>0) {
        isBuy = false;
        lastType = 1;
        lastDay = Today();
      }
    }else if(isSell && !isBuy && !(lastType == 2 && lastDay == Today())){
      if (SellNow()>0) {
        isSell = false;
        lastType = 2;
        lastDay = Today();
      }
    }else if(isSell && isBuy){
      Print("Error: Buy and sell signals are issued at the same time!");
    }
  }
  
  return(0);
}


//+------------------------------------------------------------------+
//| Buy                                                              |
//+------------------------------------------------------------------+
int BuyNow()
{
    double trueSL, trueTP, lotSize;
    lotSize = GetLots(Symbol(), Lots, Lots_PCT, Use_Lots_PCT);
    trueSL = Get_SL(OP_BUY, Ask, Ask, SL, SL_Mode, TS_DynamicFactor);
    if (TP > 0) trueTP = Bid+(TP*Point);
    int ticket = OrderSendEx(Symbol(), OP_BUY, lotSize, Ask, open_slippage*Point, trueSL, trueTP, expert_name + " " + Symbol() + Period(), main_magic, 0, Yellow);
    if (ticket > 0) orderType = OP_BUY; orderPrice = Bid;
    return(ticket);
}

//+------------------------------------------------------------------+
//| Sell                                                             |
//+------------------------------------------------------------------+

int SellNow()
{
  double trueSL, trueTP, lotSize;
  lotSize = GetLots(Symbol(), Lots, Lots_PCT, Use_Lots_PCT);
  trueSL = Get_SL(OP_SELL, Bid, Bid, SL, SL_Mode, TS_DynamicFactor);
  if (TP > 0) trueTP = Bid-(TP*Point);
  int ticket = OrderSendEx(Symbol(), OP_SELL, lotSize, Bid, open_slippage*Point, trueSL, trueTP, expert_name + " " + Symbol() + Period(), main_magic, 0, Yellow);
  if (ticket > 0) orderType = OP_SELL; orderPrice = Bid;
  return(ticket);
}

//+------------------------------------------------------------------+
//| Control trailing stop                                            |
//+------------------------------------------------------------------+
void ControlTrailingStop(int ticket, double SLs, int TS_Modes, int TS_Triggers, double TS_DynamicFactors, int TS_Sensitivitys)
{
  if (ticket == 0 || TS_Modes == 0) return;
  
  double ts;
  if (OrderSelectEx(ticket, SELECT_BY_TICKET, MODE_TRADES)==false) return;
  if (OrderType() == OP_BUY){
    ts = Get_SL(OP_BUY, OrderOpenPrice(), Bid, SLs, TS_Modes, TS_DynamicFactors);
    if ((ts >= OrderStopLoss() + TS_Sensitivitys*Point) && (ts > 0)  && (Bid >= OrderOpenPrice() + TS_Triggers*Point )) {
      if (Bid - ts >= 5 * Point)
      {
        OrderModifyEx(ticket, OrderOpenPrice(), ts, OrderTakeProfit(), 0, Red);
      }else if(Bid <= ts){
        CloseNow(ticket);
      }
    }
    
  }else if(OrderType() == OP_SELL){
    ts = Get_SL(OP_SELL, OrderOpenPrice(), Ask, SLs, TS_Modes, TS_DynamicFactors);
    if ((ts <= OrderStopLoss() - TS_Sensitivitys*Point) && (ts > 0) && (Ask <= OrderOpenPrice() - TS_Triggers*Point)){
      if (ts - Ask >= 5 * Point)
      {
        OrderModifyEx(ticket, OrderOpenPrice(), ts, OrderTakeProfit(), 0, Red);
      }else if(ts <= Ask){
        CloseNow(ticket);
      }
    }
  }
  
}


double Get_SL(int order_type, double order_price, double price, double sl, int sl_mode, double sl_dynamicfactor)
{
  if (sl_mode == 0) return(0);
  
  double ts;
  //double ma_0, hh, ll;
  if (order_type == OP_BUY){
    switch (sl_mode){
      case TS_MODE_FIXED_SL: if(sl > 0) ts = price-(Point*sl); break;
      case TS_MODE_ATR: ts = Low[0] - (sl_dynamicfactor * iATR(NULL,0,14,0)); break;
      case TS_MODE_HALF_VOLATILITY: ts = Low[0] - (sl_dynamicfactor *(High[0]-Low[0])); break;
      case TS_MODE_BREAKOUT: ts = Low[1] - Point; break;
      case TS_MODE_BREAKEVEN: ts = order_price; break;
    }
  }else if(order_type == OP_SELL){
    switch (sl_mode){
      case TS_MODE_FIXED_SL: if(sl > 0) ts = price+(Point*sl); break;
      case TS_MODE_ATR: ts = High[0] + (sl_dynamicfactor * iATR(NULL,0,14,0)); break;
      case TS_MODE_HALF_VOLATILITY: ts = High[0] + (sl_dynamicfactor *(High[0]-Low[0])); break;
      case TS_MODE_BREAKOUT: ts = High[1] + Point; break;
      case TS_MODE_BREAKEVEN: ts = order_price; break;
    }
  }
  return(ts);
}


//+------------------------------------------------------------------+
//| Close at market price                                            |
//+------------------------------------------------------------------+
bool CloseNow(int ticket)
{
  if (OrderSelectEx(ticket, SELECT_BY_TICKET))
  {
    if (OrderType() == OP_BUY)
    {
      OrderCloseEx(ticket, OrderLots(), Bid, close_slippage);
    }else  if (OrderType() == OP_SELL){
      OrderCloseEx(ticket, OrderLots(), Ask, close_slippage);
    }
  }
return(0);
}

//+------------------------------------------------------------------+
//| Lots size functions (fixed lot, compound lot, etc)               |
//+------------------------------------------------------------------+
double GetLots(string symbol, double lots, double lots_pct, bool use_lots_pct)
{
  double lot;
  if (!use_lots_pct)
  {
    lot = lots;
  }else{
    double lotStep = MarketInfo(symbol, MODE_LOTSTEP);
    double lotSize = MarketInfo(symbol, MODE_LOTSIZE) / AccountLeverage();
    lot = (AccountBalance() * (lots_pct/100)) / lotSize;
    double leftover = MathMod(lot, lotStep);
    if (MathMod(lot/lotStep, 1) >= 0.00001) lot = lot - leftover;
  }
 
  return(lot);
}


//+------------------------------------------------------------------+
//| Extended order execution functions for used in multiple pairs    |
//| with automatic retry attempts.                                   |
//+------------------------------------------------------------------+
int OrderSendEx(string symbol, int cmd, double volume, double price, int slippage, double stoploss, double takeprofit, string comment, int magic, datetime expiration=0, color arrow_color=CLR_NONE) {
  if(!WaitWhileBusy())
  {
    Print("Error in OrderSendEx(): Timeout encountered");
    return(-1);
  }
  SetBusyState();
  int ticket = OrderSend(symbol, cmd, volume, price, slippage, stoploss, takeprofit, comment, magic, expiration, arrow_color);
  Sleep(6000);
  ReleaseBusyState();  
  return(ticket);
}


bool OrderCloseEx(int ticket, double lots, double price, int slippage, color Color=CLR_NONE)
{
  if(!WaitWhileBusy())
  {
    Print("Error in OrderCloseEx(): Timeout encountered");
    return(false);
  }
  SetBusyState();
  bool ret = OrderClose(ticket, lots, price, slippage, Color);
  Sleep(6000);
  ReleaseBusyState();  
  return(ret);
}


bool OrderModifyEx( int ticket, double price, double stoploss, double takeprofit, datetime expiration, color arrow_color=CLR_NONE)
{
  if(!WaitWhileBusy())
  {
    Print("Error in OrderModifyEx(): Timeout encountered");
    return(false);
  }
  SetBusyState();
  bool ret = OrderModify(ticket, price, stoploss, takeprofit, expiration, arrow_color);
  if(ret)
  {
    Sleep(6000);
  }else{
    Print("Error in OrderModifyEx(): ", LastErrorText());
  }
  ReleaseBusyState();  
  return(ret);
}


bool OrderSelectEx(int index, int select, int pool = MODE_TRADES)
{
  if (OrderSelect(index,select,pool)==true)
  {
    return(true);
  }else{
    Print("Error: Order #", index ," cannot be selected. ", LastErrorText());
  }
return(0);
}


//+------------------------------------------------------------------+
//| Calling state functions                                          |
//+------------------------------------------------------------------+
bool WaitWhileBusy()
{
   datetime OldCurTime;
   int timeoutsec=6;
   
   OldCurTime=CurTime();
   while (GlobalVariableCheck("InTrade") || !IsTradeAllowed()) {
      if(OldCurTime + timeoutsec <= CurTime()) {
         return(false); 
      }
      Sleep(1000);
   }
   return(true);
}


void SetBusyState()
{
  GlobalVariableSet("InTrade", CurTime());  // set lock indicator
}

void ReleaseBusyState()
{
  GlobalVariableDel("InTrade");   // clear lock indicator
}

//+------------------------------------------------------------------+
//| Get order ticket by magic number                                 |
//+------------------------------------------------------------------+
int OrderTicketByMagicNum(int magic_number) {

  for(int i=0;i<OrdersTotal();i++)
  {
    if (OrderSelect(i, SELECT_BY_POS) == false) continue;
    if (OrderMagicNumber() == magic_number) return(OrderTicket());
  }   
      
return(0);
}

//+------------------------------------------------------------------+
//| Time frame interval appropriation function                       |
//+------------------------------------------------------------------+
int GetTimeframeConstant(int chart_period) {
   switch(chart_period) {
      case 1:  // M1
         return(50);
      case 5:  // M5
         return(100);
      case 15:
         return(150);
      case 30:
         return(200);
      case 60:
         return(250);
      case 240:
         return(300);
      case 1440:
         return(350);
      case 10080:
         return(400);
      case 43200:
         return(450);
   }
   return(0);
}


//+------------------------------------------------------------------+
//| Symbol to index                                                  |
//+------------------------------------------------------------------+
int GetSymbolConstant(string symbol) {

	if(symbol=="EURUSD" || symbol=="mEURUSD" || symbol=="EURUSDm") {	return(1);
	} else if(symbol=="GBPUSD" || symbol=="GBPUSDm") { return(2);
	} else if(symbol=="USDCHF" || symbol=="USDCHFm") {	return(3);
	} else if(symbol=="USDJPY" || symbol=="USDJPYm") {	return(4);
	} else if(symbol=="USDCAD" || symbol=="USDCADm") {	return(5);
	} else if(symbol=="AUDUSD" || symbol=="AUDUSDm") {	return(6);
	} else if(symbol=="CHFJPY" || symbol=="CHFJPYm") {	return(7);
	} else if(symbol=="EURAUD" || symbol=="EURAUDm") {	return(8);
	} else if(symbol=="EURCAD" || symbol=="EURCADm") {	return(9);
	} else if(symbol=="EURCHF" || symbol=="EURCHFm") {	return(10);
	} else if(symbol=="EURGBP" || symbol=="EURGBPm") {	return(11);
	} else if(symbol=="EURJPY" || symbol=="EURJPYm") {	return(12);
  } else if(symbol=="GBPCHF" || symbol=="GBPCHFm") {	return(13);
	} else if(symbol=="GBPJPY" || symbol=="GBPJPYm") {	return(14);
	} else if(symbol=="GOLD"   || symbol=="GOLDm") {	return(15);
	} else {Print("Error: Unexpected symbol."); return(0);
	}
}

//+------------------------------------------------------------------+
//| Get last error description                                       |
//+------------------------------------------------------------------+
string LastErrorText()
{
  return(ErrorDescription(GetLastError()));
}


datetime StripTime(datetime dt)
{
  return (dt - (TimeHour(dt)*3600) - (TimeMinute(dt)*60) - TimeSeconds(dt));
}

datetime Today()
{
  return (StripTime(CurTime()));
}

void watermark()
  {
   ObjectCreate("fxfisherman", OBJ_LABEL, 0, 0, 0);
   ObjectSetText("fxfisherman", "code example only", 11, "Lucida Handwriting", RoyalBlue);
   ObjectSet("fxfisherman", OBJPROP_CORNER, 2);
   ObjectSet("fxfisherman", OBJPROP_XDISTANCE, 5);
   ObjectSet("fxfisherman", OBJPROP_YDISTANCE, 10);
   
  }