//+------------------------------------------------------------------+
//|                                                  ExpFVEWPRBB.mq5 |
//|                                  Copyright 2026, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Trade\Trade.mqh>
#include <Arrays\ArrayLong.mqh>

//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//--- Signal types
enum ENUM_SIGNAL_TYPE
  {
   SIGNAL_TYPE_NONE,                                                 // No signal
   SIGNAL_TYPE_LONG,                                                 // Buy signal
   SIGNAL_TYPE_SHORT,                                                // Buy signal
  };
  
//--- List of used volumes
enum ENUM_USED_VOLUME
  {
   USED_VOLUME_REAL,                                                 // Real Volume
   USED_VOLUME_TICK,                                                 // Tick Volume
  };

//--- States of the FVESlope and PriceSlope lines
enum ENUM_LINE_SLOPE_STATE
  {
   LINE_SLOPE_STATE_UNKNOWN,                                         // State is undefined
   LINE_SLOPE_STATE_FLAT,                                            // Low activity
   LINE_SLOPE_STATE_GROWING,                                         // Volumes are growing
   LINE_SLOPE_STATE_FALLING,                                         // Volumes are falling
   LINE_SLOPE_STATE_TURN_TO_GROW,                                    // Transition to volume growth
   LINE_SLOPE_STATE_TURN_TO_FALL                                     // Transition to volume decrease
  };
  
//--- Mutual states of the FVESlope and PriceSlope lines
enum ENUM_FVE_STATE
  {
   FVE_STATE_UNKNOWN,                                                // State is undefined
   
   //--- FVE GROWING property group (Growth)
   FVE_STATE_FVE_GROWING_PRICE_GROWING,                              // FVE is growing, Price is growing
   FVE_STATE_FVE_GROWING_PRICE_FALLING,                              // FVE is growing, Price is falling
   FVE_STATE_FVE_GROWING_PRICE_TURN_GROW,                            // FVE is growing, Price has reversed up
   FVE_STATE_FVE_GROWING_PRICE_TURN_FALL,                            // FVE is growing, Price has reversed down
   
   //--- FVE FALLING group of properties (Fall)
   FVE_STATE_FVE_FALLING_PRICE_FALLING,                              // FVE is falling, Price is falling
   FVE_STATE_FVE_FALLING_PRICE_GROWING,                              // FVE is falling, Price is growing
   FVE_STATE_FVE_FALLING_PRICE_TURN_FALL,                            // FVE is falling, Price has reversed down
   FVE_STATE_FVE_FALLING_PRICE_TURN_GROW,                            // FVE is falling, Price has reversed up
   
   //--- Group of FVE TURN_GROW properties (Upward reversal)
   FVE_STATE_FVE_TURN_GROW_PRICE_GROWING,                            // FVE has reversed up, Price is growing
   FVE_STATE_FVE_TURN_GROW_PRICE_FALLING,                            // FVE has reversed up, Price is falling
   FVE_STATE_FVE_TURN_GROW_PRICE_TURN_GROW,                          // FVE has reversed up, Price has reversed up
   FVE_STATE_FVE_TURN_GROW_PRICE_TURN_FALL,                          // FVE has reversed up, Price has reversed down
   
   //--- Group of FVE TURN_FALL properties (Downward reversal)
   FVE_STATE_FVE_TURN_FALL_PRICE_FALLING,                            // FVE has reversed down, Price is falling
   FVE_STATE_FVE_TURN_FALL_PRICE_GROWING,                            // FVE has reversed down, Price is growing
   FVE_STATE_FVE_TURN_FALL_PRICE_TURN_FALL,                          // FVE has reversed down, Price has reversed down
   FVE_STATE_FVE_TURN_FALL_PRICE_TURN_GROW                           // FVE has reversed down, Price has reversed up
  };

//+------------------------------------------------------------------+
//| Structures                                                       |
//+------------------------------------------------------------------+
//--- Position structure
struct SData
  {
   CArrayLong  list_tickets;                                         // List of tickets of open positions
   double      total_volume;                                         // Total volume of open positions
  };

//--- Data structure of positions by types
struct SDataPositions
  {
   SData       Buy;                                                  // Buy position data
   SData       Sell;                                                 // Sell position data
  }Data;

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define  DATA_COUNT        3                                         // Number of data received from indicators (3 and more)
#define  ENV_ATTEMPTS      3                                         // Number of attempts to wait for the environment to be received
#define  ENV_WAIT_ATTEMPT  1000                                      // Number of milliseconds to wait for environment updates
#define  SPREAD_MLTP       3                                         // Spread multiplier for stop order distance

//+------------------------------------------------------------------+
//| Input parameters                                                 |
//+------------------------------------------------------------------+
//--- WPR
input int                  InpPeriodWPR      =  32;                  /* WPR calculation period */
input double               InpOverboughtWPR  = -20;                  /* WPR Overbought Level */
input double               InpOversoldWPR    = -80;                  /* WPR Oversold Level */
//--- BB
input int                  InpPeriodBB       =  58;                  /* BB calculation period */
input double               InpDeviationBB    =  2.0;                 /* BB deviations */
input int                  InpShiftBB        =  0;                   /* BB shift */
input ENUM_APPLIED_PRICE   InpPriceBB        =  PRICE_CLOSE;         /* BB applied price */
//--- ATR
input int                  InpPeriodATR      =  64;                  /* ATR calculation period */

//--- FinVolEleLinRegSl
input int                  InpSamples        =  22;                  /* FVERegSl Samples */
input double               InpCutOff         =  0.3;                 /* FVERegSl Threshold */
input int                  InpSlopePeriod    =  35;                  /* FVERegSl SlopePeriod */
input double               InpPriceSlopeFactor= 2500;                /* FVERegSl PriceSlopeFactor */
input ENUM_USED_VOLUME     InpUsedVolume     =  USED_VOLUME_TICK;    /* FVERegSl Used Volume */
input bool                 InpUseFilterByFVE =  true;                /* Use signal filtering by FVERegSl */

