//+------------------------------------------------------------------+
//|                            Fuzzy logic for fuzzy algotraders.mq5 |
//|                                Copyright 2017, Dmitrievskiy Max. |
//|                        https://www.mql5.com/ru/users/dmitrievsky |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Dmitrievskiy Max."
#property link      "https://www.mql5.com/ru/users/dmitrievsky"
#property version   "1.00"
#include <Math\Fuzzy\MamdaniFuzzySystem.mqh>
#include <Trade\AccountInfo.mqh>
#include <MT4Orders.mqh>

CMamdaniFuzzySystem *OurFuzzy=new CMamdaniFuzzySystem();

CFuzzyVariable *firstInput=new CFuzzyVariable("rsi1",0.0,1.0);
CFuzzyVariable *secondInput=new CFuzzyVariable("rsi2",0.0,1.0);
CFuzzyVariable *thirdInput=new CFuzzyVariable("rsi3",0.0,1.0);
CFuzzyVariable *fuzzyOut=new CFuzzyVariable("out",0.0,1.0);

CDictionary_Obj_Double *firstTerm=new CDictionary_Obj_Double;
CDictionary_Obj_Double *secondTerm=new CDictionary_Obj_Double;
CDictionary_Obj_Double *thirdTerm=new CDictionary_Obj_Double;
CDictionary_Obj_Double *Output;

CMamdaniFuzzyRule *rule1, *rule2, *rule3, *rule4, *rule5, *rule6, *rule7, *rule8, *rule9, *rule10, *rule11, *rule12;

CList *Inputs=new CList;

int hnd1, hnd2, hnd3;
double arr1[], arr2[], arr3[];

input string Fuzzy_Setings;        //Fuzzy optimization settings
input double      Gsigma = 0.5;    //sigma From 0.05 to 0.5 with 0.05 step
input double      Gposition=0.5;   //position From 0.0 to 1.0 with 0.1 step
input double MinNeutralSignal=0.4; //MinNeutralSignal from 0.3 to 0.5 with 0.1 step
input double MaxNeutralSignal=0.6; //MaxNeutralSignal from 0.5 to 0.7 with 0.1 step

input string Lotsettings;          //Trade settings
input double   MaximumRisk=0.01;
input double   CustomLot=0;    
input int      OrderMagic=666;

double   lots;
static datetime last_time=0;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   Inputs.FreeMode(false);
   hnd1 = iRSI(_Symbol,0,9,PRICE_CLOSE);
   hnd2 = iRSI(_Symbol,0,14,PRICE_CLOSE);
   hnd3 = iRSI(_Symbol,0,21,PRICE_CLOSE);
   
   
   firstInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
   firstInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(0.5, 0.2)));
   firstInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
   OurFuzzy.Input().Add(firstInput);
   
   secondInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
   secondInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(0.5, 0.2)));
   secondInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
   OurFuzzy.Input().Add(secondInput);
   
   thirdInput.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
   thirdInput.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(0.5, 0.2)));
   thirdInput.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
   OurFuzzy.Input().Add(thirdInput);
   
   fuzzyOut.Terms().Add(new CFuzzyTerm("buy", new CZ_ShapedMembershipFunction(0.0,0.6)));
   fuzzyOut.Terms().Add(new CFuzzyTerm("neutral", new CNormalMembershipFunction(Gposition, Gsigma)));
   fuzzyOut.Terms().Add(new CFuzzyTerm("sell", new CS_ShapedMembershipFunction(0.4,1.0)));
   OurFuzzy.Output().Add(fuzzyOut);
   
   rule1 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is buy) and (rsi3 is buy) then (out is buy)");
   rule2 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is sell) and (rsi3 is sell) then (out is sell)");
   rule3 = OurFuzzy.ParseRule("if (rsi1 is neutral) and (rsi2 is neutral) and (rsi3 is neutral) then (out is neutral)"); 
   
   rule4 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is sell) and (rsi3 is buy) then (out is neutral)");
   rule5 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is sell) and (rsi3 is buy) then (out is neutral)");
   rule6 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is buy) and (rsi3 is sell) then (out is neutral)"); 
   
   rule7 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is buy) and (rsi3 is neutral) then (out is buy)");
   rule8 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is sell) and (rsi3 is neutral) then (out is sell)");
   rule9 = OurFuzzy.ParseRule("if (rsi1 is buy) and (rsi2 is neutral) and (rsi3 is buy) then (out is buy)");
   rule10 = OurFuzzy.ParseRule("if (rsi1 is sell) and (rsi2 is neutral) and (rsi3 is sell) then (out is sell)");
   rule11 = OurFuzzy.ParseRule("if (rsi1 is neutral) and (rsi2 is buy) and (rsi3 is buy) then (out is buy)");
   rule12 = OurFuzzy.ParseRule("if (rsi1 is neutral) and (rsi2 is sell) and (rsi3 is sell) then (out is sell)");
   
   
   OurFuzzy.Rules().Add(rule1);
   OurFuzzy.Rules().Add(rule2);
   OurFuzzy.Rules().Add(rule3);
   OurFuzzy.Rules().Add(rule4);
   OurFuzzy.Rules().Add(rule5);
   OurFuzzy.Rules().Add(rule6);
   OurFuzzy.Rules().Add(rule7);
   OurFuzzy.Rules().Add(rule8);
   OurFuzzy.Rules().Add(rule9);
   OurFuzzy.Rules().Add(rule10);
   OurFuzzy.Rules().Add(rule11);
   OurFuzzy.Rules().Add(rule12);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   Inputs.FreeMode(true);
   delete Inputs;
   delete OurFuzzy;
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(!isNewBar())
     {  
      return;
     }
     
   double TradeSignal=CalculateMamdani(); 
   
   if(CountOrders(0)!=0 || CountOrders(1)!=0)
      {
       for(int b=OrdersTotal()-1; b>=0; b--)
         {
          if(OrderSelect(b,SELECT_BY_POS)==true)
           {
            if(OrderSymbol()==_Symbol && OrderMagicNumber()==OrderMagic)
             {
              if(OrderType()==OP_BUY && TradeSignal>=MinNeutralSignal)
               {
                if(OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),0,Red))
                 {
                  if(TradeSignal>MaxNeutralSignal)
                  {
                   lots = LotsOptimized(); 
                   if(OrderSend(Symbol(),OP_SELL,lots,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0,0,NULL,OrderMagic,Red)){
                     };
                  }
                 }
               }
               if(OrderType()==OP_SELL && TradeSignal<=MaxNeutralSignal)
               {
                if(OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),0,Red))
                 {
                  if(TradeSignal<MinNeutralSignal)
                  {
                   lots = LotsOptimized(); 
                   if(OrderSend(Symbol(),OP_BUY,lots,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0,0,NULL,OrderMagic,Green)){
                    };
                  }
                 }
               }
             }
            }
          }
         return;
        }
        
   lots = LotsOptimized(); 
   if(TradeSignal<MinNeutralSignal && CheckMoneyForTrade(_Symbol,lots,ORDER_TYPE_BUY))
     { 
      if(OrderSend(Symbol(),OP_BUY,lots,SymbolInfoDouble(_Symbol,SYMBOL_ASK),0,0,0,NULL,OrderMagic,Green)){
       };
     }
   else if(TradeSignal>MaxNeutralSignal && CheckMoneyForTrade(_Symbol,lots,ORDER_TYPE_SELL))
     {
      if(OrderSend(Symbol(),OP_SELL,lots,SymbolInfoDouble(_Symbol,SYMBOL_BID),0,0,0,NULL,OrderMagic,Red)){
       };
     }
   return;
     
  }