//--- Trading
input double               InpVolume         =  0.1;                 /* Position volume */
sinput ulong               InpDeviation      =  10;                  /* Slippage (in points) */
sinput ulong               InpMagic          =  123456;              /* Magic number */
input int                  InpStopLoss       =  -1;                  /* Stop loss (in points), 0 - none, -1 - half of BB */
input int                  InpTakeProfit     =  -1;                  /* Take profit (in points), 0 - none, -1 - ATR value */
input double               InpSLMltp         =  2.6;                 /* Stop loss size multiplier, if SL==-1 */
input double               InpTPMltp         =  1.3;                 /* Take profit size multiplier, if TP==-1 */

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
CTrade   trade;                                                      // Trade class object
int      handle_wpr;                                                 // WPR indicator handle
int      handle_bb;                                                  // BB indicator handle
int      handle_atr;                                                 // ATR indicator handle
int      handle_fve;                                                 // FVERegSl indicator handle
double   wpr[DATA_COUNT]={};                                         // WPR value array
double   bb0[DATA_COUNT]={};                                         // BB value array, buffer 0 (Upper)
double   bb1[DATA_COUNT]={};                                         // BB value array, buffer 1 (Lower)
double   bb2[DATA_COUNT]={};                                         // BB value array, buffer 2 (Middle)
double   atr[DATA_COUNT]={};                                         // ATR value array
double   fv0[DATA_COUNT]={};                                         // FVESlope value array
double   fv1[DATA_COUNT]={};                                         // FVEPriceSlope value array
MqlRates prc[DATA_COUNT]={};                                         // Price and time array

int      period_wpr;                                                 // WPR calculation period
double   overbought_wpr;                                             // WPR overbought level
double   oversold_wpr;                                               // WPR oversold level

int      period_bb;                                                  // BB calculation period
double   deviation_bb;                                               // BB deviations
int      shift_bb;                                                   // BB shift

int      period_atr;                                                 // ATR calculation period

int      samples;                                                    // FVERegSl calculation period
double   cutoff;                                                     // FVERegSl sensitivity threshold
int      slope_period;                                               // FVERegSl linear regression period

double   lot;                                                        // Position volume
string   program_name;                                               // Program name
int      prev_total;                                                 // Number of positions in the previous check
bool     netto;                                                      // Net account feature

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- If the account is not a hedging type, set a flag and report the EA's incorrect operation
   netto=false;
   if(AccountInfoInteger(ACCOUNT_MARGIN_MODE)!=ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
     {
      Print("The advisor is designed for use on a hedging account. Correct operation on a netting account is not guaranteed.");
      netto=true;
     }
   
//--- Set and adjust the indicator inputs
//--- WPR
   period_wpr=(InpPeriodWPR<1 ? 14 : InpPeriodWPR);
   overbought_wpr=(InpOverboughtWPR<-99  ? -99  : InpOverboughtWPR> 0   ?  0  : InpOverboughtWPR);
   oversold_wpr  =(InpOversoldWPR  <-100 ? -100 : InpOversoldWPR  >-1   ? -1  : InpOversoldWPR);
   if(overbought_wpr<=oversold_wpr)
      overbought_wpr+=1;
//--- BB
   period_bb=(InpPeriodBB<2 ? 20 : InpPeriodBB);
   deviation_bb=InpDeviationBB;
   shift_bb=InpShiftBB;
//--- ATR
   period_atr=(InpPeriodATR<1 ? 14 : InpPeriodATR);
//--- FVERegSl
   samples=(InpSamples<1? 22 : InpSamples);
   cutoff=InpCutOff/100.0;
   slope_period=(InpSlopePeriod<2? 35 : InpSlopePeriod);
   
//--- Initialize arrays of indicator values
   ArrayInitialize(wpr,EMPTY_VALUE);
   ArrayInitialize(bb0,EMPTY_VALUE);
   ArrayInitialize(bb1,EMPTY_VALUE);
   ArrayInitialize(bb2,EMPTY_VALUE);
   ArrayInitialize(atr,EMPTY_VALUE);
   ArrayInitialize(fv0,EMPTY_VALUE);
   ArrayInitialize(fv1,EMPTY_VALUE);
   ZeroMemory(prc);

//--- Create indicator handles
//--- WPR
   handle_wpr=iWPR(Symbol(),PERIOD_CURRENT,period_wpr);
   if(handle_wpr==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create iWPR(%d) handle",__FUNCTION__,period_wpr);
      return INIT_FAILED;
     }
//--- BB
   handle_bb=iBands(Symbol(),PERIOD_CURRENT,period_bb,shift_bb,deviation_bb,InpPriceBB);
   if(handle_bb==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create iBands(%d,%d,%.3f,%s) handle",__FUNCTION__,period_bb,shift_bb,deviation_bb,EnumToString(InpPriceBB));
      return INIT_FAILED;
     }
//--- ATR
   handle_atr=iATR(Symbol(),PERIOD_CURRENT,period_atr);
   if(handle_atr==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create iATR(%d) handle",__FUNCTION__,period_atr);
      return INIT_FAILED;
     }
//--- FVERegSl
   handle_fve=iCustom(Symbol(),PERIOD_CURRENT,"FinVolEleLinRegSl",samples,cutoff,slope_period,InpPriceSlopeFactor,InpUsedVolume);
   if(handle_fve==INVALID_HANDLE)
     {
      PrintFormat("%s: Failed to create FinVolEleLinRegSl(%d,%.3f,%d,%.1f) handle",__FUNCTION__,samples,cutoff,slope_period,InpPriceSlopeFactor);
      return INIT_FAILED;
     }

//--- Program name and number of positions in the previous check
   program_name=MQLInfoString(MQL_PROGRAM_NAME);
   prev_total=0;
   
//--- Automatic setting of the filling type
   trade.SetTypeFilling(GetTypeFilling());
//--- Set magic
   trade.SetExpertMagicNumber(InpMagic);
//--- Set slippage
   trade.SetDeviationInPoints(InpDeviation);
//--- Set the lot with adjustment of the entered value
   lot=CorrectLots(InpVolume);
   
//--- All is successful
   PrintFormat("%s::%s: Initialization was successful",program_name,__FUNCTION__);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Get data from three indicator bars and prices in arrays
   if(!CopyIndicatorsData(1) || !CopyPricesData(1))
      return;
   
//--- Fill the lists of position tickets
   int positions_total=PositionsTotal();
   if(prev_total!=positions_total)
     {
      if(!FillingListTickets(Symbol(),InpMagic))
         return;
      prev_total=positions_total;
     }
   
//--- Get signals from indicators
   ENUM_SIGNAL_TYPE signal_wpr=SignalWPR();
   ENUM_SIGNAL_TYPE signal_bb=SignalBB();
//--- General signal
   ENUM_SIGNAL_TYPE signal=(signal_wpr==signal_bb ? signal_wpr : SIGNAL_TYPE_NONE);
   
//--- If EA signals sorting is used,
   if(InpUseFilterByFVE)
     {
      //--- adjust the signal based on the state of the FinVolEleLinRegSl indicator
      signal=SignalByFVELinRegSl(signal,0);
     }
     
//--- Trade by signals
   TradeProcess(signal);
  }
//+------------------------------------------------------------------+
//| Get Open values for three bars                                   |
//+------------------------------------------------------------------+
bool CopyPricesData(const int index)
  {
   ResetLastError();
   if(CopyRates(Symbol(),PERIOD_CURRENT,index,DATA_COUNT,prc)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get price Open data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Get WPR values for three bars                                    |
//+------------------------------------------------------------------+
bool CopyWPRData(const int index)
  {
   ResetLastError();
   if(CopyBuffer(handle_wpr,0,index,DATA_COUNT,wpr)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get WPR data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Get BB values for three bars                                     |
//+------------------------------------------------------------------+
bool CopyBBData(const int index)
  {
   ResetLastError();
   if(CopyBuffer(handle_bb,UPPER_BAND,index,DATA_COUNT,bb0)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get BB Upper Line data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   if(CopyBuffer(handle_bb,LOWER_BAND,index,DATA_COUNT,bb1)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get BB Lower Line data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   if(CopyBuffer(handle_bb,BASE_LINE,index,DATA_COUNT,bb2)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get BB Base Line data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Get ATR values for three bars                                    |
//+------------------------------------------------------------------+
bool CopyATRData(const int index)
  {
   ResetLastError();
   if(CopyBuffer(handle_atr,0,index,DATA_COUNT,atr)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get ATR data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Get FVERegSl values for three bars                               |
//+------------------------------------------------------------------+
bool CopyFVERegSlData(const int index)
  {
   if(index<0)
      return false;
   ResetLastError();
   if(CopyBuffer(handle_fve,0,index,DATA_COUNT,fv0)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get FVESlope Line data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   if(CopyBuffer(handle_fve,1,index,DATA_COUNT,fv1)!=DATA_COUNT)
     {
      PrintFormat("%s: Failed to get PriceSlope Line data. Error %d",__FUNCTION__,GetLastError());
      return false;
     }
   return true;
  }
//+------------------------------------------------------------------+
//| Get indicator values for three bars                              |
//+------------------------------------------------------------------+
bool CopyIndicatorsData(const int index)
  {
   bool res=CopyWPRData(index);     // WPR data retrieval result
   res &=CopyBBData(index);         // BB data retrieval result
   res &=CopyATRData(index);        // ATR data retrieval result
   res &=CopyFVERegSlData(index);   // FVERegSl data retrieval result
   return res;
  }
//+-----------------------------------------------------------------------+
//| Return the Open price from the array by the timeseries index (0 - 2)  |
//+-----------------------------------------------------------------------+
double PriceOpen(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].open);
  }
//+-----------------------------------------------------------------------+
//| Return the High price from the array by the timeseries index (0 - 2)  |
//+-----------------------------------------------------------------------+
double PriceHigh(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].high);
  }
//+-----------------------------------------------------------------------+
//| Return the Low price from the array by the timeseries index (0 - 2)   |
//+-----------------------------------------------------------------------+
double PriceLow(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].low);
  }
//+-----------------------------------------------------------------------+
//| Return the Close price from the array by the timeseries index (0 - 2) |
//+-----------------------------------------------------------------------+
double PriceClose(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].close);
  }
//+-----------------------------------------------------------------------+
//| Return the bar time from the array by the timeseries index (0 - 2)    |
//+-----------------------------------------------------------------------+
datetime Time(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? 0 : prc[DATA_COUNT-index-1].time);
  }
//+------------------------------------------------------------------+
//| Return WPR data from an array by timeseries index (0 - 2)        |
//+------------------------------------------------------------------+
double WPR(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : wpr[DATA_COUNT-index-1]);
  }
//+------------------------------------------------------------------+
//|Return BB Upper data from the array by timeseries index (0 - 2)   |
//+------------------------------------------------------------------+
double BBUpper(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : bb0[DATA_COUNT-index-1]);
  }
//+------------------------------------------------------------------+
//|Return BB Lower data from the array by timeseries index (0 - 2)   |
//+------------------------------------------------------------------+
double BBLower(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : bb1[DATA_COUNT-index-1]);
  }
//+--------------------------------------------------------------------+
//|Return BB Middle data from the array by the timeseries index (0 - 2)|
//+--------------------------------------------------------------------+
double BBMiddle(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : bb2[DATA_COUNT-index-1]);
  }
//+------------------------------------------------------------------+
//| Return half the width of the BB in points                        |
//+------------------------------------------------------------------+
int HalfSizeBB(const int index)
  {
   double up=BBUpper(index);
   double dn=BBLower(index);
   if(up==EMPTY_VALUE || dn==EMPTY_VALUE)
      return 0;
   return (int)round(((up-dn)/2.0)/Point());
  }
//+------------------------------------------------------------------+
//| Return ATR data from an array by timeseries index (0 - 2)        |
//+------------------------------------------------------------------+
double ATR(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : atr[DATA_COUNT-index-1]);
  }
//+------------------------------------------------------------------+
//|Return FVERegSl data from an array by timeseries index (0 - 2)    |
//+------------------------------------------------------------------+
double FVESlope(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : fv0[DATA_COUNT-index-1]);
  }
//+------------------------------------------------------------------+
//| Return PriceSl data from an array by timeseries index (0 - 2)    |
//+------------------------------------------------------------------+
double FVEPriceSlope(const int index)
  {
   return(index<0 || index>DATA_COUNT-1 ? EMPTY_VALUE : fv1[DATA_COUNT-index-1]);
  }
//+------------------------------------------------------------------+
//| WPR signal                                                       |
//+------------------------------------------------------------------+
ENUM_SIGNAL_TYPE SignalWPR(void)
  {
//--- Get WPR prices
   double wpr0=WPR(0);
   double wpr1=WPR(1);
   double wpr2=WPR(2);
//--- Error - no signal
   if(wpr0==EMPTY_VALUE || wpr1==EMPTY_VALUE || wpr2==EMPTY_VALUE)
      return SIGNAL_TYPE_NONE;
      
//--- A Buy signal
   if(wpr0>wpr1 && wpr1<=wpr2 && wpr1<=oversold_wpr)
      return SIGNAL_TYPE_LONG;
//--- A sell signal
   if(wpr0<wpr1 && wpr1>=wpr2 && wpr1>=overbought_wpr)
      return SIGNAL_TYPE_SHORT;
//--- No signal
   return SIGNAL_TYPE_NONE;
  }
//+------------------------------------------------------------------+
//| BB signal                                                        |
//+------------------------------------------------------------------+
ENUM_SIGNAL_TYPE SignalBB(void)
  {
//--- Get BB and Open prices
   double bbup=BBUpper(0);
   double bbdn=BBLower(0);
   double bbmd=BBMiddle(0);
   double price=PriceOpen(0);
//--- Error - no signal
   if(bbup==EMPTY_VALUE || bbdn==EMPTY_VALUE || bbmd==EMPTY_VALUE || price==0)
      return SIGNAL_TYPE_NONE;
   
//--- Average prices above and below the average line
   double upper=(bbup+bbmd)*0.5;
   double lower=(bbdn+bbmd)*0.5;
   
//--- Signal to go long
   if(price<lower)
      return SIGNAL_TYPE_LONG;
//--- Signal to go short
   if(price>upper)
      return SIGNAL_TYPE_SHORT;
//--- No signal
   return SIGNAL_TYPE_NONE;
  }