//+------------------------------------------------------------------+

double CalculateMamdani()
{
 CopyBuffer(hnd1,0,0,1,arr1);
 NormalizeArrays(arr1);
   
 CopyBuffer(hnd2,0,0,1,arr2);
 NormalizeArrays(arr2);
    
 CopyBuffer(hnd3,0,0,1,arr3);
 NormalizeArrays(arr3);
     
 firstTerm.SetAll(firstInput,arr1[0]);
 secondTerm.SetAll(secondInput,arr2[0]);
 thirdTerm.SetAll(thirdInput,arr2[0]);
       
 Inputs.Clear(); 
 Inputs.Add(firstTerm);
 Inputs.Add(secondTerm);
 Inputs.Add(thirdTerm);
      
 CList *FuzzResult=OurFuzzy.Calculate(Inputs);
 Output=FuzzResult.GetNodeAtIndex(0);
 double res = Output.Value();
 delete FuzzResult;

 return(res);
}

void NormalizeArrays(double &a[])
  {
   double d1=0;
   double d2=1.0;
   double x_min=0.0;
   double x_max=100;
   for(int i=0;i<ArraySize(a);i++)
     {
      a[i]=(((a[i]-x_min)*(d2-d1))/(x_max-x_min))+d1;
     }
  }
  
int CountOrders(int a)
  {
   int result=0;
   for(int k=0; k<OrdersTotal(); k++)
     {
      if(OrderSelect(k,SELECT_BY_POS,MODE_TRADES)==true)
        {
         if(OrderType()==a && OrderMagicNumber()==OrderMagic && OrderSymbol()==_Symbol)
           {result++;}
        }
     }
   return(result);
  }
  
double LotsOptimized()
  {
   CAccountInfo myaccount; SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
   double lot;
   
   lot=NormalizeDouble(myaccount.FreeMargin()*MaximumRisk/1000.0,2);
   if(CustomLot!=0.0) lot=CustomLot;
   
   double volume_step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
   int ratio=(int)MathRound(lot/volume_step);
   if(MathAbs(ratio*volume_step-lot)>0.0000001)
     {
      lot = ratio*volume_step;
     }
   if(lot<SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)) lot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   if(lot>SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)) lot=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   return(lot);
  }
  
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool isNewBar()
  {
   datetime lastbar_time=datetime(SeriesInfoInteger(Symbol(),_Period,SERIES_LASTBAR_DATE));
   if(last_time==0)
     {
      last_time=lastbar_time;
      return(false);
     }
   if(last_time!=lastbar_time)
     {
      last_time=lastbar_time;
      return(true);
     }
   return(false);
  
  }
//+------------------------------------------------------------------+

bool CheckMoneyForTrade(string symb,double lot,ENUM_ORDER_TYPE type)
  {
//--- Getting the opening price
   MqlTick mqltick;
   SymbolInfoTick(symb,mqltick);
   double price=mqltick.ask;
   if(type==ORDER_TYPE_SELL)
      price=mqltick.bid;
//--- values of the required and free margin
   double margin,free_margin=AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   //--- call of the checking function
   if(!OrderCalcMargin(type,symb,lots,price,margin))
     {
      //--- something went wrong, report and return false
      Print("Error in ",__FUNCTION__," code=",GetLastError());
      return(false);
     }
   //--- if there are insufficient funds to perform the operation
   if(margin>free_margin)
     {
      //--- report the error and return false
      Print("Not enough money for ",EnumToString(type)," ",lots," ",symb," Error code=",GetLastError());
      return(false);
     }
//--- checking successful
   return(true);
  }