//+------------------------------------------------------------------+
//| FVESlope line state                                              |
//+------------------------------------------------------------------+
ENUM_LINE_SLOPE_STATE StateFVESlope(void)
  {
//--- Get FVESlope data
   double fve0=FVESlope(0);
   double fve1=FVESlope(1);
   double fve2=FVESlope(2);
//--- Error receiving data - no signal
   if(fve0==EMPTY_VALUE || fve1==EMPTY_VALUE || fve2==EMPTY_VALUE)
      return LINE_SLOPE_STATE_UNKNOWN;
      
//--- Volume is growing
   if(fve0>fve1 && fve1>fve2)
      return LINE_SLOPE_STATE_GROWING;
//--- Volume is falling
   if(fve0<fve1 && fve1<fve2)
      return LINE_SLOPE_STATE_FALLING;
//--- Transition to growth (there was a decline/flat, now there is growth)
   if(fve0>fve1 && fve1<=fve2)
      return LINE_SLOPE_STATE_TURN_TO_GROW;
//--- Transition to a fall (there was growth/flat, now there is a decline)
   if(fve0<fve1 && fve1>=fve2)
      return LINE_SLOPE_STATE_TURN_TO_FALL;
//--- Flat
   return LINE_SLOPE_STATE_FLAT;   
  }
//+------------------------------------------------------------------+
//| PriceSlope line state                                            |
//+------------------------------------------------------------------+
ENUM_LINE_SLOPE_STATE StatePriceSlope(void)
  {
//--- Get PriceSlope data
   double fve0=FVEPriceSlope(0);
   double fve1=FVEPriceSlope(1);
   double fve2=FVEPriceSlope(2);
//--- Error receiving data - no signal
   if(fve0==EMPTY_VALUE || fve1==EMPTY_VALUE || fve2==EMPTY_VALUE)
      return LINE_SLOPE_STATE_UNKNOWN;
      
//--- Price is growing
   if(fve0>fve1 && fve1>fve2)
      return LINE_SLOPE_STATE_GROWING;
//--- Price is falling
   if(fve0<fve1 && fve1<fve2)
      return LINE_SLOPE_STATE_FALLING;
//--- Transition to growth (there was a decline/flat, now there is growth)
   if(fve0>fve1 && fve1<=fve2)
      return LINE_SLOPE_STATE_TURN_TO_GROW;
//--- Transition to a fall (there was growth/flat, now there is a decline)
   if(fve0<fve1 && fve1>=fve2)
      return LINE_SLOPE_STATE_TURN_TO_FALL;
//--- Flat
   return LINE_SLOPE_STATE_FLAT;   
  }
//+------------------------------------------------------------------+
//| Mutual state of the FVESlope and PriceSlope lines                |
//+------------------------------------------------------------------+
ENUM_FVE_STATE StateFVELinRegSl(const int index)
  {
//--- Copy FVELinRegSl data to the array starting from the specified index
   if(!CopyFVERegSlData(index))
      return FVE_STATE_UNKNOWN;
   
//--- Get the states of the FVESlope and PriceSlope lines
   ENUM_LINE_SLOPE_STATE fve_slope=StateFVESlope();
   ENUM_LINE_SLOPE_STATE prc_slope=StatePriceSlope();
   
//--- Analysis of the FVELinRegSl line states
   switch(fve_slope)
     {
      //--- FVE is growing
      case LINE_SLOPE_STATE_GROWING:
         switch(prc_slope)
           {
            case LINE_SLOPE_STATE_GROWING:      return FVE_STATE_FVE_GROWING_PRICE_GROWING;     // [Bullish] Confirmed growth: both lines are steadily up
            case LINE_SLOPE_STATE_FALLING:      return FVE_STATE_FVE_GROWING_PRICE_FALLING;     // [Bullish] Sustained bullish convergence: volume rising against falling price
            case LINE_SLOPE_STATE_TURN_TO_GROW: return FVE_STATE_FVE_GROWING_PRICE_TURN_GROW;   // [Bullish] Confirmation: price has reversed up on increasing volume
            case LINE_SLOPE_STATE_TURN_TO_FALL: return FVE_STATE_FVE_GROWING_PRICE_TURN_FALL;   // [Bearish] Hidden bearish divergence: price has reversed down on growing volume
           }
        break;
   
      //--- FVE is falling
      case LINE_SLOPE_STATE_FALLING:
         switch(prc_slope)
           {
            case LINE_SLOPE_STATE_FALLING:      return FVE_STATE_FVE_FALLING_PRICE_FALLING;     // [Bearish] Confirmed fall: both lines are steadily down
            case LINE_SLOPE_STATE_GROWING:      return FVE_STATE_FVE_FALLING_PRICE_GROWING;     // [Bearish] Persistent bearish divergence: volume falls while price rises
            case LINE_SLOPE_STATE_TURN_TO_FALL: return FVE_STATE_FVE_FALLING_PRICE_TURN_FALL;   // [Bearish] Confirmation: price has reversed down to falling volume
            case LINE_SLOPE_STATE_TURN_TO_GROW: return FVE_STATE_FVE_FALLING_PRICE_TURN_GROW;   // [Bullish] Hidden bullish convergence: price has reversed up on falling volume
           }
        break;
   
      //--- FVE has reversed up
      case LINE_SLOPE_STATE_TURN_TO_GROW:
         switch(prc_slope)
           {
            case LINE_SLOPE_STATE_GROWING:      return FVE_STATE_FVE_TURN_GROW_PRICE_GROWING;   // [Bullish] Volume momentum: FVE has reversed up to chase rising price
            case LINE_SLOPE_STATE_FALLING:      return FVE_STATE_FVE_TURN_GROW_PRICE_FALLING;   // [Bullish] Start of a bullish convergence: volume has reversed up against the price decline
            case LINE_SLOPE_STATE_TURN_TO_GROW: return FVE_STATE_FVE_TURN_GROW_PRICE_TURN_GROW; // [Bullish] Synchronous upward reversal: strong buy signal
            case LINE_SLOPE_STATE_TURN_TO_FALL: return FVE_STATE_FVE_TURN_GROW_PRICE_TURN_FALL; // [Bullish] Mirror Convergence: FVE up, price down (searching for bottom)
           }
        break;
   
      //--- FVE has reversed down
      case LINE_SLOPE_STATE_TURN_TO_FALL:
         switch(prc_slope)
           {
            case LINE_SLOPE_STATE_FALLING:      return FVE_STATE_FVE_TURN_FALL_PRICE_FALLING;   // [Bearish] Volume momentum: FVE has reversed down in pursuit of falling price
            case LINE_SLOPE_STATE_GROWING:      return FVE_STATE_FVE_TURN_FALL_PRICE_GROWING;   // [Bearish] Bearish divergence begins: volume has reversed down against rising price
            case LINE_SLOPE_STATE_TURN_TO_FALL: return FVE_STATE_FVE_TURN_FALL_PRICE_TURN_FALL; // [Bearish] Synchronous downside reversal: strong sell signal
            case LINE_SLOPE_STATE_TURN_TO_GROW: return FVE_STATE_FVE_TURN_FALL_PRICE_TURN_GROW; // [Bearish] Mirror spread: FVE down, price up (searching for top)
           }
        break;
     }   
   
//--- Undefined state
   return FVE_STATE_UNKNOWN;
  }
//+------------------------------------------------------------------+
//| Return a text description of the FinVolEleLinRegSl               |
//+------------------------------------------------------------------+
string FVEStateDescription(ENUM_FVE_STATE state)
  {
   switch(state)
     {
      //--- Bullish signals
      case FVE_STATE_FVE_GROWING_PRICE_GROWING:      return("[Bullish] Confirmed growth (Trend)");
      case FVE_STATE_FVE_GROWING_PRICE_FALLING:      return("[Bullish] Sustained convergence (Accumulation)");
      case FVE_STATE_FVE_GROWING_PRICE_TURN_GROW:    return("[Bullish] Price confirmed the volume increase");
      case FVE_STATE_FVE_FALLING_PRICE_TURN_GROW:    return("[Bullish] Hidden convergence (Price Reversal)");
      case FVE_STATE_FVE_TURN_GROW_PRICE_GROWING:    return("[Bullish] Volume momentum following price");
      case FVE_STATE_FVE_TURN_GROW_PRICE_FALLING:    return("[Bullish] Convergence start (FVE Bottom)");
      case FVE_STATE_FVE_TURN_GROW_PRICE_TURN_GROW:  return("[Bullish] Synchronous upward reversal (Strong Signal)");
      case FVE_STATE_FVE_TURN_GROW_PRICE_TURN_FALL:  return("[Bullish] Mirror reversal (Volume Reversal)");

      //--- Bearish signals
      case FVE_STATE_FVE_FALLING_PRICE_FALLING:      return("[Bearish] Confirmed fall (Trend)");
      case FVE_STATE_FVE_FALLING_PRICE_GROWING:      return("[Bearish] Persistent divergence (Distribution)");
      case FVE_STATE_FVE_FALLING_PRICE_TURN_FALL:    return("[Bearish] Price confirmed falling volume");
      case FVE_STATE_FVE_GROWING_PRICE_TURN_FALL:    return("[Bearish] Hidden divergence (Price Reversal)");
      case FVE_STATE_FVE_TURN_FALL_PRICE_FALLING:    return("[Bearish] Volume momentum following price");
      case FVE_STATE_FVE_TURN_FALL_PRICE_GROWING:    return("[Bearish] Divergence start (FVE Top)");
      case FVE_STATE_FVE_TURN_FALL_PRICE_TURN_FALL:  return("[Bearish] Synchronous reversal down (Strong Signal)");
      case FVE_STATE_FVE_TURN_FALL_PRICE_TURN_GROW:  return("[Bearish] Mirror divergence (Volume Reversal)");

      //--- By default
      default: return("Undefined state");
     }
  }  
//+------------------------------------------------------------------+
//| FinVolEleLinRegSl signal on the bar specified by index           |
//+------------------------------------------------------------------+
ENUM_SIGNAL_TYPE SignalByFVELinRegSl(const ENUM_SIGNAL_TYPE signal,const int index)
  {
//--- Get the state of the FVELinRegSl indicator at the specified index
   ENUM_FVE_STATE fve_state=StateFVELinRegSl(index);

//--- By uncommenting the lines, we get the output of the indicator state in a chart comment
   /*
   static string text="";
   string txt=StringFormat("\nStateFVE: %s (%s)",StringSubstr(EnumToString(fve_state),10),FVEStateDescription(fve_state));
   if(text!=txt)
     {
      text=txt;
      Comment(text);
     }
   */
//--- Depending on the direction of the signal, we simply limit the opposite signals using the filter
   switch(signal)
     {
      //--- in case of a buy signal
      case SIGNAL_TYPE_LONG   :
        //--- if the filter state shows the opposite signal, make the main signal absent
        switch(fve_state)
          {
           case FVE_STATE_FVE_FALLING_PRICE_FALLING      :  //[Bearish] Confirmed fall (Trend)
           case FVE_STATE_FVE_FALLING_PRICE_GROWING      :  //[Bearish] Persistent divergence (Distribution)
           case FVE_STATE_FVE_FALLING_PRICE_TURN_FALL    :  //[Bearish] Price confirmed the volume fall
           case FVE_STATE_FVE_GROWING_PRICE_TURN_FALL    :  //[Bearish] Hidden divergence (Price Reversal)
           case FVE_STATE_FVE_TURN_FALL_PRICE_FALLING    :  //[Bearish] Volume momentum following price
           case FVE_STATE_FVE_TURN_FALL_PRICE_GROWING    :  //[Bearish] Divergence start (FVE Top)
           case FVE_STATE_FVE_TURN_FALL_PRICE_TURN_FALL  :  //[Bearish] Synchronous reversal down (Strong Signal)
           case FVE_STATE_FVE_TURN_FALL_PRICE_TURN_GROW  :  //[Bearish] Mirror reversal (Volume Reversal)
             return SIGNAL_TYPE_NONE;
           break;
           //--- otherwise, if the main signal matches the direction of the signal by the filter, return the signal unchanged
           default: return signal;
          }
        break;
      //--- in case of a sell signal
      case SIGNAL_TYPE_SHORT  :
        //--- if the filter state shows the opposite signal, make the main signal absent
        switch(fve_state)
          {
           case FVE_STATE_FVE_GROWING_PRICE_GROWING      :  //[Bullish] Confirmed growth (Trend)
           case FVE_STATE_FVE_GROWING_PRICE_FALLING      :  //[Bullish] Stable convergence (Accumulation)
           case FVE_STATE_FVE_GROWING_PRICE_TURN_GROW    :  //[Bullish] Price confirmed the increase in volume
           case FVE_STATE_FVE_FALLING_PRICE_TURN_GROW    :  //[Bullish] Hidden convergence (Price Reversal)
           case FVE_STATE_FVE_TURN_GROW_PRICE_GROWING    :  //[Bullish] Volume momentum following price
           case FVE_STATE_FVE_TURN_GROW_PRICE_FALLING    :  //[Bullish] Convergence begins (FVE Bottom)
           case FVE_STATE_FVE_TURN_GROW_PRICE_TURN_GROW  :  //[Bullish] Synchronous upward reversal (Strong Signal)
           case FVE_STATE_FVE_TURN_GROW_PRICE_TURN_FALL  :  //[Bullish] Mirror reversal (Volume Reversal)
             return SIGNAL_TYPE_NONE;
           break;
           //--- otherwise, if the main signal matches the direction of the signal by the filter, return the signal unchanged
           default: return signal;
          }
        break;
      //--- return the signal unchanged
      default: return signal;
     }
  }
//+------------------------------------------------------------------+
//| Place signal icons on the chart                                  |
//+------------------------------------------------------------------+
void SetArrow(const ENUM_INDICATOR indicator,const ENUM_SIGNAL_TYPE signal,bool chart_redraw)
  {
//--- If there is no signal, or an incorrect indicator type is passed, leave
   if(signal==SIGNAL_TYPE_NONE || (indicator!=IND_WPR && indicator!=IND_BANDS))
      return;
   
//--- Get the open time of the current bar
   datetime time=Time(0);
   if(time==0)
      return;

   ENUM_OBJECT obj_type;   // Object type
   double      price=0;    // Price for installing the icon
//--- Depending on the indicator type, we set the price and icon type
   switch(indicator)
     {
      //--- WPR
      case IND_WPR :
        price=PriceOpen(0);
        obj_type=(signal==SIGNAL_TYPE_LONG ? OBJ_ARROW_BUY : OBJ_ARROW_SELL);
        break;
      
      //--- BB
      default:
        obj_type=OBJ_ARROW;
        price=(signal==SIGNAL_TYPE_LONG ? BBLower(0) : BBUpper(0));
        break;
     }
//--- If we could not get the time, leave
   if(price==0)
      return;
   
//--- Create an object name
   string ind=(indicator==IND_WPR ? "_WPR" : "_BB");
   string sig=(signal==SIGNAL_TYPE_LONG ? "_Long_signal_" : "_Short_signal_");
   string name=program_name+ind+sig+TimeToString(time);
//--- If an object with this name already exists, leave
   if(ObjectFind(0,name)==0)
      return;
      
//--- Create the object
   if(!ObjectCreate(0,name,obj_type,0,time,price))
      return;
   
//--- set a sign color
   ObjectSetInteger(0,name,OBJPROP_COLOR,(signal==SIGNAL_TYPE_LONG ? clrBlue : clrRed));
//--- set a line style (when highlighted)
   ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
//--- set a line size (when highlighted)
   ObjectSetInteger(0,name,OBJPROP_WIDTH,0);
   
//--- set the arrow code for the BB signal
   if(indicator==IND_BANDS)
      ObjectSetInteger(0,name,OBJPROP_ARROWCODE,159);
   
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(0,name,OBJPROP_BACK,false);
//--- disable the mode of selecting and moving the object with the mouse
   ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
   ObjectSetInteger(0,name,OBJPROP_SELECTED,false);
//--- hide the name of the graphical object in the list of objects
   ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
   
//--- Redraw the chart
   if(chart_redraw)
      ChartRedraw();
  }
//+-----------------------------------------------------------------------+
//| Return an order expiration type equal to 'type'                       |
//| if it is available on a symbol, otherwise, return the correct option  |
//| https://www.mql5.com/ru/forum/170952/page4#comment_4128864            |
//+-----------------------------------------------------------------------+
ENUM_ORDER_TYPE_FILLING GetTypeFilling(const ENUM_ORDER_TYPE_FILLING type=ORDER_FILLING_RETURN)
  {
   const ENUM_SYMBOL_TRADE_EXECUTION exe_mode=(ENUM_SYMBOL_TRADE_EXECUTION)::SymbolInfoInteger(Symbol(),SYMBOL_TRADE_EXEMODE);
   const int filling_mode=(int)::SymbolInfoInteger(Symbol(),SYMBOL_FILLING_MODE);

   return((filling_mode==0 || (type>=ORDER_FILLING_RETURN) || ((filling_mode &(type+1))!=type+1)) ?
          (((exe_mode==SYMBOL_TRADE_EXECUTION_EXCHANGE) || (exe_mode==SYMBOL_TRADE_EXECUTION_INSTANT)) ?
          ORDER_FILLING_RETURN :((filling_mode==SYMBOL_FILLING_IOC) ? ORDER_FILLING_IOC : ORDER_FILLING_FOK)) : type);
  }
//+------------------------------------------------------------------+
//| Return the correct lot                                           |
//+------------------------------------------------------------------+
double CorrectLots(const double lots,const bool to_min_correct=true)
  {
   double min=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   double max=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX);
   double step=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP);
   return(to_min_correct ? VolumeRoundToSmaller(lots,min,max,step) : VolumeRoundToCorrect(lots,min,max,step));
  }
//+------------------------------------------------------------------+
//| Return the nearest valid lot                                     |
//+------------------------------------------------------------------+
double VolumeRoundToCorrect(const double volume,const double min,const double max,const double step)
  {
   return(step==0 ? min : fmin(fmax(round(volume/step)*step,min),max));
  }
//+------------------------------------------------------------------+
//| Return the nearest valid lot to the lower side                   |
//+------------------------------------------------------------------+
double VolumeRoundToSmaller(const double volume,const double min,const double max,const double step)
  {
   return(step==0 ? min : fmin(fmax(floor(volume/step)*step,min),max));
  }
//+----------------------------------------------------------------------------------------+
//| Return the flag indicating that the total volume on the account has not been exceeded  |
//+----------------------------------------------------------------------------------------+
bool CheckLotForLimitAccount(const ENUM_POSITION_TYPE position_type,const double volume)
  {
   double lots_limit=SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_LIMIT);
   if(lots_limit==0)
      return true;
   double total_volume=(position_type==POSITION_TYPE_BUY ? Data.Buy.total_volume : Data.Sell.total_volume);
   return(total_volume+volume<=lots_limit);
  }
//+------------------------------------------------------------------+
//| Return correct StopLoss relative to StopLevel                    |
//+------------------------------------------------------------------+
double CorrectStopLoss(const ENUM_POSITION_TYPE position_type,const int stop_loss)
  {
   if(stop_loss==0)
      return 0;
   double pt=Point();
   double price=(position_type==POSITION_TYPE_BUY ? SymbolInfoDouble(Symbol(),SYMBOL_ASK) : SymbolInfoDouble(Symbol(),SYMBOL_BID));
   int lv=StopLevel(), dg=Digits();
   return(position_type==POSITION_TYPE_BUY   ?  NormalizeDouble(fmin(price-lv*pt,price-stop_loss*pt),dg) :
                                                NormalizeDouble(fmax(price+lv*pt,price+stop_loss*pt),dg));
  }
//+------------------------------------------------------------------+
//| Return correct TakeProfit relative to StopLevel                  |
//+------------------------------------------------------------------+
double CorrectTakeProfit(const ENUM_POSITION_TYPE position_type,const int take_profit)
  {
   if(take_profit==0)
      return 0;
   double pt=Point();
   double price=(position_type==POSITION_TYPE_BUY ? SymbolInfoDouble(Symbol(),SYMBOL_ASK) : SymbolInfoDouble(Symbol(),SYMBOL_BID));
   int lv=StopLevel(), dg=Digits();
   return(position_type==POSITION_TYPE_BUY   ?  NormalizeDouble(fmax(price+lv*pt,price+take_profit*pt),dg) :
                                                NormalizeDouble(fmin(price-lv*pt,price-take_profit*pt),dg));
  }
//+------------------------------------------------------------------+
//| Return the calculated StopLevel                                  |
//+------------------------------------------------------------------+
int StopLevel(void)
  {
   int sp=(int)SymbolInfoInteger(Symbol(),SYMBOL_SPREAD);
   int lv=(int)SymbolInfoInteger(Symbol(),SYMBOL_TRADE_STOPS_LEVEL);
   return(lv==0 ? sp*SPREAD_MLTP : lv);
  }
//+------------------------------------------------------------------+
//| Return the "undefined" state of the trading environment          |
//+------------------------------------------------------------------+
bool IsUncertainStateEnv(const string symbol_name,const ulong magic_number)
  {
//--- In the tester, the environment state is always correct
   if(MQLInfoInteger(MQL_TESTER))
      return false;
//--- In a loop by the number of orders
   int total=OrdersTotal();
   for(int i=total-1; i>=0; i--)
     {
      //--- select an order to get its properties
      if(OrderGetTicket(i)==0)
         continue;
      //--- if the order magic number does not match the one we are looking for,  we skip it
      if(OrderGetInteger(ORDER_MAGIC)!=magic_number)
         continue;
      //--- if the order type is neither Buy nor Sell, skip it
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
      if(type!=ORDER_TYPE_BUY && type!=ORDER_TYPE_SELL)
         continue;
      //--- if the order symbol matches the one being searched for, but the order does not contain a record of the position ID,
      //--- this means that the data about the opening position has not yet been added to the order history.
      //--- This is an undefined environment state - return 'true'
      if(!OrderGetInteger(ORDER_POSITION_ID) && OrderGetString(ORDER_SYMBOL)==symbol_name)
         return true;
     }
//--- The environment is fine
   return false;
  }
//+------------------------------------------------------------------+
//| Check the environment state                                      |
//+------------------------------------------------------------------+
bool CheckUncertainStateEnv(const string symbol_name,const ulong magic_number,const int attempts,const int wait)
  {
//--- If the environment is OK, return 'true'
   if(IsUncertainStateEnv(symbol_name,magic_number))
      return true;
//--- Make a cycle of ENV_ATTEMPTS attempts to obtain the correct environment, waiting for ENV_WAIT_ATTEMPT between attempts
   int n=0;
   while(!IsStopped() && n<attempts && IsUncertainStateEnv(symbol_name,magic_number))
     {
      n++;
      Sleep(wait);
     }
//--- If the environment is still undefined after the wait, report this and return 'false'
   if(n>=attempts && IsUncertainStateEnv(symbol_name,magic_number))
     {
      PrintFormat("%s: Uncertain state of the environment. Please try again.",__FUNCTION__);
      return false;
     }
//--- The environment is correct
   return true;
  }
//+------------------------------------------------------------------+
//| Fill in the position ticket arrays                               |
//+------------------------------------------------------------------+
bool FillingListTickets(const string symbol_name,const ulong magic_number)
  {
//--- If the trading environment is invalid, return 'false'
   if(!CheckUncertainStateEnv(symbol_name,magic_number,ENV_ATTEMPTS,ENV_WAIT_ATTEMPT))
      return false;

//--- Clear the lists and initialize the variables
   Data.Buy.list_tickets.Clear();
   Data.Sell.list_tickets.Clear();
   Data.Buy.total_volume=0;
   Data.Sell.total_volume=0;
   
//--- In a loop through open positions
   int total=PositionsTotal();
   for(int i=total-1; i>WRONG_VALUE; i--)
     {
      //--- select a position to get properties
      ulong ticket=PositionGetTicket(i);
      if(ticket==0)
         continue;
      //--- If the magic number or symbol does not match those passed to the function, move on.
      if(PositionGetInteger(POSITION_MAGIC)!=InpMagic || PositionGetString(POSITION_SYMBOL)!=symbol_name)
         continue;
      //--- Get position type and volume
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
      double volume=PositionGetDouble(POSITION_VOLUME);
      //--- Depending on the position type, add the ticket and volume to the corresponding lists
      if(type==POSITION_TYPE_BUY)
        {
         Data.Buy.list_tickets.Add(ticket);
         Data.Buy.total_volume+=volume;
        }
      //--- POSITION_TYPE_SELL
      else
        {
         Data.Sell.list_tickets.Add(ticket);
         Data.Sell.total_volume+=volume;
        }
     }
//--- All is successful
   return true;
  }
//+------------------------------------------------------------------+
//| Return the number of Buy positions                               |
//+------------------------------------------------------------------+
int TotalBuy(void)
  {
   return Data.Buy.list_tickets.Total();
  }
//+------------------------------------------------------------------+
//| Return the number of Sell positions                              |
//+------------------------------------------------------------------+
int TotalSell(void)
  {
   return Data.Sell.list_tickets.Total();
  }
//+------------------------------------------------------------------+
//| Returns the last added ticket of the position by type            |
//+------------------------------------------------------------------+
ulong LastAddedTicket(const ENUM_POSITION_TYPE type)
  {
   return(type==POSITION_TYPE_BUY ? (TotalBuy()>0 ? Data.Buy.list_tickets.At(0) : 0) : (TotalSell()>0 ? Data.Sell.list_tickets.At(0) : 0));
  }
//+------------------------------------------------------------------+
//| Returns the number of the bar the position was opened on         |
//+------------------------------------------------------------------+
int PositionBar(const ulong ticket)
  {
//--- Select a position by ticket
   ResetLastError();
   if(!PositionSelectByTicket(ticket))
     {
      PrintFormat("%s: Failed to select position by ticket #%I64u. Error %d",__FUNCTION__,ticket,GetLastError());
      return -1;
     }
//--- Get the open time and position symbol
   datetime time=(datetime)PositionGetInteger(POSITION_TIME);
   string   symbol=PositionGetString(POSITION_SYMBOL);
   
//--- Return the bar number based on the position opening time
   return iBarShift(symbol,PERIOD_CURRENT,time);
  }
//+------------------------------------------------------------------+
//| Return whether the specified position is open on the current bar |
//+------------------------------------------------------------------+
bool IsPresentPosOnCurrentBar(const ENUM_POSITION_TYPE type)
  {
   ulong ticket=LastAddedTicket(type);
   return(ticket>0 ? PositionBar(ticket)==0 : false);
  }
//+------------------------------------------------------------------+
//| Return the position open price by ticket                         |
//+------------------------------------------------------------------+
double PositionPriceOpen(const ulong ticket)
  {
//--- Check the ticket
   if(ticket==0)
      return 0;
//--- Select a position by ticket
   ResetLastError();
   if(!PositionSelectByTicket(ticket))
     {
      PrintFormat("%s: Failed to select position by ticket #%I64u. Error %d",__FUNCTION__,ticket,GetLastError());
      return 0;
     }
//--- Return the position open price
   return PositionGetDouble(POSITION_PRICE_OPEN);
  }
//+------------------------------------------------------------------+
//| Close Buy positions                                              |
//+------------------------------------------------------------------+
bool CloseBuy(void)
  {
   int total=TotalBuy();
   bool res=true;
   for(int i=total-1; i>=0; i--)
     {
      ulong ticket=Data.Buy.list_tickets.At(i);
      if(ticket==NULL)
         continue;
      if(!trade.PositionClose(ticket,InpDeviation))
         res=false;
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Close Sell positions                                             |
//+------------------------------------------------------------------+
bool CloseSell(void)
  {
   int total=TotalSell();
   bool res=true;
   for(int i=total-1; i>=0; i--)
     {
      ulong ticket=Data.Sell.list_tickets.At(i);
      if(ticket==NULL)
         continue;
      if(!trade.PositionClose(ticket,InpDeviation))
         res=false;
     }
   return res;
  }
//+------------------------------------------------------------------+
//| Open a position                                                  |
//+------------------------------------------------------------------+
bool OpenPosition(const string symbol_name,const ENUM_POSITION_TYPE type,const double volume,const string comment)
  {
//--- Calculate values for stop orders
   int    bb=int(HalfSizeBB(0)*InpSLMltp);
   double atrd=ATR(0);
   int    atrp=(atrd!=EMPTY_VALUE ? int(round(atrd*InpTPMltp/Point())) : 0);
   double sl=(InpStopLoss==0   ? 0 : (InpStopLoss<0 ? (bb!=0   ? CorrectStopLoss(type,bb)     : 0) : CorrectStopLoss(type,InpStopLoss)));
   double tp=(InpTakeProfit==0 ? 0 : (InpStopLoss<0 ? (atrp!=0 ? CorrectTakeProfit(type,atrp) : 0) : CorrectTakeProfit(type,InpTakeProfit)));

//--- Remove stop orders on the netting account
   if(netto)
      sl=tp=0;

//--- Get the prices
   MqlTick tick={};
   if(!SymbolInfoTick(symbol_name,tick))
     {
      PrintFormat("%s: Unable to get prices");
      return false;
     }
//--- Check and obtain the normalized lot of the opened position
   double ll=trade.CheckVolume(symbol_name,volume,(type==POSITION_TYPE_BUY ? tick.ask : tick.bid),(ENUM_ORDER_TYPE)type);
   if(ll==0)
     {
      PrintFormat("%s: Error. CheckVolume() returned a zero lot",__FUNCTION__);
      return false;
     }

//--- Check the limit on the maximum volume of open positions on the account
   if(!CheckLotForLimitAccount(type,ll))
     {
      PrintFormat("%s: CheckLotForLimitAccount() returned an error",__FUNCTION__);
      return false;
     }

//--- There may be a situation when a trade order has already been sent, but it has not yet been fully processed,
//--- which may lead to duplication of the opened position.
//--- If the trading environment is invalid, return 'false'
   if(!CheckUncertainStateEnv(symbol_name,InpMagic,ENV_ATTEMPTS,ENV_WAIT_ATTEMPT))
      return false;

//--- It may take some time to receive the correct trading environment
//--- Let's get the prices again
   if(!SymbolInfoTick(symbol_name,tick))
     {
      PrintFormat("%s: Unable to get prices");
      return false;
     }

//--- Return the result of sending a trade request to the server
   return(type==POSITION_TYPE_BUY ? trade.Buy(ll,symbol_name,tick.ask,sl,tp,comment) : trade.Sell(ll,symbol_name,tick.bid,sl,tp,comment));
  }
//+------------------------------------------------------------------+
//| Trading process                                                  |
//+------------------------------------------------------------------+
void TradeProcess(const ENUM_SIGNAL_TYPE signal)
  {
//--- No signal - leave
   if(signal==SIGNAL_TYPE_NONE)
      return;
   
//--- A Buy signal
   if(signal==SIGNAL_TYPE_LONG)
     {
      //--- If there is no open Buy position on the bar
      if(!IsPresentPosOnCurrentBar(POSITION_TYPE_BUY))
        {
         //--- Get the price of the last open Buy position
         double price_last=PositionPriceOpen(LastAddedTicket(POSITION_TYPE_BUY));
         //--- If this is the very first Buy position, or the open price is better than the open price of the previous position,
         //--- send a request to open a Buy position
         if(price_last==0 || price_last>SymbolInfoDouble(Symbol(),SYMBOL_ASK))
           {
            //--- If the position is open, update the list of tickets for open positions
            if(OpenPosition(Symbol(),POSITION_TYPE_BUY,lot,""))
               FillingListTickets(Symbol(),InpMagic);
           }
        }
     }
   
//--- A sell signal
   if(signal==SIGNAL_TYPE_SHORT)
     {
      //--- If there is no open Sell position on this bar
      if(!IsPresentPosOnCurrentBar(POSITION_TYPE_SELL))
        {
         //--- Get the price of the last open Sell position
         double price_last=PositionPriceOpen(LastAddedTicket(POSITION_TYPE_SELL));
         //--- If this is the very first Sell position, or the open price is better than the open price of the previous position,
         //--- send a request to open a Sell position
         if(price_last<SymbolInfoDouble(Symbol(),SYMBOL_ASK))
           {
            //--- If the position is open, update the list of tickets for open positions
            if(OpenPosition(Symbol(),POSITION_TYPE_SELL,lot,""))
               FillingListTickets(Symbol(),InpMagic);
           }
        }
     }
  }
//+------------------------------------------------------------------+
