Initial Commit.

This commit is contained in:
ZhijuCen
2026-06-23 21:47:51 +08:00
commit d17f68e979
5201 changed files with 318318 additions and 0 deletions
@@ -0,0 +1,31 @@
# Group of Functions for Working with Arrays
[Arrays](/en/docs/basis/variables#array_define) are allowed to be maximum four-dimensional. Each dimension is indexed from 0 to dimension_size-1. In a particular case of a one-dimensional array of 50 elements, calling of the first element will appear as array[0], of the last one - as array[49].
| Function | Action |
| --- | --- |
| ArrayBsearch | Returns index of the first found element in the first array dimension |
| ArrayCopy | Copies one array into another |
| ArrayCompare | Returns the result of comparing two arrays of simple types or custom structures without complex objects |
| ArrayFree | Frees up buffer of any dynamic array and sets the size of the zero dimension in 0. |
| ArrayGetAsSeries | Checks direction of array indexing |
| ArrayInitialize | Sets all elements of a numeric array into a single value |
| ArrayFill | Fills an array with the specified value |
| ArrayIsSeries | Checks whether an array is a timeseries |
| ArrayIsDynamic | Checks whether an array is dynamic |
| ArrayMaximum | Search for an element with the maximal value |
| ArrayMinimum | Search for an element with the minimal value |
| ArrayPrint | Prints an array of a simple type or a simple structure into journal |
| ArrayRange | Returns the number of elements in the specified dimension of the array |
| ArrayResize | Sets the new size in the first dimension of the array |
| ArrayInsert | Inserts the specified number of elements from a source array to a receiving one starting from a specified index |
| ArrayRemove | Removes the specified number of elements from the array starting with a specified index |
| ArrayReverse | Reverses the specified number of elements in the array starting with a specified index |
| ArraySetAsSeries | Sets the direction of array indexing |
| ArraySize | Returns the number of elements in the array |
| ArraySort | Sorting of numeric arrays by the first dimension |
| ArraySwap | Swaps the contents of two dynamic arrays of the same type |
| ArrayToFP16 | Copies an array of type float or double into an array of type ushort with the given format |
| ArrayToFP8 | Copies an array of type float or double into an array of type uchar with the given format |
| ArrayFromFP16 | Copies an array of type ushort into an array of float or double type with the given format |
| ArrayFromFP8 | Copies an array of type uchar into an array of float or double type with the given format |
@@ -0,0 +1,176 @@
# ArrayBsearch
Searches for a specified value in a multidimensional numeric array [sorted](/en/docs/array/arraysort) ascending. Search is performed through the elements of the first dimension.
For searching in an array of double type
```
int  ArrayBsearch(
   const double&    array[],   // array for search
   double           value      // what is searched for
   );
```
For searching in an array of float type
```
int  ArrayBsearch(
   const float&    array[],   // array for search
   float           value      // what is searched for
   );
```
For searching in an array of long type
```
int  ArrayBsearch(
   const long&    array[],   // array for search
   long           value      // what is searched for
   );
```
For searching in an array of int type
```
int  ArrayBsearch(
   const int&    array[],   // array for search
   int           value      // what is searched for
   );
```
For searching in an array of short type
```
int  ArrayBsearch(
   const short&    array[],   // array for search
   short           value      // what is searched for
   );
```
For searching in an array of char type
```
int  ArrayBsearch(
   const char&    array[],   // array for search
   char           value      // what is searched for
   );
```
Parameters
array[]
[in]  Numeric array for search.
value
[in]  Value for search.
Return Value
The function returns index of a found element. If the wanted value isn't found, the function returns the index of an element nearest in value.
Note
Binary search processes only sorted arrays. To sort numeric arrays use the [ArraySort()](/en/docs/array/arraysort) function.
Example:
```
#property description "Script based on RSI indicator data displays"
#property description "how often the market was in"
#property description "overbought and oversold areas in the specified time interval."
//--- display the window of input parameters when launching the script
#property script_show_inputs
//--- input parameters
input int                InpMAPeriod=14;                    // Moving average period
input ENUM_APPLIED_PRICE InpAppliedPrice=PRICE_CLOSE;       // Price type
input double             InpOversoldValue=30.0;             // Oversold level
input double             InpOverboughtValue=70.0;           // Overbought level
input datetime           InpDateStart=D'2012.01.01 00:00';  // Analysis start date
input datetime           InpDateFinish=D'2013.01.01 00:00'; // Analysis finish date
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
   double rsi_buff[]; // array of the indicator values
   int    size=0;     // array size
//--- receive RSI indicator handle
   ResetLastError();
   int rsi_handle=iRSI(Symbol(),Period(),InpMAPeriod,InpAppliedPrice);
   if(rsi_handle==INVALID_HANDLE)
     {
      //--- failed to receive the indicator handle
      PrintFormat("Indicator handle receiving error. Error code = %d",GetLastError());
      return;
     }
//--- being in the loop, until the indicator calculates all its values
   while(BarsCalculated(rsi_handle)==-1)
     {
      //--- exit if the indicator has forcedly completed the script's operation
      if(IsStopped())
         return;
      //--- a pause to allow the indicator to calculate all its values
      Sleep(10);
     }
//--- copy the indicator values for a certain period of time
   ResetLastError();
   if(CopyBuffer(rsi_handle,0,InpDateStart,InpDateFinish,rsi_buff)==-1)
     {
      PrintFormat("Failed to copy the indicator values. Error code = %d",GetLastError());
      return;
     }
//--- receive the array size
   size=ArraySize(rsi_buff);
//--- sort out the array
   ArraySort(rsi_buff);
//--- find out the time (in percentage terms) the market was in the oversold area
   double ovs=(double)ArrayBsearch(rsi_buff,InpOversoldValue)*100/(double)size;
//--- find out the time (in percentage terms) the market was in the overbought area
   double ovb=(double)(size-ArrayBsearch(rsi_buff,InpOverboughtValue))*100/(double)size;
//--- form the strings for displaying the data
   string str="From "+TimeToString(InpDateStart,TIME_DATE)+" to "
              +TimeToString(InpDateFinish,TIME_DATE)+" the market was:";
   string str_ovb="in overbought area "+DoubleToString(ovb,2)+"% of time";
   string str_ovs="in oversold area "+DoubleToString(ovs,2)+"% of time";
//--- display the data on the chart
   CreateLabel("top",5,60,str,clrDodgerBlue);
   CreateLabel("overbought",5,35,str_ovb,clrDodgerBlue);
   CreateLabel("oversold",5,10,str_ovs,clrDodgerBlue);
//--- redraw the chart
   ChartRedraw(0);
//--- pause
   Sleep(10000);
  }
//+------------------------------------------------------------------+
//| Display comment in the bottom left corner of the chart           |
//+------------------------------------------------------------------+
void CreateLabel(const string name,const int x,const int y,
                 const string str,const color clr)
  {
//--- create the label
   ObjectCreate(0,name,OBJ_LABEL,0,0,0);
//--- bind the label to the bottom left corner
   ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER);
//--- change position of the anchor point
   ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
//--- distance from the anchor point in X-direction
   ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
//--- distance from the anchor point in Y-direction
   ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
//--- label text
   ObjectSetString(0,name,OBJPROP_TEXT,str);
//--- text color
   ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
//--- text size
   ObjectSetInteger(0,name,OBJPROP_FONTSIZE,12);
  }
```
@@ -0,0 +1,241 @@
# ArrayCopy
It copies an array into another one.
```
int  ArrayCopy(
   void&        dst_array[],         // destination array
   const void&  src_array[],         // source array
   int          dst_start=0,         // index starting from which write into destination array
   int          src_start=0,         // first index of a source array
   int          count=WHOLE_ARRAY    // number of elements
   );
```
Parameters
dst_array[]
[out]  Destination array
src_array[]
[in]  Source array
dst_start=0
[in]  Starting index from the destination array. By default, start index is 0.
src_start=0
[in]  Starting index for the source array. By default, start index is 0.
count=WHOLE_ARRAY
[in]  Number of elements that should be copied. By default, the whole array is copied (count=[WHOLE_ARRAY](/en/docs/constants/namedconstants/otherconstants)).
Return Value
It returns the number of copied elements.
Note
If count<0 or count>src_size-src_start, all the remaining array part is copied. Arrays are copied from left to right. For series arrays, the starting position is correctly defined adjusted for copying from left to right.
If arrays are of different types, during copying it tries to transform each element of a source array into the type of the destination array. A string array can be copied into a string array only. Array of [classes and structures](/en/docs/basis/types/classes) containing objects that require initialization aren't copied. An array of structures can be copied into an array of the same type only.
For dynamic arrays with indexing as in [timeseries](/en/docs/series/bufferdirection), the size of a destination array is automatically increased to the amount of copied data (if the latter exceeds the array size). The destination array size is not decreased automatically.
Example:
```
#property description "The indicator highlights the candlesticks that are local"
#property description "highs and lows. Interval length for finding"
#property description "extreme values should be found using an input parameters."
//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots   1
//---- plot
#property indicator_label1  "Extremums"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrLightSteelBlue,clrRed,clrBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- predefined constant
#define INDICATOR_EMPTY_VALUE 0.0
//--- input parameters
input int InpNum=4; // Half-interval length
//--- indicator buffers
double ExtOpen[];
double ExtHigh[];
double ExtLow[];
double ExtClose[];
double ExtColor[];
//--- global variables
int    ExtStart=0; // index of the first candlestick that is not an extremum
int    ExtCount=0; // number of non-extremums in the interval
//+------------------------------------------------------------------+
//| Filling out non-extremum candlesticks                            |
//+------------------------------------------------------------------+
void FillCandles(const double &open[],const double &high[],
                 const double &low[],const double &close[])
  {
//--- fill out the candlesticks
   ArrayCopy(ExtOpen,open,ExtStart,ExtStart,ExtCount);
   ArrayCopy(ExtHigh,high,ExtStart,ExtStart,ExtCount);
   ArrayCopy(ExtLow,low,ExtStart,ExtStart,ExtCount);
   ArrayCopy(ExtClose,close,ExtStart,ExtStart,ExtCount);
  }
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtOpen);
   SetIndexBuffer(1,ExtHigh);
   SetIndexBuffer(2,ExtLow);
   SetIndexBuffer(3,ExtClose);
   SetIndexBuffer(4,ExtColor,INDICATOR_COLOR_INDEX);
//--- specify the value, which is not displayed
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,INDICATOR_EMPTY_VALUE);
//--- specify the names of indicator buffers for displaying in the data window
   PlotIndexSetString(0,PLOT_LABEL,"Open;High;Low;Close");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- set straight indexing in time series
   ArraySetAsSeries(open,false);
   ArraySetAsSeries(high,false);
   ArraySetAsSeries(low,false);
   ArraySetAsSeries(close,false);
//--- variable of the bar calculation start
   int start=prev_calculated;
//--- calculation is not performed for the first InpNum*2 bars
   if(start==0)
     {
      start+=InpNum*2;
      ExtStart=0;
      ExtCount=0;
     }
//--- if the bar has just formed, check the next potential extremum
   if(rates_total-start==1)
      start--;
//--- bar index to be checked for the extremum
   int ext;
//--- indicator value calculation loop
   for(int i=start;i<rates_total-1;i++)
     {
      //--- initially on i bar without drawing
      ExtOpen[i]=0;
      ExtHigh[i]=0;
      ExtLow[i]=0;
      ExtClose[i]=0;
      //--- extremum index for check
      ext=i-InpNum;
      //--- check for the local maximum
      if(IsMax(high,ext))
        {
         //--- highlight an extremum candlestick
         ExtOpen[ext]=open[ext];
         ExtHigh[ext]=high[ext];
         ExtLow[ext]=low[ext];
         ExtClose[ext]=close[ext];
         ExtColor[ext]=1;
         //--- highlight other candles up to the extremum with a neutral color
         FillCandles(open,high,low,close);
         //--- change the variable colors
         ExtStart=ext+1;
         ExtCount=0;
         //--- pass to the next iteration
         continue;
        }
      //--- check for the local minimum
      if(IsMin(low,ext))
        {
         //--- highlight an extremum candlestick
         ExtOpen[ext]=open[ext];
         ExtHigh[ext]=high[ext];
         ExtLow[ext]=low[ext];
         ExtClose[ext]=close[ext];
         ExtColor[ext]=2;
         //--- highlight other candles up to the extremum with a neutral color
         FillCandles(open,high,low,close);
         //--- change variable values
         ExtStart=ext+1;
         ExtCount=0;
         //--- pass to the next iteration
         continue;
        }
      //--- increase the number of non-extremums at the interval
      ExtCount++;
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Check if the current array element is a local high               |
//+------------------------------------------------------------------+
bool IsMax(const double &price[],const int ind)
  {
//--- interval start variable
   int i=ind-InpNum;
//--- interval end period
   int finish=ind+InpNum+1;
//--- check for the first half of the interval
   for(;i<ind;i++)
     {
      if(price[ind]<=price[i])
         return(false);
     }
//--- check for the second half of the interval
   for(i=ind+1;i<finish;i++)
     {
      if(price[ind]<=price[i])
         return(false);
     }
//--- this is an extremum
   return(true);
  }
//+------------------------------------------------------------------+
//| Check if the current array element is a local low                |
//+------------------------------------------------------------------+
bool IsMin(const double &price[],const int ind)
  {
//--- interval start variable
   int i=ind-InpNum;
//--- interval end variable
   int finish=ind+InpNum+1;
//--- check for the first half of the interval
   for(;i<ind;i++)
     {
      if(price[ind]>=price[i])
         return(false);
     }
//--- check for the second half of the interval
   for(i=ind+1;i<finish;i++)
     {
      if(price[ind]>=price[i])
         return(false);
     }
//--- this is an extremum
   return(true);
  }
```
@@ -0,0 +1,149 @@
# ArrayCompare
The function returns the result of comparing two arrays of the same type. It can be used to compare arrays of [simple types](/en/docs/basis/types#base_types) or custom structures without [complex objects](/en/docs/basis/types#complex_types), that is the custom structures that do not contain [strings](/en/docs/basis/types/stringconst), [dynamic arrays](/en/docs/basis/types/dynamic_array), classes and other structures with complex objects.
```
int  ArrayCompare(
   const void&  array1[],            // first array
   const void&  array2[],            // second array
   int          start1=0,            // initial offset in the first array
   int          start2=0,            // initial offset in the second array
   int          count=WHOLE_ARRAY    // number of elements for comparison
   );
```
Parameters
array1[]
[in]  First array.
array2[]
[in]  Second array.
start1=0
[in]  The element's initial index in the first array, from which comparison starts. The default start index - 0.
start2=0
[in]  The element's initial index in the second array, from which comparison starts. The default start index - 0.
count=WHOLE_ARRAY
[in]  Number of elements to be compared. All elements of both arrays participate in comparison by default (count=[WHOLE_ARRAY](/en/docs/constants/namedconstants/otherconstants)).
Return Value
- -1, if array1[] less than array2[]
- 0, if array1[] equal to array2[]
- 1, if array1[] more than array2[]
- -2, if an error occurs due to incompatibility of the types of compared arrays or if start1, start2 or count values lead to falling outside the array.
Note
The function will not return 0 (the arrays will not be considered equal) if the arrays differ in size and count=WHOLE_ARRAY for the case when one array is a faithful subset of another one. In this case, the result of comparing the sizes of that arrays will be returned: -1, if the size of array1[] is less than the size of array2[] , otherwise 1.
Example:
```
//--- global variables 
double   ExtArrayFirst[];
double   ExtArraySecond[];
 
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- set the array sizes
   if(ArrayResize(ExtArrayFirst,10)!=10)
     {
      Print("ArrayResize() failed for ExtArrayFirst. Error code: ",GetLastError());
      return;
     }
   if(ArrayResize(ExtArraySecond,10)!=10)
     {
      Print("ArrayResize() failed for ExtArraySecond. Error code: ",GetLastError());
      return;
     }
     
//--- fill the arrays with the values of i and j indices in a loop
   int total=ArraySize(ExtArrayFirst);
   for(int i=0, j=total-1; i<total; i++,j--)
     {
      //--- fill the ExtArrayFirst array from left to right
      //--- fill the ExtArraySecond array from right to left
      ExtArrayFirst[i]=i;
      ExtArraySecond[i]=j;
     }
//--- compare the arrays and print the result in the log
   ArrayComparePrint(ExtArrayFirst,ExtArraySecond);
   /*
   Result:
   ExtArrayFirst:
   0.00000 1.00000 2.00000 3.00000 4.00000 5.00000 6.00000 7.00000 8.00000 9.00000
   ExtArraySecond:
   9.00000 8.00000 7.00000 6.00000 5.00000 4.00000 3.00000 2.00000 1.00000 0.00000
   Result ArrayCompare(): ExtArrayFirst is smaller than ExtArraySecond (result = -1)
   */
   
//--- now let's flip the arrays
//--- fill the arrays with the values of i and j indices in a loop
   for(int i=0, j=total-1; i<total; i++,j--)
     {
      //--- fill the ExtArrayFirst array from right to left
      //--- fill the ExtArraySecond array from left to right
      ExtArrayFirst[i]=j;
      ExtArraySecond[i]=i;
     }
//--- compare the arrays and print the result in the log
   ArrayComparePrint(ExtArrayFirst,ExtArraySecond);
   /*
   Result:
   ExtArrayFirst:
   9.00000 8.00000 7.00000 6.00000 5.00000 4.00000 3.00000 2.00000 1.00000 0.00000
   ExtArraySecond:
   0.00000 1.00000 2.00000 3.00000 4.00000 5.00000 6.00000 7.00000 8.00000 9.00000
   Result ArrayCompare(): ExtArrayFirst is larger than ExtArraySecond (result = 1)
   */
   
//--- now let's fill the arrays in one direction
//--- fill the arrays with the values of i index in a loop
   for(int i=0; i<total; i++)
     {
      //--- fill both arrays from left to right
      ExtArrayFirst[i]=i;
      ExtArraySecond[i]=i;
     }
//--- compare the arrays and print the result in the log
   ArrayComparePrint(ExtArrayFirst,ExtArraySecond);
   /*
   Result:
   ExtArrayFirst:
   0.00000 1.00000 2.00000 3.00000 4.00000 5.00000 6.00000 7.00000 8.00000 9.00000
   ExtArraySecond:
   0.00000 1.00000 2.00000 3.00000 4.00000 5.00000 6.00000 7.00000 8.00000 9.00000
   Result ArrayCompare(): ExtArrayFirst and ExtArraySecond are equal (result = 0)
   */
  }
//+------------------------------------------------------------------+
//| Compare and display the result                                   |
//+------------------------------------------------------------------+
void ArrayComparePrint(const double &array1[], const double &array2[])
  {
   //--- print the header and contents of the arrays
   Print("ExtArrayFirst:");
   ArrayPrint(array1);
   Print("ExtArraySecond:");
   ArrayPrint(array2);
   //--- compare the arrays and print the comparison result
   int    res=ArrayCompare(array1,array2);
   string res_str=(res>0 ? "ExtArrayFirst is larger than ExtArraySecond" : res<0 ? "ExtArrayFirst is smaller than ExtArraySecond" : "ExtArrayFirst and ExtArraySecond are equal");
   PrintFormat("Result ArrayCompare(): %s (result = %d)\n",res_str,res);
  }
//+------------------------------------------------------------------+
```
@@ -0,0 +1,406 @@
# ArrayFree
It frees up a buffer of any dynamic array and sets the size of the zero dimension to 0.
```
void  ArrayFree(
   void&  array[]      // array
   );
```
Parameters
array[]
[in]  Dynamic array.
Return Value
No return value.
Note
The need for using ArrayFree() function may not appear too often considering that all used memory is freed at once and main work with the arrays comprises the access to the indicator buffers. The sizes of the buffers are automatically managed by the terminal's executive subsystem.
In case it is necessary to manually manage the memory in complex dynamic environment of the application, ArrayFree() function allows users to free the memory occupied by the already unnecessary dynamic array explicitly and immediately.
Example:
```
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Controls\Label.mqh>
#include <Controls\ComboBox.mqh>
//--- predefined constants
#define X_START 0
#define Y_START 0
#define X_SIZE 280
#define Y_SIZE 300
//+------------------------------------------------------------------+
//| Dialog class for working with memory                             |
//+------------------------------------------------------------------+
class CMemoryControl : public CAppDialog
  {
private:
   //--- array size
   int               m_arr_size;
   //--- arrays
   char              m_arr_char[];
   int               m_arr_int[];
   float             m_arr_float[];
   double            m_arr_double[];
   long              m_arr_long[];
   //--- labels
   CLabel            m_lbl_memory_physical;
   CLabel            m_lbl_memory_total;
   CLabel            m_lbl_memory_available;
   CLabel            m_lbl_memory_used;
   CLabel            m_lbl_array_size;
   CLabel            m_lbl_array_type;
   CLabel            m_lbl_error;
   CLabel            m_lbl_change_type;
   CLabel            m_lbl_add_size;
   //--- buttons
   CButton           m_button_add;
   CButton           m_button_free;
   //--- combo boxes
   CComboBox         m_combo_box_step;
   CComboBox         m_combo_box_type;
   //--- current value of the array type from the combo box
   int               m_combo_box_type_value;
 
public:
                     CMemoryControl(void);
                    ~CMemoryControl(void);
   //--- class object creation method
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- handler of chart events
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
 
protected:
   //--- create labels
   bool              CreateLabel(CLabel &lbl,const string name,const int x,const int y,const string str,const int font_size,const int clr);
   //--- create control elements
   bool              CreateButton(CButton &button,const string name,const int x,const int y,const string str,const int font_size,const int clr);
   bool              CreateComboBoxStep(void);
   bool              CreateComboBoxType(void);
   //--- event handlers
   void              OnClickButtonAdd(void);
   void              OnClickButtonFree(void);
   void              OnChangeComboBoxType(void);
   //--- methods for working with the current array
   void              CurrentArrayFree(void);
   bool              CurrentArrayAdd(void);
  };
//+------------------------------------------------------------------+
//| Free memory of the current array                                 |
//+------------------------------------------------------------------+
void CMemoryControl::CurrentArrayFree(void)
  {
//--- reset array size
   m_arr_size=0;
//--- free the array
   if(m_combo_box_type_value==0)
      ArrayFree(m_arr_char);
   if(m_combo_box_type_value==1)
      ArrayFree(m_arr_int);
   if(m_combo_box_type_value==2)
      ArrayFree(m_arr_float);
   if(m_combo_box_type_value==3)
      ArrayFree(m_arr_double);
   if(m_combo_box_type_value==4)
      ArrayFree(m_arr_long);
  }  
//+------------------------------------------------------------------+
//| Attempt to add memory for the current array                      |
//+------------------------------------------------------------------+
bool CMemoryControl::CurrentArrayAdd(void)
  {
//--- exit if the size of the used memory exceeds the size of the physical memory
   if(TerminalInfoInteger(TERMINAL_MEMORY_PHYSICAL)/TerminalInfoInteger(TERMINAL_MEMORY_USED)<2)
      return(false);
//--- attempt to allocate memory according to the current type
   if(m_combo_box_type_value==0 && ArrayResize(m_arr_char,m_arr_size)==-1)
      return(false);
   if(m_combo_box_type_value==1 && ArrayResize(m_arr_int,m_arr_size)==-1)
      return(false);
   if(m_combo_box_type_value==2 && ArrayResize(m_arr_float,m_arr_size)==-1)
      return(false);
   if(m_combo_box_type_value==3 && ArrayResize(m_arr_double,m_arr_size)==-1)
      return(false);
   if(m_combo_box_type_value==4 && ArrayResize(m_arr_long,m_arr_size)==-1)
      return(false);
//--- memory allocated
   return(true);
  }  
//+------------------------------------------------------------------+
//| Handling events                                                  |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CMemoryControl)
ON_EVENT(ON_CLICK,m_button_add,OnClickButtonAdd)
ON_EVENT(ON_CLICK,m_button_free,OnClickButtonFree)
ON_EVENT(ON_CHANGE,m_combo_box_type,OnChangeComboBoxType)
EVENT_MAP_END(CAppDialog)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMemoryControl::CMemoryControl(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CMemoryControl::~CMemoryControl(void)
  {
  }
//+------------------------------------------------------------------+
//| Class object creation method                                     |
//+------------------------------------------------------------------+
bool CMemoryControl::Create(const long chart,const string name,const int subwin,
                            const int x1,const int y1,const int x2,const int y2)
  {
//--- create base class object
   if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- prepare strings for labels
   string str_physical="Memory physical = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_PHYSICAL)+" Mb";
   string str_total="Memory total = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_TOTAL)+" Mb";
   string str_available="Memory available = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE)+" Mb";
   string str_used="Memory used = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_USED)+" Mb";
//--- create labels
   if(!CreateLabel(m_lbl_memory_physical,"physical_label",X_START+10,Y_START+5,str_physical,12,clrBlack))
      return(false);
   if(!CreateLabel(m_lbl_memory_total,"total_label",X_START+10,Y_START+30,str_total,12,clrBlack))
      return(false);
   if(!CreateLabel(m_lbl_memory_available,"available_label",X_START+10,Y_START+55,str_available,12,clrBlack))
      return(false);
   if(!CreateLabel(m_lbl_memory_used,"used_label",X_START+10,Y_START+80,str_used,12,clrBlack))
      return(false);
   if(!CreateLabel(m_lbl_array_type,"type_label",X_START+10,Y_START+105,"Array type = double",12,clrBlack))
      return(false);
   if(!CreateLabel(m_lbl_array_size,"size_label",X_START+10,Y_START+130,"Array size = 0",12,clrBlack))
      return(false);
   if(!CreateLabel(m_lbl_error,"error_label",X_START+10,Y_START+155,"",12,clrRed))
      return(false);
   if(!CreateLabel(m_lbl_change_type,"change_type_label",X_START+10,Y_START+185,"Change type",10,clrBlack))
      return(false);
   if(!CreateLabel(m_lbl_add_size,"add_size_label",X_START+10,Y_START+210,"Add to array",10,clrBlack))
      return(false);
//--- create control elements
   if(!CreateButton(m_button_add,"add_button",X_START+15,Y_START+245,"Add",12,clrBlue))
      return(false);
   if(!CreateButton(m_button_free,"free_button",X_START+75,Y_START+245,"Free",12,clrBlue))
      return(false);
   if(!CreateComboBoxType())
      return(false);
   if(!CreateComboBoxStep())
      return(false);
//--- initialize the variable
   m_arr_size=0;
//--- successful execution
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the button                                                |
//+------------------------------------------------------------------+
bool CMemoryControl::CreateButton(CButton &button,const string name,const int x,
                                  const int y,const string str,const int font_size,
                                  const int clr)
  {
//--- create the button
   if(!button.Create(m_chart_id,name,m_subwin,x,y,x+50,y+20))
      return(false);
//--- text
   if(!button.Text(str))
      return(false);
//--- font size
   if(!button.FontSize(font_size))
      return(false);
//--- label color
   if(!button.Color(clr))
      return(false);
//--- add the button to the control elements
   if(!Add(button))
      return(false);
//--- successful execution
   return(true);
  }
//+------------------------------------------------------------------+
//| Create a combo box for the array size                            |
//+------------------------------------------------------------------+
bool CMemoryControl::CreateComboBoxStep(void)
  {
//--- create the combo box
   if(!m_combo_box_step.Create(m_chart_id,"step_combobox",m_subwin,X_START+100,Y_START+185,X_START+200,Y_START+205))
      return(false);
//--- add elements to the combo box
   if(!m_combo_box_step.ItemAdd("100 000",100000))
      return(false);
   if(!m_combo_box_step.ItemAdd("1 000 000",1000000))
      return(false);
   if(!m_combo_box_step.ItemAdd("10 000 000",10000000))
      return(false);
   if(!m_combo_box_step.ItemAdd("100 000 000",100000000))
      return(false);
//--- set the current combo box element
   if(!m_combo_box_step.SelectByValue(1000000))
      return(false);
//--- add the combo box to control elements
   if(!Add(m_combo_box_step))
      return(false);
//--- successful execution
   return(true);
  }
//+------------------------------------------------------------------+
//| Create a combo box for the array type                            |
//+------------------------------------------------------------------+
bool CMemoryControl::CreateComboBoxType(void)
  {
//--- create the combo box
   if(!m_combo_box_type.Create(m_chart_id,"type_combobox",m_subwin,X_START+100,Y_START+210,X_START+200,Y_START+230))
      return(false);
//--- add elements to the combo box
   if(!m_combo_box_type.ItemAdd("char",0))
      return(false);
   if(!m_combo_box_type.ItemAdd("int",1))
      return(false);
   if(!m_combo_box_type.ItemAdd("float",2))
      return(false);
   if(!m_combo_box_type.ItemAdd("double",3))
      return(false);
   if(!m_combo_box_type.ItemAdd("long",4))
      return(false);
//--- set the current combo box element
   if(!m_combo_box_type.SelectByValue(3))
      return(false);
//--- store the current combo box element
   m_combo_box_type_value=3;
//--- add the combo box to control elements
   if(!Add(m_combo_box_type))
      return(false);
//--- successful execution
   return(true);
  }
//+------------------------------------------------------------------+
//| Create a label                                                   |
//+------------------------------------------------------------------+
bool CMemoryControl::CreateLabel(CLabel &lbl,const string name,const int x,
                                 const int y,const string str,const int font_size,
                                 const int clr)
  {
//--- create a label
   if(!lbl.Create(m_chart_id,name,m_subwin,x,y,0,0))
      return(false);
//--- text
   if(!lbl.Text(str))
      return(false);
//--- font size
   if(!lbl.FontSize(font_size))
      return(false);
//--- color
   if(!lbl.Color(clr))
      return(false);
//--- add the label to control elements
   if(!Add(lbl))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of clicking "Add" button event                           |
//+------------------------------------------------------------------+
void CMemoryControl::OnClickButtonAdd(void)
  {
//--- increase the array size
   m_arr_size+=(int)m_combo_box_step.Value();
//--- attempt to allocate memory for the current array
   if(CurrentArrayAdd())
     {
      //--- memory allocated, display the current status on the screen
      m_lbl_memory_available.Text("Memory available = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE)+" Mb");
      m_lbl_memory_used.Text("Memory used = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_USED)+" Mb");
      m_lbl_array_size.Text("Array size = "+IntegerToString(m_arr_size));
      m_lbl_error.Text("");
     }
   else
     {
      //--- failed to allocate memory, display the error message
      m_lbl_error.Text("Array is too large, error!");
      //--- return the previous array size
      m_arr_size-=(int)m_combo_box_step.Value();
     }
  }
//+------------------------------------------------------------------+
//| Handler of clicking "Free" button event                          |
//+------------------------------------------------------------------+
void CMemoryControl::OnClickButtonFree(void)
  {
//--- free the memory of the current array
   CurrentArrayFree();
//--- display the current status on the screen
   m_lbl_memory_available.Text("Memory available = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE)+" Mb");
   m_lbl_memory_used.Text("Memory used = "+(string)TerminalInfoInteger(TERMINAL_MEMORY_USED)+" Mb");
   m_lbl_array_size.Text("Array size = 0");
   m_lbl_error.Text("");
  }
//+------------------------------------------------------------------+
//| Handler of the combo box change event                            |
//+------------------------------------------------------------------+
void CMemoryControl::OnChangeComboBoxType(void)
  {
//--- check if the array's type has changed
   if(m_combo_box_type.Value()!=m_combo_box_type_value)
     {
      //--- free the memory of the current array
      OnClickButtonFree();
      //--- work with another array type
      m_combo_box_type_value=(int)m_combo_box_type.Value();
      //--- display the new array type on the screen
      if(m_combo_box_type_value==0)
         m_lbl_array_type.Text("Array type = char");
      if(m_combo_box_type_value==1)
         m_lbl_array_type.Text("Array type = int");
      if(m_combo_box_type_value==2)
         m_lbl_array_type.Text("Array type = float");
      if(m_combo_box_type_value==3)
         m_lbl_array_type.Text("Array type = double");
      if(m_combo_box_type_value==4)
         m_lbl_array_type.Text("Array type = long");
     }
  }
//--- CMemoryControl class object
CMemoryControl ExtDialog;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- create the dialog
   if(!ExtDialog.Create(0,"MemoryControl",0,X_START,Y_START,X_SIZE,Y_SIZE))
      return(INIT_FAILED);
//--- launch
   ExtDialog.Run();
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   ExtDialog.Destroy(reason);
  }
//+------------------------------------------------------------------+
//| Expert chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   ExtDialog.ChartEvent(id,lparam,dparam,sparam);
  }
```
@@ -0,0 +1,125 @@
# ArrayGetAsSeries
It checks direction of an array index.
```
bool  ArrayGetAsSeries(
   const void&  array[]    // array for checking
   );
```
Parameters
array
[in]  Checked array.
Return Value
Returns [true](/en/docs/basis/types/integer/boolconst), if the specified array has the AS_SERIES flag set, i.e. access to the array is performed back to front as in timeseries. A [timeseries](/en/docs/series) differs from a usual array in that the indexing of timeseries elements is performed from its end to beginning (from the newest data to old).
Note
To check whether an array belongs to timeseries, use the [ArrayIsSeries()](/en/docs/array/arrayisseries) function. Arrays of price data passed as input parameters into the [OnCalculate()](/en/docs/basis/function/events#oncalculate2) function do not obligatorily have the indexing direction the same as in timeseries. The necessary indexing direction can be set using the [ArraySetAsSeries()](/en/docs/array/arraysetasseries) function.
Example:
```
#property description "Indicator calculates absolute values of the difference between"
#property description "Open and Close or High and Low prices displaying them in a separate subwindow"
#property description "as a histrogram."
//--- indicator settings
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1
//---- plot
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_style1  STYLE_SOLID
#property indicator_width1  3
//--- input parameters
input bool InpAsSeries=true; // Indexing direction in the indicator buffer
input bool InpPrices=true;   // Calculation prices (true - Open,Close; false - High,Low)
//--- indicator buffer
double ExtBuffer[];
//+------------------------------------------------------------------+
//| Calculating indicator values                                     |
//+------------------------------------------------------------------+
void CandleSizeOnBuffer(const int rates_total,const int prev_calculated,
                        const double &first[],const double &second[],double &buffer[])
  {
//--- start variable for calculation of bars
   int start=prev_calculated;
//--- work at the last bar if the indicator values have already been calculated at the previous tick
   if(prev_calculated>0)
      start--;
//--- define indexing direction in arrays
   bool as_series_first=ArrayGetAsSeries(first);
   bool as_series_second=ArrayGetAsSeries(second);
   bool as_series_buffer=ArrayGetAsSeries(buffer);
//--- replace indexing direction with direct one if necessary
   if(as_series_first)
      ArraySetAsSeries(first,false);
   if(as_series_second)
      ArraySetAsSeries(second,false);
   if(as_series_buffer)
      ArraySetAsSeries(buffer,false);
//--- calculate indicator values
   for(int i=start;i<rates_total;i++)
      buffer[i]=MathAbs(first[i]-second[i]);
  }
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- bind indicator buffers
   SetIndexBuffer(0,ExtBuffer);
//--- set indexing element in the indicator buffer
   ArraySetAsSeries(ExtBuffer,InpAsSeries);
//--- check for what prices the indicator is calculated
   if(InpPrices)
     {
      //--- Open and Close prices
      PlotIndexSetString(0,PLOT_LABEL,"BodySize");
      //--- set the indicator color
      PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrOrange);
     }
   else
     {
      //--- High and Low prices
      PlotIndexSetString(0,PLOT_LABEL,"ShadowSize");
      //--- set the indicator color
      PlotIndexSetInteger(0,PLOT_LINE_COLOR,clrDodgerBlue);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- calculate the indicator according to the flag value
   if(InpPrices)
      CandleSizeOnBuffer(rates_total,prev_calculated,open,close,ExtBuffer);
   else
      CandleSizeOnBuffer(rates_total,prev_calculated,high,low,ExtBuffer);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
```
See also
[Access to timeseries](/en/docs/series), [ArraySetAsSeries](/en/docs/array/arraysetasseries)
@@ -0,0 +1,125 @@
# ArrayInitialize
The function initializes a numeric array by a preset value.
For initialization of an array of char type
```
int  ArrayInitialize(
   char    array[],     // initialized array
   char    value        // value that will be set
   );
```
For initialization of an array of short type
```
int  ArrayInitialize(
   short   array[],     // initialized array
   short   value        // value that will be set
   );
```
For initialization of an array of int type
```
int  ArrayInitialize(
   int     array[],     // initialized array
   int     value        // value that will be set
   );
```
For initialization of an array of long type
```
int  ArrayInitialize(
   long    array[],     // initialized array
   long    value        // value that will be set
   );
```
For initialization of an array of float type
```
int  ArrayInitialize(
   float   array[],     // initialized array
   float   value        // value that will be set
   );
```
For initialization of an array of double type
```
int  ArrayInitialize(
   double  array[],     // initialized array
   double  value        // value that will be set
   );
```
For initialization of an array of bool type
```
int  ArrayInitialize(
   bool    array[],     // initialized array
   bool    value        // value that will be set
   );
```
For initialization of an array of uint type
```
int  ArrayInitialize(
   uint    array[],     // initialized array
   uint    value        // value that will be set
   );
```
Parameters
array[]
[out]  Numeric array that should be initialized.
value
[in]  New value that should be set to all array elements.
Return Value
Number of initialized elements.
Note
The [ArrayResize()](/en/docs/array/arrayresize) function allows to set size of an array with a reserve for further expansion without the physical relocation of memory. It is implemented for the better performance, because the operations of memory relocation are reasonably slow.
Initialization of the array using ArrayInitialize(array, init_val) doesn't mean the initialization with the same value of reserve elements allocated for this array. At further expanding of the array using the ArrayResize() function, the elements will be added at the end of the array, their values will be undefined and in most cases will not be equal to init_value.
Example:
```
void OnStart()
  {
//--- dynamic array
   double array[];
//--- let's set the array size for 100 elements and reserve a buffer for another 10 elements
   ArrayResize(array,100,10);
//--- initialize the array elements with EMPTY_VALUE=DBL_MAX value
   ArrayInitialize(array,EMPTY_VALUE);
   Print("Values of 10 last elements after initialization");
   for(int i=90;i<100;i++) printf("array[%d] = %G",i,array[i]);
//--- expand the array by 5 elements
   ArrayResize(array,105);
   Print("Values of 10 last elements after ArrayResize(array,105)");
//--- values of 5 last elements are obtained from reserve buffer
   for(int i=95;i<105;i++) printf("array[%d] = %G",i,array[i]);
  }
```
@@ -0,0 +1,60 @@
# ArrayFill
The function fills an array with the specified value.
```
void  ArrayFill(
   void&  array[],      // array
   int    start,         // starting index
   int    count,         // number of elements to fill
   void   value          // value
   );
```
Parameters
array[]
[out]  Array of simple type ([char](/en/docs/basis/types/integer/integertypes), [uchar](/en/docs/basis/types/integer/integertypes), [short](/en/docs/basis/types/integer/integertypes), [ushort](/en/docs/basis/types/integer/integertypes), [int](/en/docs/basis/types/integer/integertypes), [uint](/en/docs/basis/types/integer/integertypes), [long](/en/docs/basis/types/integer/integertypes), [ulong](/en/docs/basis/types/integer/integertypes), [bool](/en/docs/basis/types/integer/boolconst), [color](/en/docs/basis/types/integer/color), [datetime](/en/docs/basis/types/integer/datetime), [float](/en/docs/basis/types/double), [double](/en/docs/basis/types/double)).
start
[in]  Starting index. In such a case, specified [AS_SERIES flag](/en/docs/array/arraysetasseries) is ignored.
count
[in]  Number of elements to fill.
value
[in]  Value to fill the array with.
Return Value
No return value.
Note
When ArrayFill() function is called, normal indexation direction (from left to right) is always implied. It means that the change of the order of access to the array elements using [ArraySetAsSeries()](/en/docs/array/arraysetasseries) function is ignored.
A multidimensional array is shown as one-dimensional when processed by ArrayFill() function. For example, array[2][4] is processed as array[8]. Therefore, you may specify the initial element's index to be equal to 5 when working with this array. Thus, the call of ArrayFill(array, 5, 2, 3.14) for array[2][4] fills array[1][1] and array[1][2] elements with 3.14.
Example:
```
void OnStart()
  {
//--- declare dynamic array
   int a[];
//--- set size
   ArrayResize(a,10);
//--- fill first 5 elements with 123
   ArrayFill(a,0,5,123);
//--- fill next 5 elements with 456
   ArrayFill(a,5,5,456);
//--- show values
   for(int i=0;i<ArraySize(a);i++) printf("a[%d] = %d",i,a[i]);
  }
```
@@ -0,0 +1,95 @@
# ArrayIsDynamic
The function checks whether an array is dynamic.
```
bool  ArrayIsDynamic(
   const void&  array[]    // checked array
   );
```
Parameters
array[]
[in]  Checked array.
Return Value
It returns true if the selected array is [dynamic](/en/docs/basis/types/dynamic_array), otherwise it returns false.
Example:
```
#property description "This indicator does not calculate values. It makes a single attempt to"
#property description "apply the call of ArrayFree() function to three arrays: dynamic one, static one and"
#property description "an indicator buffer. Results are shown in Experts journal."
//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- global variables
double ExtDynamic[];   // dynamic array
double ExtStatic[100]; // static array
bool   ExtFlag=true;   // flag
double ExtBuff[];      // indicator buffer
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- allocate memory for the array
   ArrayResize(ExtDynamic,100);
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtBuff);
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//--- perform a single analysis
   if(ExtFlag)
     {
      //--- attempt to free memory for arrays
      //--- 1. Dynamic array
      Print("+============================+");
      Print("1. Check dynamic array:");
      Print("Size before memory is freed = ",ArraySize(ExtDynamic));
      Print("Is this a dynamic array = ",ArrayIsDynamic(ExtDynamic) ? "Yes" : "No");
      //--- attempt to free array memory
      ArrayFree(ExtDynamic);
      Print("Size after memory is freed = ",ArraySize(ExtDynamic));
      //--- 2. Static array
      Print("2. Check static array:");
      Print("Size before memory is freed = ",ArraySize(ExtStatic));
      Print("Is this a dynamic array = ",ArrayIsDynamic(ExtStatic) ? "Yes" : "No");
      //--- attempt to free array memory
      ArrayFree(ExtStatic);
      Print("Size after memory is freed = ",ArraySize(ExtStatic));
      //--- 3. Indicator buffer
      Print("3. Check indicator buffer:");
      Print("Size before memory is freed = ",ArraySize(ExtBuff));
      Print("Is this a dynamic array = ",ArrayIsDynamic(ExtBuff) ? "Yes" : "No");
      //--- attempt to free array memory
      ArrayFree(ExtBuff);
      Print("Size after memory is freed = ",ArraySize(ExtBuff));
      //--- change the flag value
      ExtFlag=false;
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
```
See also
[Access to timeseries and indicators](/en/docs/series)
@@ -0,0 +1,72 @@
# ArrayIsSeries
The function checks whether an array is a timeseries.
```
bool  ArrayIsSeries(
   const void&  array[]    // checked array
   );
```
Parameters
array[]
[in]  Checked array.
Return Value
It returns true, if a checked array is an array timeseries, otherwise it returns false. Arrays passed as a parameter to the [OnCalculate()](/en/docs/basis/function/events#oncalculate2) function must be checked for the order of accessing the array elements by [ArrayGetAsSeries()](/en/docs/array/arraygetasseries).
Example:
```
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//---- plot Label1
#property indicator_label1  "Label1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double         Label1Buffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,Label1Buffer,INDICATOR_DATA);
//---
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(ArrayIsSeries(open))
      Print("open[] is timeseries");
   else
      Print("open[] is not timeseries!!!");
//--- return value of prev_calculated for next call
   return(rates_total);
  }
```
See also
[Access to timeseries and indicators](/en/docs/series)
@@ -0,0 +1,542 @@
# ArrayMaximum
Searches for the largest element in the first dimension of a multidimensional numeric array.
```
int  ArrayMaximum(
   const void&   array[],             // array for search
   int           start=0,             // index to start checking with
   int           count=WHOLE_ARRAY    // number of checked elements
   );
```
Parameters
array[]
[in]  A numeric array, in which search is made.
start=0
[in]  Index to start checking with.
count=WHOLE_ARRAY
[in]  Number of elements for search. By default, searches in the entire array (count=[WHOLE_ARRAY](/en/docs/constants/namedconstants/otherconstants)).
Return Value
The function returns an index of a found element taking into account the array [serial](/en/docs/array/arraygetasseries). In case of failure it returns -1.
Note
The [AS_SERIES](/en/docs/array/arraygetasseries) flag value is taken into account while searching for a maximum.
Functions ArrayMaximum and ArrayMinimum accept any-dimensional arrays as a parameter. However, searching is always applied to the first (zero) dimension.
Example:
```
#property description "The indicator displays larger timeframe's candlesticks on the current one."
//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 16
#property indicator_plots   8
//---- plot 1
#property indicator_label1  "BearBody"
#property indicator_color1  clrSeaGreen,clrSeaGreen
//---- plot 2
#property indicator_label2  "BearBodyEnd"
#property indicator_color2  clrSeaGreen,clrSeaGreen
//---- plot 3
#property indicator_label3  "BearShadow"
#property indicator_color3  clrSalmon,clrSalmon
//---- plot 4
#property indicator_label4  "BearShadowEnd"
#property indicator_color4  clrSalmon,clrSalmon
//---- plot 5
#property indicator_label5  "BullBody"
#property indicator_color5  clrOlive,clrOlive
//---- plot 6
#property indicator_label6  "BullBodyEnd"
#property indicator_color6  clrOlive,clrOlive
//---- plot 7
#property indicator_label7  "BullShadow"
#property indicator_color7  clrSkyBlue,clrSkyBlue
//---- plot 8
#property indicator_label8  "BullShadowEnd"
#property indicator_color8  clrSkyBlue,clrSkyBlue
//--- predefined constant
#define INDICATOR_EMPTY_VALUE 0.0
//--- input parameters
input ENUM_TIMEFRAMES InpPeriod=PERIOD_H4;              // Time frame for the indicator calculation
input datetime        InpDateStart=D'2013.01.01 00:00'; // Analysis start date
//--- indicator buffers for bearish candlesticks
double   ExtBearBodyFirst[];
double   ExtBearBodySecond[];
double   ExtBearBodyEndFirst[];
double   ExtBearBodyEndSecond[];
double   ExtBearShadowFirst[];
double   ExtBearShadowSecond[];
double   ExtBearShadowEndFirst[];
double   ExtBearShadowEndSecond[];
//--- indicator buffers for bullish candlesticks
double   ExtBullBodyFirst[];
double   ExtBullBodySecond[];
double   ExtBullBodyEndFirst[];
double   ExtBullBodyEndSecond[];
double   ExtBullShadowFirst[];
double   ExtBullShadowSecond[];
double   ExtBullShadowEndFirst[];
double   ExtBullShadowEndSecond[];
//--- global variables
datetime ExtTimeBuff[];      // larger time frame's time buffer
int      ExtSize=0;          // time buffer size
int      ExtCount=0;         // index inside time buffer
int      ExtStartPos=0;      // initial position for the indicator calculation
bool     ExtStartFlag=true;  // auxiliary flag for receiving the initial position
datetime ExtCurrentTime[1];  // last time of the larger time frame's bar generation
datetime ExtLastTime;        // last time from the larger time frame, for which the calculation is performed
bool     ExtBearFlag=true;   // flag for defining the order of writing the data to bearish indicator buffers
bool     ExtBullFlag=true;   // flag for defining the order of writing the data to bullish indicator buffers
int      ExtIndexMax=0;      // index of the maximum element in the array
int      ExtIndexMin=0;      // index of the minimum element in the array
int      ExtDirectionFlag=0; // price movement direction for the current candlestick
//--- shift between the candlestick's open and close price for correct drawing
const double ExtEmptyBodySize=0.2*SymbolInfoDouble(Symbol(),SYMBOL_POINT);
//+------------------------------------------------------------------+
//| Filling the basic part of the candlestick                        |
//+------------------------------------------------------------------+
void FillCandleMain(const double &open[],const double &close[],
                    const double &high[],const double &low[],
                    const int start,const int last,const int fill_index,
                    int &index_max,int &index_min)
  {
//--- find the index of the maximum and minimum elements in the arrays
   index_max=ArrayMaximum(high,ExtStartPos,last-start+1); // maximum in High
   index_min=ArrayMinimum(low,ExtStartPos,last-start+1);  // minimum in Low
//--- define how many bars from the current time frame are to be filled out
   int count=fill_index-start+1;
//--- if the close price at the first bar exceeds the one at the last bar, the candlestick is bearish
   if(open[start]>close[last])
     {
      //--- if the candlestick has been bullish before that, clear the values of bullish indicator buffers
      if(ExtDirectionFlag!=-1)
         ClearCandle(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,start,count);
      //--- bearish candlestick
      ExtDirectionFlag=-1;
      //--- generate the candlestick
      FormCandleMain(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,open[start],
                     close[last],high[index_max],low[index_min],start,count,ExtBearFlag);
      //--- exit the function
      return;
     }
//--- if the close price at the first bar is less than the one at the last bar, the candlestick is bullish
   if(open[start]<close[last])
     {
      //--- if the candlestick has been bearish before that, clear the values of bearish indicator buffers
      if(ExtDirectionFlag!=1)
         ClearCandle(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,start,count);
      //--- bullish candlestick
      ExtDirectionFlag=1;
      //--- generate the candlestick
      FormCandleMain(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,close[last],
                     open[start],high[index_max],low[index_min],start,count,ExtBullFlag);
      //--- exit the function             
      return;
     }
//--- if you are in this part of the function, the open price at the first bar is equal to
//--- the close price at the last bar; such candlestick is considered bearish
//--- if the candlestick has been bullish before that, clear the values of bullish indicator buffers
   if(ExtDirectionFlag!=-1)
      ClearCandle(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,start,count);
//--- bearish candlestick
   ExtDirectionFlag=-1;
//--- if close and open prices are equal, use the shift for correct display
   if(high[index_max]!=low[index_min])
      FormCandleMain(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,open[start],
                     open[start]-ExtEmptyBodySize,high[index_max],low[index_min],start,count,ExtBearFlag);
   else
      FormCandleMain(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,
                     open[start],open[start]-ExtEmptyBodySize,high[index_max],
                     high[index_max]-ExtEmptyBodySize,start,count,ExtBearFlag);
  }
//+------------------------------------------------------------------+
//| Fill out the end of the candlestick                              |
//+------------------------------------------------------------------+
void FillCandleEnd(const double &open[],const double &close[],
                   const double &high[],const double &low[],
                   const int start,const int last,const int fill_index,
                   const int index_max,const int index_min)
  {
//--- do not draw in case of a single bar
   if(last-start==0)
      return;
//--- if the close price at the first bar exceeds the one at the last bar, the candlestick is bearish
   if(open[start]>close[last])
     {
      //--- generate the end of the candlestick
      FormCandleEnd(ExtBearBodyEndFirst,ExtBearBodyEndSecond,ExtBearShadowEndFirst,ExtBearShadowEndSecond,
                    open[start],close[last],high[index_max],low[index_min],fill_index,ExtBearFlag);
      //--- exit the function
      return;
     }
//--- if the close price at the first bar is less than the one at the last bar, the candlestick is bullish
   if(open[start]<close[last])
     {
      //--- generate the end of the candlestick
      FormCandleEnd(ExtBullBodyEndFirst,ExtBullBodyEndSecond,ExtBullShadowEndFirst,ExtBullShadowEndSecond,
                    close[last],open[start],high[index_max],low[index_min],fill_index,ExtBullFlag);
      //--- exit the function
      return;
     }
//--- if you are in this part of the function, the open price at the first bar is equal to
//--- the close price at the last bar; such candlestick is considered bearish
//--- generate the end of the candlestick
   if(high[index_max]!=low[index_min])
      FormCandleEnd(ExtBearBodyEndFirst,ExtBearBodyEndSecond,ExtBearShadowEndFirst,ExtBearShadowEndSecond,open[start],
                    open[start]-ExtEmptyBodySize,high[index_max],low[index_min],fill_index,ExtBearFlag);
   else
      FormCandleEnd(ExtBearBodyEndFirst,ExtBearBodyEndSecond,ExtBearShadowEndFirst,ExtBearShadowEndSecond,open[start],
                    open[start]-ExtEmptyBodySize,high[index_max],high[index_max]-ExtEmptyBodySize,fill_index,ExtBearFlag);
  }
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- check the indicator period
   if(!CheckPeriod((int)Period(),(int)InpPeriod))
      return(INIT_PARAMETERS_INCORRECT);
//--- display price data in the foreground
   ChartSetInteger(0,CHART_FOREGROUND,0,1);
//--- binding indicator buffers
   SetIndexBuffer(0,ExtBearBodyFirst);
   SetIndexBuffer(1,ExtBearBodySecond);
   SetIndexBuffer(2,ExtBearBodyEndFirst);
   SetIndexBuffer(3,ExtBearBodyEndSecond);
   SetIndexBuffer(4,ExtBearShadowFirst);
   SetIndexBuffer(5,ExtBearShadowSecond);
   SetIndexBuffer(6,ExtBearShadowEndFirst);
   SetIndexBuffer(7,ExtBearShadowEndSecond);
   SetIndexBuffer(8,ExtBullBodyFirst);
   SetIndexBuffer(9,ExtBullBodySecond);
   SetIndexBuffer(10,ExtBullBodyEndFirst);
   SetIndexBuffer(11,ExtBullBodyEndSecond);
   SetIndexBuffer(12,ExtBullShadowFirst);
   SetIndexBuffer(13,ExtBullShadowSecond);
   SetIndexBuffer(14,ExtBullShadowEndFirst);
   SetIndexBuffer(15,ExtBullShadowEndSecond);
//--- set some property values for creating the indicator
   for(int i=0;i<8;i++)
     {
      PlotIndexSetInteger(i,PLOT_DRAW_TYPE,DRAW_FILLING); // graphical construction type
      PlotIndexSetInteger(i,PLOT_LINE_STYLE,STYLE_SOLID); // drawing line style
      PlotIndexSetInteger(i,PLOT_LINE_WIDTH,1);           // drawing line width
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- in case there are no calculated bars yet
   if(prev_calculated==0)
     {
      //--- receive larger time frame's bars arrival time
      if(!GetTimeData())
         return(0);
     }
//--- set direct indexing
   ArraySetAsSeries(time,false);
   ArraySetAsSeries(high,false);
   ArraySetAsSeries(low,false);
   ArraySetAsSeries(open,false);
   ArraySetAsSeries(close,false);
//--- start variable for calculation of bars
   int start=prev_calculated;
//--- if the bar is generated, recalculate the indicator value on it
   if(start!=0 && start==rates_total)
      start--;
//--- the loop for calculating the indicator values
   for(int i=start;i<rates_total;i++)
     {
      //--- fill i elements of the indicator buffers by empty values
      FillIndicatorBuffers(i);
      //--- perform calculation for bars starting from InpDateStart date
      if(time[i]>=InpDateStart)
        {
         //--- define position, from which the values are to be displayed, for the first time
         if(ExtStartFlag)
           {
            //--- store the number of the initial bar
            ExtStartPos=i;
            //--- define the first date from the larger time frame exceeding time[i]
            while(time[i]>=ExtTimeBuff[ExtCount])
               if(ExtCount<ExtSize-1)
                  ExtCount++;
            //--- change the value of the flag in order not to run into this block again
            ExtStartFlag=false;
           }
         //--- check if there are still any elements in the array
         if(ExtCount<ExtSize)
           {
            //--- wait for the current time frame's value to reach the larger time frame's one
            if(time[i]>=ExtTimeBuff[ExtCount])
              {
               //--- draw the main part of the candlestick (without filling out the area between the last and penultimate bar)
               FillCandleMain(open,close,high,low,ExtStartPos,i-1,i-2,ExtIndexMax,ExtIndexMin);
               //--- fill out the end of the candlestick (the area between the last and the penultimate bar)
               FillCandleEnd(open,close,high,low,ExtStartPos,i-1,i-1,ExtIndexMax,ExtIndexMin);
               //--- shift the initial position for drawing the next candlestick
               ExtStartPos=i;
               //--- increase the array counter
               ExtCount++;
              }
            else
               continue;
           }
         else
           {
            //--- reset the array values
            ResetLastError();
            //--- receive the last date from the larger time frame
            if(CopyTime(Symbol(),InpPeriod,0,1,ExtCurrentTime)==-1)
              {
               Print("Data copy error, code = ",GetLastError());
               return(0);
              }
            //--- if the new date is later, stop generating the candlestick
            if(ExtCurrentTime[0]>ExtLastTime)
              {
               //--- clear the area between the last and penultimate bars in the main indicator buffers
               ClearEndOfBodyMain(i-1);
               //--- fill out the area using auxiliary indicator buffers
               FillCandleEnd(open,close,high,low,ExtStartPos,i-1,i-1,ExtIndexMax,ExtIndexMin);
               //--- shift the initial position for drawing the next candlestick
               ExtStartPos=i;
               //--- reset price direction flag
               ExtDirectionFlag=0;
               //--- store the new last date
               ExtLastTime=ExtCurrentTime[0];
              }
            else
              {
               //--- generate the candlestick
               FillCandleMain(open,close,high,low,ExtStartPos,i,i,ExtIndexMax,ExtIndexMin);
              }
           }
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Check correctness of the specified indicator period              |
//+------------------------------------------------------------------+
bool CheckPeriod(int current_period,int high_period)
  {
//--- the indicator period should exceed the timeframe on which it is displayed
   if(current_period>=high_period)
     {
      Print("Error! The value of the indicator period should exceed the value of the current time frame!");
      return(false);
     }
//--- if the indicator period is one week or month, the period is correct
   if(high_period>32768)
      return(true);
//--- convert period values to minutes
   if(high_period>30)
      high_period=(high_period-16384)*60;
   if(current_period>30)
      current_period=(current_period-16384)*60;
//--- the indicator period should be multiple of the time frame it is displayed on
   if(high_period%current_period!=0)
     {
      Print("Error! The value of the indicator period should be multiple of the value of the current time frame!");
      return(false);
     }
//--- the indicator period should exceed the time frame it is displayed on 3 or more times
   if(high_period/current_period<3)
     {
      Print("Error! The indicator period should exceed the current time frame 3 or more times!");
      return(false);
     }
//--- the indicator period is correct for the current time frame
   return(true);
  }
//+------------------------------------------------------------------+
//| Receive time data from the larger time frame                     |
//+------------------------------------------------------------------+
bool GetTimeData(void)
  {
//--- reset the error value
   ResetLastError();
//--- copy all data for the current time
   if(CopyTime(Symbol(),InpPeriod,InpDateStart,TimeCurrent(),ExtTimeBuff)==-1)
     {
      //--- receive the error code
      int code=GetLastError();
      //--- print out the error message
      PrintFormat("Data copy error! %s",code==4401
                  ? "History is still being uploaded!"
                  : "Code = "+IntegerToString(code));
      //--- return false to make a repeated attempt to download data
      return(false);
     }
//--- receive the array size
   ExtSize=ArraySize(ExtTimeBuff);
//--- set the loop index for the array to zero
   ExtCount=0;
//--- set the current candlestick's position on the time frame to zero
   ExtStartPos=0;
   ExtStartFlag=true;
//--- store the last time value from the larger time frame
   ExtLastTime=ExtTimeBuff[ExtSize-1];
//--- successful execution
   return(true);
  }
//+--------------------------------------------------------------------------+
//| Function forms the main part of the candlestick. Depending on the flag's |
//| value, the function defines what data and arrays are                     |
//| to be used for correct display.                                          |
//+--------------------------------------------------------------------------+
void FormCandleMain(double &body_fst[],double &body_snd[],
                    double &shadow_fst[],double &shadow_snd[],
                    const double fst_value,const double snd_value,
                    const double fst_extremum,const double snd_extremum,
                    const int start,const int count,const bool flag)
  {
//--- check the flag's value
   if(flag)
     {
      //--- generate the candlestick's body
      FormMain(body_fst,body_snd,fst_value,snd_value,start,count);
      //--- generate the candlestick's shadow
      FormMain(shadow_fst,shadow_snd,fst_extremum,snd_extremum,start,count);
     }
   else
     {
      //--- generate the candlestick's body
      FormMain(body_fst,body_snd,snd_value,fst_value,start,count);
      //--- generate the candlestick's shadow
      FormMain(shadow_fst,shadow_snd,snd_extremum,fst_extremum,start,count);
     }
  }
//+-------------------------------------------------------------------------------+
//| The function forms the end of the candlestick. Depending on the flag's value, |
//| the function defines what data and arrays are                                 |
//| to be used for correct display.                                               |
//+-------------------------------------------------------------------------------+
void FormCandleEnd(double &body_fst[],double &body_snd[],
                   double &shadow_fst[],double &shadow_snd[],
                   const double fst_value,const double snd_value,
                   const double fst_extremum,const double snd_extremum,
                   const int end,bool &flag)
  {
//--- check the flag's value
   if(flag)
     {
      //--- generate the end of the candlestick's body
      FormEnd(body_fst,body_snd,fst_value,snd_value,end);
      //--- generate the end of the candlestick's shadow
      FormEnd(shadow_fst,shadow_snd,fst_extremum,snd_extremum,end);
      //--- change the flag's value to the opposite one
      flag=false;
     }
   else
     {
      //--- generate the end of the candlestick's body
      FormEnd(body_fst,body_snd,snd_value,fst_value,end);
      //--- generate the end of the candlestick's shadow
      FormEnd(shadow_fst,shadow_snd,snd_extremum,fst_extremum,end);
      //--- change the flag's value to the opposite one
      flag=true;
     }
  }
//+---------------------------------------------------------------------------------+
//| Clear the end of the candlestick (the area between the last and the penultimate |
//| bar)                                                                            |
//+---------------------------------------------------------------------------------+
void ClearEndOfBodyMain(const int ind)
  {
   ClearCandle(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,ind,1);
   ClearCandle(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,ind,1);
  }
//+--------------------------------------------------------------------------+
//| Clear the candlestick                                                    |
//+--------------------------------------------------------------------------+
void ClearCandle(double &body_fst[],double &body_snd[],double &shadow_fst[],
                 double &shadow_snd[],const int start,const int count)
  {
//--- check
   if(count!=0)
     {
      //--- fill indicator buffers with empty values
      ArrayFill(body_fst,start,count,INDICATOR_EMPTY_VALUE);
      ArrayFill(body_snd,start,count,INDICATOR_EMPTY_VALUE);
      ArrayFill(shadow_fst,start,count,INDICATOR_EMPTY_VALUE);
      ArrayFill(shadow_snd,start,count,INDICATOR_EMPTY_VALUE);
     }
  }
//+--------------------------------------------------------------------------+
//| Generate the main part of the candlestick                                |
//+--------------------------------------------------------------------------+
void FormMain(double &fst[],double &snd[],const double fst_value,
              const double snd_value,const int start,const int count)
  {
//--- check
   if(count!=0)
     {
      //--- fill indicator buffers with values
      ArrayFill(fst,start,count,fst_value);
      ArrayFill(snd,start,count,snd_value);
     }
  }
//+-----------------------------------------------------------------------------+
//| Generate the end of the candlestick                                         |
//+-----------------------------------------------------------------------------+
void FormEnd(double &fst[],double &snd[],const double fst_value,
             const double snd_value,const int last)
  {
//--- fill indicator buffers with values
   ArrayFill(fst,last-1,2,fst_value);
   ArrayFill(snd,last-1,2,snd_value);
  }
//+------------------------------------------------------------------+
//| Fill i element of the indicator buffers by empty values          |
//+------------------------------------------------------------------+
void FillIndicatorBuffers(const int i)
  {
//--- set an empty value in the cell of the indicator buffers
   ExtBearBodyFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearBodySecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBearBodyEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearBodyEndSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowEndSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodyFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodySecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodyEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodyEndSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowEndSecond[i]=INDICATOR_EMPTY_VALUE;
  }
```
@@ -0,0 +1,542 @@
# ArrayMinimum
Searches for the lowest element in the first dimension of a multidimensional numeric array.
```
int  ArrayMinimum(
   const void&   array[],             // array for search
   int           start=0,             // index to start checking with
   int           count=WHOLE_ARRAY    // number of checked elements
   );
```
Parameters
array[]
[in]  A numeric array, in which search is made.
start=0
[in]  Index to start checking with.
count=WHOLE_ARRAY
[in]  Number of elements for search. By default, searches in the entire array (count=[WHOLE_ARRAY](/en/docs/constants/namedconstants/otherconstants)).
Return Value
The function returns an index of a found element taking into account the array [serial](/en/docs/array/arraygetasseries). In case of failure it returns -1.
Note
The [AS_SERIES](/en/docs/array/arraygetasseries) flag value is taken into account while searching for a minimum.
Functions ArrayMaximum and ArrayMinimum accept any-dimensional arrays as a parameter. However, searching is always applied to the first (zero) dimension.
Example:
```
#property description "The indicator displays larger timeframe's candlesticks on the current one."
//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 16
#property indicator_plots   8
//---- plot 1
#property indicator_label1  "BearBody"
#property indicator_color1  clrSeaGreen,clrSeaGreen
//---- plot 2
#property indicator_label2  "BearBodyEnd"
#property indicator_color2  clrSeaGreen,clrSeaGreen
//---- plot 3
#property indicator_label3  "BearShadow"
#property indicator_color3  clrSalmon,clrSalmon
//---- plot 4
#property indicator_label4  "BearShadowEnd"
#property indicator_color4  clrSalmon,clrSalmon
//---- plot 5
#property indicator_label5  "BullBody"
#property indicator_color5  clrOlive,clrOlive
//---- plot 6
#property indicator_label6  "BullBodyEnd"
#property indicator_color6  clrOlive,clrOlive
//---- plot 7
#property indicator_label7  "BullShadow"
#property indicator_color7  clrSkyBlue,clrSkyBlue
//---- plot 8
#property indicator_label8  "BullShadowEnd"
#property indicator_color8  clrSkyBlue,clrSkyBlue
//--- predefined constant
#define INDICATOR_EMPTY_VALUE 0.0
//--- input parameters
input ENUM_TIMEFRAMES InpPeriod=PERIOD_H4;              // Time frame for the indicator calculation
input datetime        InpDateStart=D'2013.01.01 00:00'; // Analysis start date
//--- indicator buffers for bearish candlesticks
double   ExtBearBodyFirst[];
double   ExtBearBodySecond[];
double   ExtBearBodyEndFirst[];
double   ExtBearBodyEndSecond[];
double   ExtBearShadowFirst[];
double   ExtBearShadowSecond[];
double   ExtBearShadowEndFirst[];
double   ExtBearShadowEndSecond[];
//--- indicator buffers for bullish candlesticks
double   ExtBullBodyFirst[];
double   ExtBullBodySecond[];
double   ExtBullBodyEndFirst[];
double   ExtBullBodyEndSecond[];
double   ExtBullShadowFirst[];
double   ExtBullShadowSecond[];
double   ExtBullShadowEndFirst[];
double   ExtBullShadowEndSecond[];
//--- global variables
datetime ExtTimeBuff[];      // larger time frame's time buffer
int      ExtSize=0;          // time buffer size
int      ExtCount=0;         // index inside time buffer
int      ExtStartPos=0;      // initial position for the indicator calculation
bool     ExtStartFlag=true;  // auxiliary flag for receiving the initial position
datetime ExtCurrentTime[1];  // last time of the larger time frame's bar generation
datetime ExtLastTime;        // last time from the larger time frame, for which the calculation is performed
bool     ExtBearFlag=true;   // flag for defining the order of writing the data to bearish indicator buffers
bool     ExtBullFlag=true;   // flag for defining the order of writing the data to bullish indicator buffers
int      ExtIndexMax=0;      // index of the maximum element in the array
int      ExtIndexMin=0;      // index of the minimum element in the array
int      ExtDirectionFlag=0; // price movement direction for the current candlestick
//--- shift between the candlestick's open and close price for correct drawing
const double ExtEmptyBodySize=0.2*SymbolInfoDouble(Symbol(),SYMBOL_POINT);
//+------------------------------------------------------------------+
//| Filling the basic part of the candlestick                        |
//+------------------------------------------------------------------+
void FillCandleMain(const double &open[],const double &close[],
                    const double &high[],const double &low[],
                    const int start,const int last,const int fill_index,
                    int &index_max,int &index_min)
  {
//--- find the index of the maximum and minimum elements in the arrays
   index_max=ArrayMaximum(high,ExtStartPos,last-start+1); // maximum in High
   index_min=ArrayMinimum(low,ExtStartPos,last-start+1);  // minimum in Low
//--- define how many bars from the current time frame are to be filled out
   int count=fill_index-start+1;
//--- if the close price at the first bar exceeds the one at the last bar, the candlestick is bearish
   if(open[start]>close[last])
     {
      //--- if the candlestick has been bullish before that, clear the values of bullish indicator buffers
      if(ExtDirectionFlag!=-1)
         ClearCandle(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,start,count);
      //--- bearish candlestick
      ExtDirectionFlag=-1;
      //--- generate the candlestick
      FormCandleMain(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,open[start],
                     close[last],high[index_max],low[index_min],start,count,ExtBearFlag);
      //--- exit the function
      return;
     }
//--- if the close price at the first bar is less than the one at the last bar, the candlestick is bullish
   if(open[start]<close[last])
     {
      //--- if the candlestick has been bearish before that, clear the values of bearish indicator buffers
      if(ExtDirectionFlag!=1)
         ClearCandle(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,start,count);
      //--- bullish candlestick
      ExtDirectionFlag=1;
      //--- generate the candlestick
      FormCandleMain(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,close[last],
                     open[start],high[index_max],low[index_min],start,count,ExtBullFlag);
      //--- exit the function             
      return;
     }
//--- if you are in this part of the function, the open price at the first bar is equal to
//--- the close price at the last bar; such candlestick is considered bearish
//--- if the candlestick has been bullish before that, clear the values of bullish indicator buffers
   if(ExtDirectionFlag!=-1)
      ClearCandle(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,start,count);
//--- bearish candlestick
   ExtDirectionFlag=-1;
//--- if close and open prices are equal, use the shift for correct display
   if(high[index_max]!=low[index_min])
      FormCandleMain(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,open[start],
                     open[start]-ExtEmptyBodySize,high[index_max],low[index_min],start,count,ExtBearFlag);
   else
      FormCandleMain(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,
                     open[start],open[start]-ExtEmptyBodySize,high[index_max],
                     high[index_max]-ExtEmptyBodySize,start,count,ExtBearFlag);
  }
//+------------------------------------------------------------------+
//| Fill out the end of the candlestick                              |
//+------------------------------------------------------------------+
void FillCandleEnd(const double &open[],const double &close[],
                   const double &high[],const double &low[],
                   const int start,const int last,const int fill_index,
                   const int index_max,const int index_min)
  {
//--- do not draw in case of a single bar
   if(last-start==0)
      return;
//--- if the close price at the first bar exceeds the one at the last bar, the candlestick is bearish
   if(open[start]>close[last])
     {
      //--- generate the end of the candlestick
      FormCandleEnd(ExtBearBodyEndFirst,ExtBearBodyEndSecond,ExtBearShadowEndFirst,ExtBearShadowEndSecond,
                    open[start],close[last],high[index_max],low[index_min],fill_index,ExtBearFlag);
      //--- exit the function
      return;
     }
//--- if the close price at the first bar is less than the one at the last bar, the candlestick is bullish
   if(open[start]<close[last])
     {
      //--- generate the end of the candlestick
      FormCandleEnd(ExtBullBodyEndFirst,ExtBullBodyEndSecond,ExtBullShadowEndFirst,ExtBullShadowEndSecond,
                    close[last],open[start],high[index_max],low[index_min],fill_index,ExtBullFlag);
      //--- exit the function
      return;
     }
//--- if you are in this part of the function, the open price at the first bar is equal to
//--- the close price at the last bar; such candlestick is considered bearish
//--- generate the end of the candlestick
   if(high[index_max]!=low[index_min])
      FormCandleEnd(ExtBearBodyEndFirst,ExtBearBodyEndSecond,ExtBearShadowEndFirst,ExtBearShadowEndSecond,open[start],
                    open[start]-ExtEmptyBodySize,high[index_max],low[index_min],fill_index,ExtBearFlag);
   else
      FormCandleEnd(ExtBearBodyEndFirst,ExtBearBodyEndSecond,ExtBearShadowEndFirst,ExtBearShadowEndSecond,open[start],
                    open[start]-ExtEmptyBodySize,high[index_max],high[index_max]-ExtEmptyBodySize,fill_index,ExtBearFlag);
  }
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- check the indicator period
   if(!CheckPeriod((int)Period(),(int)InpPeriod))
      return(INIT_PARAMETERS_INCORRECT);
//--- display price data in the foreground
   ChartSetInteger(0,CHART_FOREGROUND,0,1);
//--- binding indicator buffers
   SetIndexBuffer(0,ExtBearBodyFirst);
   SetIndexBuffer(1,ExtBearBodySecond);
   SetIndexBuffer(2,ExtBearBodyEndFirst);
   SetIndexBuffer(3,ExtBearBodyEndSecond);
   SetIndexBuffer(4,ExtBearShadowFirst);
   SetIndexBuffer(5,ExtBearShadowSecond);
   SetIndexBuffer(6,ExtBearShadowEndFirst);
   SetIndexBuffer(7,ExtBearShadowEndSecond);
   SetIndexBuffer(8,ExtBullBodyFirst);
   SetIndexBuffer(9,ExtBullBodySecond);
   SetIndexBuffer(10,ExtBullBodyEndFirst);
   SetIndexBuffer(11,ExtBullBodyEndSecond);
   SetIndexBuffer(12,ExtBullShadowFirst);
   SetIndexBuffer(13,ExtBullShadowSecond);
   SetIndexBuffer(14,ExtBullShadowEndFirst);
   SetIndexBuffer(15,ExtBullShadowEndSecond);
//--- set some property values for creating the indicator
   for(int i=0;i<8;i++)
     {
      PlotIndexSetInteger(i,PLOT_DRAW_TYPE,DRAW_FILLING); // graphical construction type
      PlotIndexSetInteger(i,PLOT_LINE_STYLE,STYLE_SOLID); // drawing line style
      PlotIndexSetInteger(i,PLOT_LINE_WIDTH,1);           // drawing line width
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- in case there are no calculated bars yet
   if(prev_calculated==0)
     {
      //--- receive larger time frame's bars arrival time
      if(!GetTimeData())
         return(0);
     }
//--- set direct indexing
   ArraySetAsSeries(time,false);
   ArraySetAsSeries(high,false);
   ArraySetAsSeries(low,false);
   ArraySetAsSeries(open,false);
   ArraySetAsSeries(close,false);
//--- start variable for calculation of bars
   int start=prev_calculated;
//--- if the bar is generated, recalculate the indicator value on it
   if(start!=0 && start==rates_total)
      start--;
//--- the loop for calculating the indicator values
   for(int i=start;i<rates_total;i++)
     {
      //--- fill i elements of the indicator buffers by empty values
      FillIndicatorBuffers(i);
      //--- perform calculation for bars starting from InpDateStart date
      if(time[i]>=InpDateStart)
        {
         //--- define position, from which the values are to be displayed, for the first time
         if(ExtStartFlag)
           {
            //--- store the number of the initial bar
            ExtStartPos=i;
            //--- define the first date from the larger time frame exceeding time[i]
            while(time[i]>=ExtTimeBuff[ExtCount])
               if(ExtCount<ExtSize-1)
                  ExtCount++;
            //--- change the value of the flag in order not to run into this block again
            ExtStartFlag=false;
           }
         //--- check if there are still any elements in the array
         if(ExtCount<ExtSize)
           {
            //--- wait for the current time frame's value to reach the larger time frame's one
            if(time[i]>=ExtTimeBuff[ExtCount])
              {
               //--- draw the main part of the candlestick (without filling out the area between the last and penultimate bar)
               FillCandleMain(open,close,high,low,ExtStartPos,i-1,i-2,ExtIndexMax,ExtIndexMin);
               //--- fill out the end of the candlestick (the area between the last and the penultimate bar)
               FillCandleEnd(open,close,high,low,ExtStartPos,i-1,i-1,ExtIndexMax,ExtIndexMin);
               //--- shift the initial position for drawing the next candlestick
               ExtStartPos=i;
               //--- increase the array counter
               ExtCount++;
              }
            else
               continue;
           }
         else
           {
            //--- reset the array values
            ResetLastError();
            //--- receive the last date from the larger time frame
            if(CopyTime(Symbol(),InpPeriod,0,1,ExtCurrentTime)==-1)
              {
               Print("Data copy error, code = ",GetLastError());
               return(0);
              }
            //--- if the new date is later, stop generating the candlestick
            if(ExtCurrentTime[0]>ExtLastTime)
              {
               //--- clear the area between the last and penultimate bars in the main indicator buffers
               ClearEndOfBodyMain(i-1);
               //--- fill out the area using auxiliary indicator buffers
               FillCandleEnd(open,close,high,low,ExtStartPos,i-1,i-1,ExtIndexMax,ExtIndexMin);
               //--- shift the initial position for drawing the next candlestick
               ExtStartPos=i;
               //--- reset price direction flag
               ExtDirectionFlag=0;
               //--- store the new last date
               ExtLastTime=ExtCurrentTime[0];
              }
            else
              {
               //--- generate the candlestick
               FillCandleMain(open,close,high,low,ExtStartPos,i,i,ExtIndexMax,ExtIndexMin);
              }
           }
        }
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Check correctness of the specified indicator period              |
//+------------------------------------------------------------------+
bool CheckPeriod(int current_period,int high_period)
  {
//--- the indicator period should exceed the timeframe on which it is displayed
   if(current_period>=high_period)
     {
      Print("Error! The value of the indicator period should exceed the value of the current time frame!");
      return(false);
     }
//--- if the indicator period is one week or month, the period is correct
   if(high_period>32768)
      return(true);
//--- convert period values to minutes
   if(high_period>30)
      high_period=(high_period-16384)*60;
   if(current_period>30)
      current_period=(current_period-16384)*60;
//--- the indicator period should be multiple of the time frame it is displayed on
   if(high_period%current_period!=0)
     {
      Print("Error! The value of the indicator period should be multiple of the value of the current time frame!");
      return(false);
     }
//--- the indicator period should exceed the time frame it is displayed on 3 or more times
   if(high_period/current_period<3)
     {
      Print("Error! The indicator period should exceed the current time frame 3 or more times!");
      return(false);
     }
//--- the indicator period is correct for the current time frame
   return(true);
  }
//+------------------------------------------------------------------+
//| Receive time data from the larger time frame                     |
//+------------------------------------------------------------------+
bool GetTimeData(void)
  {
//--- reset the error value
   ResetLastError();
//--- copy all data for the current time
   if(CopyTime(Symbol(),InpPeriod,InpDateStart,TimeCurrent(),ExtTimeBuff)==-1)
     {
      //--- receive the error code
      int code=GetLastError();
      //--- print out the error message
      PrintFormat("Data copy error! %s",code==4401
                  ? "History is still being uploaded!"
                  : "Code = "+IntegerToString(code));
      //--- return false to make a repeated attempt to download data
      return(false);
     }
//--- receive the array size
   ExtSize=ArraySize(ExtTimeBuff);
//--- set the loop index for the array to zero
   ExtCount=0;
//--- set the current candlestick's position on the time frame to zero
   ExtStartPos=0;
   ExtStartFlag=true;
//--- store the last time value from the larger time frame
   ExtLastTime=ExtTimeBuff[ExtSize-1];
//--- successful execution
   return(true);
  }
//+--------------------------------------------------------------------------+
//| Function forms the main part of the candlestick. Depending on the flag's |
//| value, the function defines what data and arrays are                     |
//| to be used for correct display.                                          |
//+--------------------------------------------------------------------------+
void FormCandleMain(double &body_fst[],double &body_snd[],
                    double &shadow_fst[],double &shadow_snd[],
                    const double fst_value,const double snd_value,
                    const double fst_extremum,const double snd_extremum,
                    const int start,const int count,const bool flag)
  {
//--- check the flag's value
   if(flag)
     {
      //--- generate the candlestick's body
      FormMain(body_fst,body_snd,fst_value,snd_value,start,count);
      //--- generate the candlestick's shadow
      FormMain(shadow_fst,shadow_snd,fst_extremum,snd_extremum,start,count);
     }
   else
     {
      //--- generate the candlestick's body
      FormMain(body_fst,body_snd,snd_value,fst_value,start,count);
      //--- generate the candlestick's shadow
      FormMain(shadow_fst,shadow_snd,snd_extremum,fst_extremum,start,count);
     }
  }
//+--------------------------------------------------------------------------------+
//| The function forms the end of the candlestick. Depending on the flag's value,  |
//| the function defines what data and arrays are                                  |
//| to be used for correct display.                                                |
//+--------------------------------------------------------------------------------+
void FormCandleEnd(double &body_fst[],double &body_snd[],
                   double &shadow_fst[],double &shadow_snd[],
                   const double fst_value,const double snd_value,
                   const double fst_extremum,const double snd_extremum,
                   const int end,bool &flag)
  {
//--- check the flag's value
   if(flag)
     {
      //--- generate the end of the candlestick's body
      FormEnd(body_fst,body_snd,fst_value,snd_value,end);
      //--- generate the end of the candlestick's shadow
      FormEnd(shadow_fst,shadow_snd,fst_extremum,snd_extremum,end);
      //--- change the flag's value to the opposite one
      flag=false;
     }
   else
     {
      //--- generate the end of the candlestick's body
      FormEnd(body_fst,body_snd,snd_value,fst_value,end);
      //--- generate the end of the candlestick's shadow
      FormEnd(shadow_fst,shadow_snd,snd_extremum,fst_extremum,end);
      //--- change the flag's value to the opposite one
      flag=true;
     }
  }
//+-------------------------------------------------------------------------------------+
//| Clear the end of the candlestick (the area between the last and the penultimate     |
//| bar)                                                                                |
//+-------------------------------------------------------------------------------------+
void ClearEndOfBodyMain(const int ind)
  {
   ClearCandle(ExtBearBodyFirst,ExtBearBodySecond,ExtBearShadowFirst,ExtBearShadowSecond,ind,1);
   ClearCandle(ExtBullBodyFirst,ExtBullBodySecond,ExtBullShadowFirst,ExtBullShadowSecond,ind,1);
  }
//+------------------------------------------------------------------+
//| Clear the candlestick                                            |
//+------------------------------------------------------------------+
void ClearCandle(double &body_fst[],double &body_snd[],double &shadow_fst[],
                 double &shadow_snd[],const int start,const int count)
  {
//--- check
   if(count!=0)
     {
      //--- fill indicator buffers with empty values
      ArrayFill(body_fst,start,count,INDICATOR_EMPTY_VALUE);
      ArrayFill(body_snd,start,count,INDICATOR_EMPTY_VALUE);
      ArrayFill(shadow_fst,start,count,INDICATOR_EMPTY_VALUE);
      ArrayFill(shadow_snd,start,count,INDICATOR_EMPTY_VALUE);
     }
  }
//+------------------------------------------------------------------+
//| Generate the main part of the candlestick                        |
//+------------------------------------------------------------------+
void FormMain(double &fst[],double &snd[],const double fst_value,
              const double snd_value,const int start,const int count)
  {
//--- check
   if(count!=0)
     {
      //--- fill indicator buffers with values
      ArrayFill(fst,start,count,fst_value);
      ArrayFill(snd,start,count,snd_value);
     }
  }
//+------------------------------------------------------------------+
//| Generate the end of the candlestick                              |
//+------------------------------------------------------------------+
void FormEnd(double &fst[],double &snd[],const double fst_value,
             const double snd_value,const int last)
  {
//--- fill indicator buffers with values
   ArrayFill(fst,last-1,2,fst_value);
   ArrayFill(snd,last-1,2,snd_value);
  }
//+------------------------------------------------------------------+
//| Fill i element of the indicator buffers by empty values          |
//+------------------------------------------------------------------+
void FillIndicatorBuffers(const int i)
  {
//--- set an empty value in the cell of the indicator buffers
   ExtBearBodyFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearBodySecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBearBodyEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearBodyEndSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBearShadowEndSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodyFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodySecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodyEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullBodyEndSecond[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowEndFirst[i]=INDICATOR_EMPTY_VALUE;
   ExtBullShadowEndSecond[i]=INDICATOR_EMPTY_VALUE;
  }
```
@@ -0,0 +1,109 @@
# ArrayPrint
Prints an array of a simple type or a simple structure into journal.
```
void  ArrayPrint(
   const void&   array[],             // printed array
   uint          digits=_Digits,      // number of decimal places
   const string  separator=NULL,      // separator of the structure field values
   ulong         start=0,             // first printed element index
   ulong         count=WHOLE_ARRAY,   // number of printed elements
   ulong         flags=ARRAYPRINT_HEADER|ARRAYPRINT_INDEX|ARRAYPRINT_LIMIT|ARRAYPRINT_ALIGN    
   );
```
Parameters
array[]
[in]  Array of a simple type or a [simple structure](/en/docs/basis/types/classes#simple_structure).
digits=_Digits
[in]  The number of decimal places for real types. The default value is [_Digits](/en/docs/predefined/_digits).
separator=NULL
[in]  Separator of the structure element field values. The default value [NULL](/en/docs/basis/types/void) means an empty line. A space is used as a separator in that case.
start=0
[in]  The index of the first printed array element.  It is printed from the zero index by default.
count=WHOLE_ARRAY
[in]  Number of the array elements to be printed. The entire array is displayed by default (count=[WHOLE_ARRAY](/en/docs/constants/namedconstants/otherconstants)).
flags=ARRAYPRINT_HEADER|ARRAYPRINT_INDEX|ARRAYPRINT_LIMIT|ARRAYPRINT_ALIGN
[in]  Combination of flags setting the output mode. All flags are enabled by default:
- ARRAYPRINT_HEADER print headers for the structure array
ARRAYPRINT_INDEX print index at the left side
ARRAYPRINT_LIMIT print only the first 100 and the last 100 array elements. Use if you want to print only a part of a large array.
ARRAYPRINT_ALIGN enable alignment of the printed values numbers are aligned to the right, while lines to the left.
ARRAYPRINT_DATE when printing datetime, print the date in the dd.mm.yyyy format
ARRAYPRINT_MINUTES when printing datetime, print the time in the HH:MM format
ARRAYPRINT_SECONDS when printing datetime, print the time in the HH:MM:SS format
Return Value
No
Note
ArrayPrint() does not print all structure array fields into journal array and [object pointer](/en/docs/basis/types/object_pointers) fields are skipped. These columns are simply not printed for more convenient presentation. If you need to print all structure fields, you need to write your own mass print function with the desired formatting.
Example:
```
//--- print the values of the last 10 bars
   MqlRates rates[];
   if(CopyRates(_Symbol,_Period,1,10,rates))
     {
      ArrayPrint(rates);
      Print("Check\n[time]\t[open]\t[high]\t[low]\t[close]\t[tick_volume]\t[spread]\t[real_volume]");
      for(int i=0;i<10;i++)
        {
         PrintFormat("[%d]\t%s\t%G\t%G\t%G\t%G\t%G\t%G\t%I64d\t",i,
         TimeToString(rates[i].time,TIME_DATE|TIME_MINUTES|TIME_SECONDS),
         rates[i].open,rates[i].high,rates[i].low,rates[i].close,
         rates[i].tick_volume,rates[i].spread,rates[i].real_volume);
        }
     }
   else
      PrintFormat("CopyRates failed, error code=%d",GetLastError());
//--- example of printing
/*
                    [time]  [open]  [high]   [low] [close] [tick_volume] [spread] [real_volume]
   [0] 2016.11.09 04:00:00 1.11242 1.12314 1.11187 1.12295         18110       10   17300175000
   [1] 2016.11.09 05:00:00 1.12296 1.12825 1.11930 1.12747         17829        9   15632176000
   [2] 2016.11.09 06:00:00 1.12747 1.12991 1.12586 1.12744         13458       10    9593492000
   [3] 2016.11.09 07:00:00 1.12743 1.12763 1.11988 1.12194         15362        9   12352245000
   [4] 2016.11.09 08:00:00 1.12194 1.12262 1.11058 1.11172         16833        9   12961333000
   [5] 2016.11.09 09:00:00 1.11173 1.11348 1.10803 1.11052         15933        8   10720384000
   [6] 2016.11.09 10:00:00 1.11052 1.11065 1.10289 1.10528         11888        9    8084811000
   [7] 2016.11.09 11:00:00 1.10512 1.11041 1.10472 1.10915          7284       10    5087113000
   [8] 2016.11.09 12:00:00 1.10915 1.11079 1.10892 1.10904          8710        9    6769629000
   [9] 2016.11.09 13:00:00 1.10904 1.10913 1.10223 1.10263          8956        7    7192138000
   Check
   [time] [open] [high] [low] [close] [tick_volume] [spread] [real_volume]
   [0] 2016.11.09 04:00:00 1.11242 1.12314 1.11187 1.12295 18110 10 17300175000 
   [1] 2016.11.09 05:00:00 1.12296 1.12825 1.1193 1.12747 17829 9 15632176000 
   [2] 2016.11.09 06:00:00 1.12747 1.12991 1.12586 1.12744 13458 10 9593492000 
   [3] 2016.11.09 07:00:00 1.12743 1.12763 1.11988 1.12194 15362 9 12352245000 
   [4] 2016.11.09 08:00:00 1.12194 1.12262 1.11058 1.11172 16833 9 12961333000 
   [5] 2016.11.09 09:00:00 1.11173 1.11348 1.10803 1.11052 15933 8 10720384000 
   [6] 2016.11.09 10:00:00 1.11052 1.11065 1.10289 1.10528 11888 9 8084811000 
   [7] 2016.11.09 11:00:00 1.10512 1.11041 1.10472 1.10915 7284 10 5087113000 
   [8] 2016.11.09 12:00:00 1.10915 1.11079 1.10892 1.10904 8710 9 6769629000 
   [9] 2016.11.09 13:00:00 1.10904 1.10913 1.10223 1.10263 8956 7 7192138000 
*/
```
See also
[FileSave](/en/docs/files/filesave), [FileLoad](/en/docs/files/fileload)
@@ -0,0 +1,56 @@
# ArrayRange
The function returns the number of elements in a selected array dimension.
```
int  ArrayRange(
   const void&   array[],      // array for check
   int           rank_index    // index of dimension
   );
```
Parameters
array[]
[in]  Checked array.
rank_index
[in]  Index of dimension.
Return Value
Number of elements in a selected array dimension.
Note
Since indexes start at zero, the number of the array dimensions is one greater than the index of the last dimension.
Example:
```
void OnStart()
  {
//--- create four-dimensional array
   double array[][5][2][4];
//--- set the size of the zero dimension
   ArrayResize(array,10,10);
//--- print dimensions
   int temp;
   for(int i=0;i<4;i++)
     {
      //--- receive the size of i dimension
      temp=ArrayRange(array,i);
      //--- print
      PrintFormat("dim = %d, range = %d",i,temp);
     }
//--- Result
// dim = 0, range = 10
// dim = 1, range = 5
// dim = 2, range = 2
// dim = 3, range = 4
  }
```
@@ -0,0 +1,119 @@
# ArrayResize
The function sets a new size for the first dimension
```
int  ArrayResize(
   void&  array[],              // array passed by reference
   int    new_size,             // new array size
   int    reserve_size=0        // reserve size value (excess)
   );
```
Parameters
array[]
[out] Array for changing sizes.
new_size
[in]  New size for the first dimension.
reserve_size=0
[in]  Distributed size to get reserve.
Return Value
If executed successfully, it returns count of all elements contained in the array after resizing, otherwise, returns -1, and array is not resized.
If ArrayResize() is applied to a [static](/en/docs/basis/types/dynamic_array#static_array) array, a [timeseries](/en/docs/series/bufferdirection) or an [indicator buffer](/en/docs/customind/setindexbuffer), the array size remains the same these arrays will not be reallocated. In this case, if new_size<=[ArraySize(](/en/docs/array/arraysize)array[)](/en/docs/array/arrayresize), the function will only return new_size; otherwise a value of -1 will be returned.
Note
The function can be applied only to [dynamic arrays](/en/docs/basis/types/dynamic_array). It should be noted that you cannot change the size of dynamic arrays assigned as indicator buffers by the [SetIndexBuffer()](/en/docs/customind/setindexbuffer) function. For indicator buffers, all operations of resizing are performed by the runtime subsystem of the terminal.
Total amount of elements in the array cannot exceed 2147483647.
With the frequent memory allocation, it is recommended to use a third parameter that sets a reserve to reduce the number of physical memory allocations. All the subsequent calls of ArrayResize do not lead to physical reallocation of memory, but only change the size of the first array dimension within the reserved memory. It should be remembered that the third parameter will be used only during physical memory allocation. For example:
```
ArrayResize(arr,1000,1000);
for(int i=1;i<3000;i++)
   ArrayResize(arr,i,1000);
```
In this case the memory will be reallocated twice, first before entering the 3000 iterations loop (the array size will be set to 1000), and the second time with i equal to 2000. If we skip the third parameter, there will be 2000 physical reallocations of memory, which will slow down the program.
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- Counters
   ulong start=GetTickCount();
   ulong now;
   int   count=0;
//--- An array for demonstration of a quick version
   double arr[];
   ArrayResize(arr,100000,100000);
//--- Check how fast the variant with memory reservation works
   Print("--- Test Fast: ArrayResize(arr,100000,100000)");
   for(int i=1;i<=300000;i++)
     {
      //--- Set a new array size specifying the reserve of 100,000 elements!
      ArrayResize(arr,i,100000);
      //--- When reaching a round number, show the array size and the time spent
      if(ArraySize(arr)%100000==0)
        {
         now=GetTickCount();
         count++;
         PrintFormat("%d. ArraySize(arr)=%d Time=%d ms",count,ArraySize(arr),(now-start));
         start=now; 
        }
     }
//--- Now show, how slow the version without memory reservation is
   double slow[];
   ArrayResize(slow,100000,100000);
//--- 
   count=0;
   start=GetTickCount();
   Print("---- Test Slow: ArrayResize(slow,100000)");
//---
   for(int i=1;i<=300000;i++)
     {
      //--- Set a new array size, but without the additional reserve
      ArrayResize(slow,i);
      //--- When reaching a round number, show the array size and the time spent
      if(ArraySize(slow)%100000==0)
        {
         now=GetTickCount();
         count++;
         PrintFormat("%d. ArraySize(slow)=%d Time=%d ms",count,ArraySize(slow),(now-start));
         start=now;
        }
     }
  }
//--- A sample result of the script
/*
   Test_ArrayResize (EURUSD,H1)   --- Test Fast: ArrayResize(arr,100000,100000)
   Test_ArrayResize (EURUSD,H1)   1. ArraySize(arr)=100000 Time=0 ms
   Test_ArrayResize (EURUSD,H1)   2. ArraySize(arr)=200000 Time=0 ms
   Test_ArrayResize (EURUSD,H1)   3. ArraySize(arr)=300000 Time=0 ms
   Test_ArrayResize (EURUSD,H1)   ---- Test Slow: ArrayResize(slow,100000)
   Test_ArrayResize (EURUSD,H1)   1. ArraySize(slow)=100000 Time=0 ms
   Test_ArrayResize (EURUSD,H1)   2. ArraySize(slow)=200000 Time=0 ms
   Test_ArrayResize (EURUSD,H1)   3. ArraySize(slow)=300000 Time=228511 ms
*/
```
See also
[ArrayInitialize](/en/docs/array/arrayinitialize)
@@ -0,0 +1,96 @@
# ArrayInsert
Inserts the specified number of elements from a source array to a receiving one starting from a specified index.
```
bool  ArrayInsert(
   void&        dst_array[],          // receiving array
   const void&  src_array[],          // source array
   uint         dst_start,            // receiver array index to be inserted
   uint         src_start=0,          // source array index to be copied
   uint         count=WHOLE_ARRAY     // number of elements to insert
   );
```
Parameters
dst_array[]
[in][out]  Receiving array the elements should be added to.
src_array[]
[in]  Source array the elements are to be added from.
dst_start
[in]  Index in the receiving array for inserting elements from the source array.
src_start=0
[in]  Index in the source array, starting from which the elements of the source array are taken for insertion.
count
[in]  Number of elements to be added from the source array. The [WHOLE_ARRAY](/en/docs/constants/namedconstants/otherconstants) means all elements from the specified index up to the end of the array.
Return Value
Returns true if successful, otherwise - false. To get information about the error, call the [GetLastError()](/en/docs/check/getlasterror) function. Possible errors:
- 5052 ERR_SMALL_ARRAY (the start and/or count parameters are set incorrectly or the src_array[] source array is empty),
- 5056 ERR_SERIES_ARRAY (the array cannot be changed, indicator buffer),
- 4006 ERR_INVALID_ARRAY (copying to oneself is not allowed, or the arrays are of different types, or there is a fixed-size array containing class objects or destructor structures),
- 4005 - ERR_STRUCT_WITHOBJECTS_ORCLASS (the array contains no [POD structures](/en/docs/basis/types/classes#simple_structure) meaning a simple copying is impossible),
- Errors occurred when changing the dst_array[] receiving array size are provided in the [ArrayRemove()](/en/docs/array/arrayremove) function description.
Note
If the function is used for a fixed-size array, the size of the dst_array[] receiving array itself does not change. Starting from the dst_start position, the elements of the receiving array are shifted to the right (the last counts of the elements "come off"), while the elements copied from the source array take their place.
You cannot insert the elements to the dynamic arrays designated as the indicator buffers by the [SetIndexBuffer()](/en/docs/customind/setindexbuffer) function. For indicator buffers, all size changing operations are performed by the terminal's executing subsystem.
In the source array, the elements are copied starting from the src_start index. The source array size remains unchanged. The elements to be added to the receiving array are not links to the source array elements. This means that subsequent changes of the elements in any of the two arrays are not reflected in the second one.
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- declare the fixed-size array and fill in the values
   int array_dest[10];
   for(int i=0;i<10;i++)
     {
      array_dest[i]=i;
     }
   //--- source array  
   int array_source[10];
   for(int i=0;i<10;i++)
     {
      array_source[i]=10+i;
     }
//--- display arrays before inserting the elements
   Print("Before calling ArrayInsert()");
   ArrayPrint(array_dest);
   ArrayPrint(array_source);
//--- insert 3 elements from the source array and show the new set of the receiving array
   ArrayInsert(array_dest,array_source,4,0,3);
   Print("After calling ArrayInsert()");
   ArrayPrint(array_dest);
/*
  Execution result
   Before calling ArrayInsert()
   0 1 2 3 4 5 6 7 8 9
   After calling ArrayInsert()
   0 1 2 3 10 11 12 7 8 9
*/
```
See also
[ArrayRemove](/en/docs/array/arrayremove),[ ArrayCopy](/en/docs/array/arraycopy), [ArrayResize](/en/docs/array/arrayresize), [ArrayFree](/en/docs/array/arrayfree)
@@ -0,0 +1,77 @@
# ArrayRemove
Removes the specified number of elements from the array starting with a specified index.
```
bool  ArrayRemove(
   void&        array[],            // array of any type
   uint         start,              // index the removal starts from
   uint         count=WHOLE_ARRAY   // number of elements
   );
```
Parameters
array[]
[in][out]  Array.
start
[in]  Index, starting from which the array elements are removed.
count=WHOLE_ARRAY
[in]  Number of removed elements. The [WHOLE_ARRAY](/en/docs/constants/namedconstants/otherconstants) value means removing all elements from the specified index up the end of the array.
Return Value
Returns true if successful, otherwise - false. To get information about the error, call the [GetLastError()](/en/docs/check/getlasterror) function. Possible errors:
- 5052 ERR_SMALL_ARRAY (too big start value),
- 5056 ERR_SERIES_ARRAY (the array cannot be changed, indicator buffer),
- 4003 ERR_INVALID_PARAMETER (too big count value),
- 4005 - ERR_STRUCT_WITHOBJECTS_ORCLASS (fixed-size array containing complex objects with the destructor),
- 4006 - ERR_INVALID_ARRAY  (fixed-size array containing structure or class objects with a destructor).
Note
If the function is used for a fixed-size array, the array size does not change: the remaining "tail" is physically copied to the start position. For accurate understanding of how the function works, see the example below. "Physical" copying means the copied objects are not created by calling the constructor or copying operator. Instead, the binary representation of an object is copied. For this reason, you cannot apply the ArrayRemove() function to the fixed-size array containing objects with the destructor (the ERR_INVALID_ARRAY or ERR_STRUCT_WITHOBJECTS_ORCLASS error is activated). When removing such an object, the destructor should be called twice for the original object and its copy.
You cannot remove elements from dynamic arrays designated as the indicator buffers by the [SetIndexBuffer()](/en/docs/customind/setindexbuffer) function. This will result in the ERR_SERIES_ARRAY error. For indicator buffers, all size changing operations are performed by the terminal's executing subsystem.
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- declare the fixed-size array and fill in the values
   int array[10];
   for(int i=0;i<10;i++)
     {
      array[i]=i;
     }
//--- display the array before removing the elements
   Print("Before calling ArrayRemove()");
   ArrayPrint(array);
//--- delete 2 elements from the array and display the new set
   ArrayRemove(array,4,2);
   Print("After calling ArrayRemove()");
   ArrayPrint(array);
/*
  Execution result:
  Before calling ArrayRemove()
   0 1 2 3 4 5 6 7 8 9
  After calling ArrayRemove()
   0 1 2 3 6 7 8 9 8 9
*/
```
See also
[ArrayInsert](/en/docs/array/arrayinsert),[ ArrayCopy](/en/docs/array/arraycopy), [ArrayResize](/en/docs/array/arrayresize), [ArrayFree](/en/docs/array/arrayfree)
@@ -0,0 +1,69 @@
# ArrayReverse
Reverses the specified number of elements in the array starting with a specified index.
```
bool  ArrayReverse(
   void&        array[],            // array of any type
   uint         start=0,            // index to start reversing the array from
   uint         count=WHOLE_ARRAY   // number of elements
   );
```
Parameters
array[]
[in][out]  Array.
start=0
[in]  Index the array reversal starts from.
count=WHOLE_ARRAY
[in]  Number of reversed elements. If WHOLE_ARRAY, then all array elements are moved in the inversed manner starting with the specified start index up to the end of the array.
Return Value
Returns true if successful, otherwise - false.
Note
The [ArraySetAsSeries()](/en/docs/array/arraysetasseries) function does not move the array elements physically. Instead, it only changes the indexation direction backwards to arrange the access to the elements as in the [timeseries](/en/docs/series). The ArrayReverse() function physically moves the array elements so that the array is "reversed".
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- declare the fixed-size array and fill in the values
   int array[10];
   for(int i=0;i<10;i++)
     {
      array[i]=i;
     }
//--- display the array before reversing the elements
   Print("Before calling ArrayReverse()");
   ArrayPrint(array);
//--- reverse 3 elements in the array and show the new set
   ArrayReverse(array,4,3);
   Print("After calling ArrayReverse()");
   ArrayPrint(array);
/*
  Execution result:
  Before calling ArrayReverse()
   0 1 2 3 4 5 6 7 8 9
  After calling ArrayReverse()
   0 1 2 3 6 5 4 7 8 9
*/
```
See also
[ArrayInsert](/en/docs/array/arrayinsert), [ArrayRemove](/en/docs/array/arrayremove), [ArrayCopy](/en/docs/array/arraycopy), [ArrayResize](/en/docs/array/arrayresize), [ArrayFree](/en/docs/array/arrayfree), [ArrayGetAsSeries](/en/docs/array/arraygetasseries), [ArraySetAsSeries](/en/docs/array/arraysetasseries)
@@ -0,0 +1,94 @@
# ArraySetAsSeries
The function sets the AS_SERIES flag to a selected [object of a dynamic array](/en/docs/basis/types/dynamic_array), and elements will be indexed like in [timeseries](/en/docs/series).
```
bool  ArraySetAsSeries(
   const void&  array[],    // array by reference
   bool         flag        // true denotes reverse order of indexing
   );
```
Parameters
array[]
[in][out]  Numeric array to set.
flag
[in]  Array indexing direction.
Return Value
The function returns true on success, otherwise  - false.
Note
The [AS_SERIES](/en/docs/array/arraygetasseries) flag can't be set for multi-dimensional arrays or static arrays (arrays, whose size in square brackets is preset already on the compilation stage). Indexing in timeseries differs from a common array in that the elements of timeseries are indexed from the end towards the beginning (from the newest to oldest data).
Example: Indicator that shows bar number
![Indicator for showing bar number](pics/barnumber.png)
```
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//---- plot Numeration
#property indicator_label1  "Numeration"
#property indicator_type1   DRAW_LINE
#property indicator_color1  CLR_NONE
//--- indicator buffers
double         NumerationBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,NumerationBuffer,INDICATOR_DATA);
//--- set indexing for the buffer like in timeseries
   ArraySetAsSeries(NumerationBuffer,true);
//--- set accuracy of showing in DataWindow
   IndicatorSetInteger(INDICATOR_DIGITS,0);
//--- how the name of the indicator array is displayed in DataWindow
   PlotIndexSetString(0,PLOT_LABEL,"Bar #"); 
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---  we'll store the time of the current zero bar opening
   static datetime currentBarTimeOpen=0;
//--- revert access to array time[] - do it like in timeseries
   ArraySetAsSeries(time,true);
//--- If time of zero bar differs from the stored one
   if(currentBarTimeOpen!=time[0])
     {
     //--- enumerate all bars from the current to the chart depth
      for(int i=rates_total-1;i>=0;i--) NumerationBuffer[i]=i;
      currentBarTimeOpen=time[0];
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
```
See also
[Access to timeseries](/en/docs/series), [ArrayGetAsSeries](/en/docs/array/arraygetasseries)
@@ -0,0 +1,71 @@
# ArraySize
The function returns the number of elements of a selected array.
```
int  ArraySize(
   const void&  array[]    // checked array
   );
```
Parameters
array[]
[in]  Array of any type.
Return Value
Value of [int](/en/docs/basis/types/integer/integertypes) type.
Note
For a one-dimensional array, the value to be returned by the ArraySize is equal to that of [ArrayRange](/en/docs/array/arrayrange)(array,0).
Example:
```
void OnStart()
  {
//--- create arrays
   double one_dim[];
   double four_dim[][10][5][2];
//--- sizes
   int one_dim_size=25;
   int reserve=20;
   int four_dim_size=5;
//--- auxiliary variable
   int size;
//--- allocate memory without backup
   ArrayResize(one_dim,one_dim_size);
   ArrayResize(four_dim,four_dim_size);
//--- 1. one-dimensional array
   Print("+==========================================================+");
   Print("Array sizes:");
   Print("1. One-dimensional array");
   size=ArraySize(one_dim);
   PrintFormat("Zero dimension size = %d, Array size = %d",one_dim_size,size);
//--- 2. multidimensional array
   Print("2. Multidimensional array");
   size=ArraySize(four_dim);
   PrintFormat("Zero dimension size = %d, Array size = %d",four_dim_size,size);
//--- dimension sizes
   int d_1=ArrayRange(four_dim,1);
   int d_2=ArrayRange(four_dim,2);
   int d_3=ArrayRange(four_dim,3);
   Print("Check:");
   Print("Zero dimension = Array size / (First dimension * Second dimension * Third dimension)");
   PrintFormat("%d = %d / (%d * %d * %d)",size/(d_1*d_2*d_3),size,d_1,d_2,d_3);
//--- 3. one-dimensional array with memory backup
   Print("3. One-dimensional array with memory backup");
//--- double the value
   one_dim_size*=2;
//--- allocate memory with backup
   ArrayResize(one_dim,one_dim_size,reserve);
//--- print out the size
   size=ArraySize(one_dim);
   PrintFormat("Size with backup = %d, Actual array size = %d",one_dim_size+reserve,size);
  }
```
@@ -0,0 +1,218 @@
# ArraySort
Sorts the values in the first dimension of a multidimensional numeric array in the ascending order.
```
bool  ArraySort(
   void&  array[]      // array for sorting
   );
```
Parameters
array[]
[in][out]  Numeric array for sorting.
Return Value
The function returns true on success, otherwise  - false.
Note
An array is always sorted in the ascending order irrespective of the [AS_SERIES](/en/docs/array/arraygetasseries) flag value.
Functions ArraySort and ArrayBSearch accept any-dimensional arrays as a parameter. However, searching and sorting are always applied to the first (zero) dimension.
Example:
```
#property description "The indicator analyzes data for the last month and draws all candlesticks with small"
#property description "and large tick volumes. The tick volume array is sorted out"
#property description "to define such candlesticks. The candlesticks having the volumes comprising the first InpSmallVolume"
#property description "per cent of the array are considered small. The candlesticks having the tick volumes comprising "
#property description "the last InpBigVolume per cent of the array are considered large."
//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 5
#property indicator_plots   1
//--- plot
#property indicator_label1  "VolumeFactor"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrDodgerBlue,clrOrange
#property indicator_style1  STYLE_SOLID
#property indicator_width1  2
//--- predefined constant
#define INDICATOR_EMPTY_VALUE 0.0
//--- input parameters
input int InpSmallVolume=15; // Percentage value of small volumes (<50)
input int InpBigVolume=20;   // Percentage value of large volumes (<50)
//--- analysis start time (will be shifted)
datetime ExtStartTime;
//--- indicator buffers
double   ExtOpenBuff[];
double   ExtHighBuff[];
double   ExtLowBuff[];
double   ExtCloseBuff[];
double   ExtColorBuff[];
//--- volume boundary values for displaying the candlesticks
long     ExtLeftBorder=0;
long     ExtRightBorder=0;
//+------------------------------------------------------------------+
//| Receive border values for tick volumes                           |
//+------------------------------------------------------------------+
bool GetVolumeBorders(void)
  {
//--- variables
   datetime stop_time;  // copy end time
   long     buff[];     // buffer for copying
//--- end time is the current one
   stop_time=TimeCurrent();
//--- start time is one month earlier from the current one
   ExtStartTime=GetStartTime(stop_time);
//--- receive the values of tick volumes
   ResetLastError();
   if(CopyTickVolume(Symbol(),Period(),ExtStartTime,stop_time,buff)==-1)
     {
      //--- failed to receive the data, return false to launch recalculation command
      PrintFormat("Failed to receive tick volume values. Error code = %d",GetLastError());
      return(false);
     }
//--- calculate array size
   int size=ArraySize(buff);
//--- sort out the array
   ArraySort(buff);
//--- define the values of the left and right border for tick volumes
   ExtLeftBorder=buff[size*InpSmallVolume/100];
   ExtRightBorder=buff[(size-1)*(100-InpBigVolume)/100];
//--- successful execution
   return(true);
  }
//+------------------------------------------------------------------+
//| Receive the data that is one month less than the passed one      |
//+------------------------------------------------------------------+
datetime GetStartTime(const datetime stop_time)
  {
//--- convert end time into MqlDateTime type structure variable
   MqlDateTime temp;
   TimeToStruct(stop_time,temp);
//--- receive the data that is one month less
   if(temp.mon>1)
      temp.mon-=1;  // the current month is not the first one in the year, therefore, the number of the previous one is one less
   else
     {
      temp.mon=12;  // the current month is the first in the year, therefore, the number of the previous one is 12,
      temp.year-=1; // while the year number is one less
     }
//--- day number will not exceed 28
   if(temp.day>28)
      temp.day=28;
//--- return the obtained date
   return(StructToTime(temp));
  }
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- check if input parameters satisfy the conditions
   if(InpSmallVolume<0 || InpSmallVolume>=50 || InpBigVolume<0 || InpBigVolume>=50)
     {
      Print("Incorrect input parameters");
      return(INIT_PARAMETERS_INCORRECT);
     }
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtOpenBuff);
   SetIndexBuffer(1,ExtHighBuff);
   SetIndexBuffer(2,ExtLowBuff);
   SetIndexBuffer(3,ExtCloseBuff);
   SetIndexBuffer(4,ExtColorBuff,INDICATOR_COLOR_INDEX);
//--- set the value that will not be displayed
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,INDICATOR_EMPTY_VALUE);
//--- set labels for indicator buffers
   PlotIndexSetString(0,PLOT_LABEL,"Open;High;Low;Close");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- check if unhandled bars are still present
   if(prev_calculated<rates_total)
     {
      //--- receive new values of the right and left borders for volumes
      if(!GetVolumeBorders())
         return(0);
     }
//--- start variable for bar calculation
   int start=prev_calculated;
//--- work at the last bar if the indicator values have already been calculated at the previous tick
   if(start>0)
      start--;
//--- set direct indexing in time series
   ArraySetAsSeries(time,false);
   ArraySetAsSeries(open,false);
   ArraySetAsSeries(high,false);
   ArraySetAsSeries(low,false);
   ArraySetAsSeries(close,false);
   ArraySetAsSeries(tick_volume,false);
//--- the loop of calculation of the indicator values
   for(int i=start;i<rates_total;i++)
     {
      //--- fill out candlesticks starting from the initial date
      if(ExtStartTime<=time[i])
        {
         //--- if the value is not less than the right border, fill out the candlestick
         if(tick_volume[i]>=ExtRightBorder)
           {
            //--- receive data for drawing the candlestick
            ExtOpenBuff[i]=open[i];
            ExtHighBuff[i]=high[i];
            ExtLowBuff[i]=low[i];
            ExtCloseBuff[i]=close[i];
            //--- DodgerBlue color
            ExtColorBuff[i]=0;
            //--- continue the loop
            continue;
           }
         //--- fill out the candlestick if the value does not exceed the left border
         if(tick_volume[i]<=ExtLeftBorder)
           {
            //--- receive data for drawing the candlestick
            ExtOpenBuff[i]=open[i];
            ExtHighBuff[i]=high[i];
            ExtLowBuff[i]=low[i];
            ExtCloseBuff[i]=close[i];
            //--- Orange color
            ExtColorBuff[i]=1;
            //--- continue the loop
            continue;
           }
        }
      //--- set empty values for bars that have not been included in the calculation
      ExtOpenBuff[i]=INDICATOR_EMPTY_VALUE;
      ExtHighBuff[i]=INDICATOR_EMPTY_VALUE;
      ExtLowBuff[i]=INDICATOR_EMPTY_VALUE;
      ExtCloseBuff[i]=INDICATOR_EMPTY_VALUE;
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
```
See also
[ArrayBsearch](/en/docs/array/arraybsearch)
@@ -0,0 +1,81 @@
# ArraySwap
Swaps the contents of two dynamic arrays of the same type. For multidimensional arrays, the number of elements in all dimensions except the first one should match.
```
bool  ArraySwap(
   void&  array1[],      // first array
   void&  array2[]       // second array
   );
```
Parameters
array1[]
[in][out]  Array of numerical type.
array2[]
[in][out]  Array of numerical type.
Return Value
Returns true if successful, otherwise false. In this case, [GetLastError()](/en/docs/check/getlasterror) returns the [ERR_INVALID_ARRAY](/en/docs/constants/errorswarnings/errorcodes) error code.
Note
The function accepts dynamic arrays of the same type and the same dimensions except the first one. For integer types, the sign is ignored, i.e. [char](/en/docs/basis/types/integer/integertypes)==uchar)
Example:
```
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- arrays for storing quotes
   double source_array[][8];
   double   dest_array[][8];
   MqlRates rates[];
//--- get the data of the last 20 candles on the current timeframe
   int copied=CopyRates(NULL,0,0,20,rates);
   if(copied<=0)
     {
      PrintFormat("CopyRates(%s,0,0,20,rates) failed, error=%d",
                  Symbol(),GetLastError());
      return;
     }
//--- set the array size for the amount of copied data
   ArrayResize(source_array,copied);
//--- fill the rate_array_1[] array by data from rates[]
   for(int i=0;i<copied;i++)
     {
      source_array[i][0]=(double)rates[i].time;
      source_array[i][1]=rates[i].open;
      source_array[i][2]=rates[i].high;
      source_array[i][3]=rates[i].low;
      source_array[i][4]=rates[i].close;
      source_array[i][5]=(double)rates[i].tick_volume;
      source_array[i][6]=(double)rates[i].spread;
      source_array[i][7]=(double)rates[i].real_volume;
     }
//--- swap data between source_array[] and dest_array[]
   if(!ArraySwap(source_array,dest_array))
     {
      PrintFormat("ArraySwap(source_array,rate_array_2) failed, error code=%d",GetLastError());
      return;
     }
//--- ensure that the source array has become zero after the swap
   PrintFormat("ArraySwap() done: ArraySize(source_array)=%d",ArraySize(source_array));
//--- display the data of the dest_array[] destination array
   ArrayPrint(dest_array);
  }
```
See also
[ArrayCopy](/en/docs/array/arraycopy), [ArrayFill](/en/docs/array/arrayfill), [ArrayRange](/en/docs/array/arrayrange), [ArrayIsDynamic](/en/docs/array/arrayisdynamic)
@@ -0,0 +1,114 @@
# ArrayToFP16
Copies an array of type float or double into an array of type [ushort](/en/docs/basis/types/integer/integertypes#ushort) with the given format.
```
bool   ArrayToFP16(
   const ushort&        dst_array[],        // copy to
   const float&         src_array[],        // copy from
   ENUM_FLOAT16_FORMAT  fmt                 // format
   );
```
Overloading for the double type
```
bool   ArrayToFP16(
   const ushort&        dst_array[],        // copy to
   const double&        src_array[],        // copy from
   ENUM_FLOAT16_FORMAT  fmt                 // format
   );
```
Parameters
dst_array[]
[out]  Receiver array or type ushort.
src_array[]
[in]  Source array of type float or double.
fmt
[in]  Copying format from the [ENUM_FLOAT16_FORMAT](/en/docs/onnx/onnx_structures#enum_float16_format) enumeration.
Return Value
Returns true if successful or false otherwise.
Note
Formats FLOAT16 and BFLOAT16 are defined in the [ENUM_FLOAT16_FORMAT](/en/docs/onnx/onnx_structures#enum_float16_format) enumeration and are used in MQL5 only for operations with [ONNX models](/en/docs/onnx).
The function converts input parameters of type float or double to type FLOAT16 and BFLOAT16. These input parameters are then used in the [OnnxRun](/en/docs/onnx/onnxrun) function.
FLOAT16, also known as [half-precision float](https://en.wikipedia.org/wiki/Half-precision_floating-point_format), uses 16 bits to represent floating-point numbers. This format provides a balance between accuracy and computational efficiency. FLOAT16 is widely used in deep learning algorithms and neural networks, which require high-performance processing of large datasets. This format accelerates computations calculations by reducing the size of numbers, which is especially important when training deep neural networks on GPUs.
BFLOAT16 (or [Brain Floating Point 16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format)) also uses 16 bits but differs from FLOAT16 in the approach to format representation. In this format, 8 bits are allocated for representing the exponent, while the remaining 7 bits are used for representing the mantissa. This format was developed for use in deep learning and artificial intelligence, especially in Google's Tensor Processing Unit (TPU). BFLOAT16 demonstrates excellent performance in neural network training and can effectively accelerate computations.
Example: function from the article [Working with ONNX models in float16 and float8 formats ](https://www.mql5.com/ru/articles/14330)
```
//+------------------------------------------------------------------+
//| RunCastFloat16ToDouble                                           |
//+------------------------------------------------------------------+
bool RunCastFloat16ToDouble(long model_handle)
  {
   PrintFormat("test=%s",__FUNCTION__);
   double test_data[12]= {1,2,3,4,5,6,7,8,9,10,11,12};
   ushort data_uint16[12];
   if(!ArrayToFP16(data_uint16,test_data,FLOAT_FP16))
     {
      Print("error in ArrayToFP16. error code=",GetLastError());
      return(false);
     }
   Print("test array:");
   ArrayPrint(test_data);
   Print("ArrayToFP16:");
   ArrayPrint(data_uint16);
   U<ushort> input_float16_values[3*4];
   U<double> output_double_values[3*4];
   float test_data_float[];
   if(!ArrayFromFP16(test_data_float,data_uint16,FLOAT_FP16))
     {
      Print("error in ArrayFromFP16. error code=",GetLastError());
      return(false);
     }
   for(int i=0; i<12; i++)
     {
      input_float16_values[i].value=data_uint16[i];
      PrintFormat("%d input value =%f  Hex float16 = %s  ushort value=%d",i,test_data_float[i],ArrayToString(input_float16_values[i].uc),input_float16_values[i].value);
     }
   Print("ONNX input array:");
   ArrayPrint(input_float16_values);
   bool res=OnnxRun(model_handle,ONNX_NO_CONVERSION,input_float16_values,output_double_values);
   if(!res)
     {
      PrintFormat("error in OnnxRun. error code=%d",GetLastError());
      return(false);
     }
   Print("ONNX output array:");
   ArrayPrint(output_double_values);
//---
   double sum_error=0.0;
   for(int i=0; i<12; i++)
     {
      double delta=test_data[i]-output_double_values[i].value;
      sum_error+=MathAbs(delta);
      PrintFormat("%d output double %f = %s  difference=%f",i,output_double_values[i].value,ArrayToString(output_double_values[i].uc),delta);
     }
//---
   PrintFormat("test=%s   sum_error=%f",__FUNCTION__,sum_error);
//---
   return(true);
  }
```
See also
[ArrayFromFP16](/en/docs/array/arrayfromfp16), [ArrayCopy](/en/docs/array/arraycopy)
@@ -0,0 +1,112 @@
# ArrayToFP8
Copies an array of type float or double into an array of type [uchar](/en/docs/basis/types/integer/integertypes#uchar) with the given format.
```
bool   ArrayToFP8(
   const uchar&         dst_array[],        // copy to
   const float&         src_array[],        // copy from
   ENUM_FLOAT8_FORMAT   fmt                 // format
   );
```
Overloading for the double type
```
bool   ArrayToFP8(
   const uchar&         dst_array[],        // copy to
   const double&        src_array[],        // copy from
   ENUM_FLOAT8_FORMAT   fmt                 // format
   );
```
Parameters
dst_array[]
[out]  Receiver array or type uchar.
src_array[]
[in]  Source array of type float or double.
fmt
[in]  Copying format from the [ENUM_FLOAT8_FORMAT](/en/docs/onnx/onnx_structures#enum_float8_format) enumeration.
Return Value
Returns true if successful or false otherwise.
Note
All kinds of FP8 format are defined in the [ENUM_FLOAT8_FORMAT](/en/docs/onnx/onnx_structures#enum_float8_format) enumeration and are used in MQL5 only for operations with [ONNX models](/en/docs/onnx).
The function converts input parameters of type float or double into one of FP8 types. These input parameters are then used in the [OnnxRun](/en/docs/onnx/onnxrun) function.
FP8 (8-bit floating point) is one of the data types used to represent floating point numbers. In FP8, each number is represented by 8 data bits, typically divided into three components: sign, exponent and mantissa. This format offers a balance between accuracy and storage efficiency, making it attractive for applications that require memory and computational efficiency.
By employing compact number representation, FP8 reduces memory requirements and accelerates calculations. In addition, FP8 can be useful for implementing low-level operations such as arithmetic calculations and signal processing.
Example: function from the article [Working with ONNX models in float16 and float8 formats ](https://www.mql5.com/ru/articles/14330)
```
//+------------------------------------------------------------------+
//| RunCastFloat8Float                                               |
//+------------------------------------------------------------------+
bool RunCastFloat8ToFloat(long model_handle,const ENUM_FLOAT8_FORMAT fmt)
  {
   PrintFormat("TEST: %s(%s)",__FUNCTION__,EnumToString(fmt));
//---
   float test_data[15]   = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
   uchar data_float8[15] = {};
   if(!ArrayToFP8(data_float8,test_data,fmt))
     {
      Print("error in ArrayToFP8. error code=",GetLastError());
      OnnxRelease(model_handle);
      return(false);
     }
   U<uchar> input_float8_values[3*5];
   U<float> output_float_values[3*5];
   float    test_data_float[];
//--- convert float8 to float
   if(!ArrayFromFP8(test_data_float,data_float8,fmt))
     {
      Print("error in ArrayFromFP8. error code=",GetLastError());
      OnnxRelease(model_handle);
      return(false);
     }
   for(uint i=0; i<data_float8.Size(); i++)
     {
      input_float8_values[i].value=data_float8[i];
      PrintFormat("%d input value =%f  Hex float8 = %s  ushort value=%d",i,test_data_float[i],ArrayToHexString(input_float8_values[i].uc),input_float8_values[i].value);
     }
   Print("ONNX input array: ",ArrayToString(input_float8_values));
//--- execute model (convert float8 to float using ONNX)
   if(!OnnxRun(model_handle,ONNX_NO_CONVERSION,input_float8_values,output_float_values))
     {
      PrintFormat("error in OnnxRun. error code=%d",GetLastError());
      OnnxRelease(model_handle);
      return(false);
     }
   Print("ONNX output array: ",ArrayToString(output_float_values));
//--- calculate error (compare ONNX and ArrayFromFP8 results)
   double sum_error=0.0;
   for(uint i=0; i<test_data.Size(); i++)
     {
      double delta=test_data_float[i]-(double)output_float_values[i].value;
      sum_error+=MathAbs(delta);
      PrintFormat("%d output float %f = %s difference=%f",i,output_float_values[i].value,ArrayToHexString(output_float_values[i].uc),delta);
     }
//---
   PrintFormat("%s(%s): sum_error=%f\n",__FUNCTION__,EnumToString(fmt),sum_error);
   return(true);
  }
```
See also
[ArrayFromFP8](/en/docs/array/arrayfromfp8), [ArrayCopy](/en/docs/array/arraycopy)
@@ -0,0 +1,114 @@
# ArrayFromFP16
Copies an array of type [ushort](/en/docs/basis/types/integer/integertypes#ushort) into an array of float or double type with the given format.
```
bool   ArrayFromFP16(
   const float&         dst_array[],        // copy to
   const ushort&        src_array[],        // copy from
   ENUM_FLOAT16_FORMAT  fmt                 // format
   );
```
Overloading for the double type
```
bool   ArrayFromFP16(
   const double&        dst_array[],        // copy to
   const ushort&        src_array[],        // copy from
   ENUM_FLOAT16_FORMAT  fmt                 // format
   );
```
Parameters
dst_array[]
[out]  Receiver array of type float or double.
src_array[]
[in]  Source array of type ushort.
fmt
[in]  Copying format from the [ENUM_FLOAT16_FORMAT](/en/docs/onnx/onnx_structures#enum_float16_format) enumeration.
Return Value
Returns true if successful or false otherwise.
Note
Formats FLOAT16 and BFLOAT16 are defined in the [ENUM_FLOAT16_FORMAT](/en/docs/onnx/onnx_structures#enum_float16_format) enumeration and are used in MQL5 only for operations with [ONNX models](/en/docs/onnx).
If the output parameters obtained from the [OnnxRun](/en/docs/onnx/onnxrun) function execution are of type FLOAT16 and BFLOAT16, you can use this function to convert the result to float or double arrays.
FLOAT16, also known as [half-precision float](https://en.wikipedia.org/wiki/Half-precision_floating-point_format), uses 16 bits to represent floating-point numbers. This format provides a balance between accuracy and computational efficiency. FLOAT16 is widely used in deep learning algorithms and neural networks, which require high-performance processing of large datasets. This format accelerates computations calculations by reducing the size of numbers, which is especially important when training deep neural networks on GPUs.
BFLOAT16 (or [Brain Floating Point 16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format)) also uses 16 bits but differs from FLOAT16 in the approach to format representation. In this format, 8 bits are allocated for representing the exponent, while the remaining 7 bits are used for representing the mantissa. This format was developed for use in deep learning and artificial intelligence, especially in Google's Tensor Processing Unit (TPU). BFLOAT16 demonstrates excellent performance in neural network training and can effectively accelerate computations.
Example: function from the article [Working with ONNX models in float16 and float8 formats ](https://www.mql5.com/ru/articles/14330)
```
//+------------------------------------------------------------------+
//| RunCastFloat16ToDouble                                           |
//+------------------------------------------------------------------+
bool RunCastFloat16ToDouble(long model_handle)
  {
   PrintFormat("test=%s",__FUNCTION__);
   double test_data[12]= {1,2,3,4,5,6,7,8,9,10,11,12};
   ushort data_uint16[12];
   if(!ArrayToFP16(data_uint16,test_data,FLOAT_FP16))
     {
      Print("error in ArrayToFP16. error code=",GetLastError());
      return(false);
     }
   Print("test array:");
   ArrayPrint(test_data);
   Print("ArrayToFP16:");
   ArrayPrint(data_uint16);
   U<ushort> input_float16_values[3*4];
   U<double> output_double_values[3*4];
   float test_data_float[];
   if(!ArrayFromFP16(test_data_float,data_uint16,FLOAT_FP16))
     {
      Print("error in ArrayFromFP16. error code=",GetLastError());
      return(false);
     }
   for(int i=0; i<12; i++)
     {
      input_float16_values[i].value=data_uint16[i];
      PrintFormat("%d input value =%f  Hex float16 = %s  ushort value=%d",i,test_data_float[i],ArrayToString(input_float16_values[i].uc),input_float16_values[i].value);
     }
   Print("ONNX input array:");
   ArrayPrint(input_float16_values);
   bool res=OnnxRun(model_handle,ONNX_NO_CONVERSION,input_float16_values,output_double_values);
   if(!res)
     {
      PrintFormat("error in OnnxRun. error code=%d",GetLastError());
      return(false);
     }
   Print("ONNX output array:");
   ArrayPrint(output_double_values);
//---
   double sum_error=0.0;
   for(int i=0; i<12; i++)
     {
      double delta=test_data[i]-output_double_values[i].value;
      sum_error+=MathAbs(delta);
      PrintFormat("%d output double %f = %s  difference=%f",i,output_double_values[i].value,ArrayToString(output_double_values[i].uc),delta);
     }
//---
   PrintFormat("test=%s   sum_error=%f",__FUNCTION__,sum_error);
//---
   return(true);
  }
```
See also
[ArrayToFP16](/en/docs/array/arraytofp16), [ArrayCopy](/en/docs/array/arraycopy)
@@ -0,0 +1,112 @@
# ArrayFromFP8
Copies an array of type [uchar](/en/docs/basis/types/integer/integertypes#uchar) into an array of float or double type with the given format.
```
bool   ArrayFromFP8(
   const float&         dst_array[],        // copy to
   const uchar&         src_array[],        // copy from
   ENUM_FLOAT8_FORMAT   fmt                 // format
   );
```
Overloading for the double type
```
bool   ArrayFromFP8(
   const double&        dst_array[],        // copy to
   const uchar&         src_array[],        // copy from
   ENUM_FLOAT8_FORMAT   fmt                 // format
   );
```
Parameters
dst_array[]
[out]  Receiver array of type float or double.
src_array[]
[in]  Source array of type uchar.
fmt
[in]  Copying format from the [ENUM_FLOAT8_FORMAT](/en/docs/onnx/onnx_structures#enum_float8_format) enumeration.
Return Value
Returns true if successful or false otherwise.
Note
All kinds of FP8 format are defined in the [ENUM_FLOAT8_FORMAT](/en/docs/onnx/onnx_structures#enum_float8_format) enumeration and are used in MQL5 only for operations with [ONNX models](/en/docs/onnx).
If the output parameters obtained from the [OnnxRun](/en/docs/onnx/onnxrun) function execution are of FP8 from the ENUM_FLOAT8_FORMAT enumeration, you can use this function to convert the result into float or double arrays.
FP8 (8-bit floating point) is one of the data types used to represent floating point numbers. In FP8, each number is represented by 8 data bits, typically divided into three components: sign, exponent and mantissa. This format offers a balance between accuracy and storage efficiency, making it attractive for applications that require memory and computational efficiency.
By employing compact number representation, FP8 reduces memory requirements and accelerates calculations. In addition, FP8 can be useful for implementing low-level operations such as arithmetic calculations and signal processing.
Example: function from the article [Working with ONNX models in float16 and float8 formats ](https://www.mql5.com/ru/articles/14330)
```
//+------------------------------------------------------------------+
//| RunCastFloat8Float                                               |
//+------------------------------------------------------------------+
bool RunCastFloat8ToFloat(long model_handle,const ENUM_FLOAT8_FORMAT fmt)
  {
   PrintFormat("TEST: %s(%s)",__FUNCTION__,EnumToString(fmt));
//---
   float test_data[15]   = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
   uchar data_float8[15] = {};
   if(!ArrayToFP8(data_float8,test_data,fmt))
     {
      Print("error in ArrayToFP8. error code=",GetLastError());
      OnnxRelease(model_handle);
      return(false);
     }
   U<uchar> input_float8_values[3*5];
   U<float> output_float_values[3*5];
   float    test_data_float[];
//--- convert float8 to float
   if(!ArrayFromFP8(test_data_float,data_float8,fmt))
     {
      Print("error in ArrayFromFP8. error code=",GetLastError());
      OnnxRelease(model_handle);
      return(false);
     }
   for(uint i=0; i<data_float8.Size(); i++)
     {
      input_float8_values[i].value=data_float8[i];
      PrintFormat("%d input value =%f  Hex float8 = %s  ushort value=%d",i,test_data_float[i],ArrayToHexString(input_float8_values[i].uc),input_float8_values[i].value);
     }
   Print("ONNX input array: ",ArrayToString(input_float8_values));
//--- execute model (convert float8 to float using ONNX)
   if(!OnnxRun(model_handle,ONNX_NO_CONVERSION,input_float8_values,output_float_values))
     {
      PrintFormat("error in OnnxRun. error code=%d",GetLastError());
      OnnxRelease(model_handle);
      return(false);
     }
   Print("ONNX output array: ",ArrayToString(output_float_values));
//--- calculate error (compare ONNX and ArrayFromFP8 results)
   double sum_error=0.0;
   for(uint i=0; i<test_data.Size(); i++)
     {
      double delta=test_data_float[i]-(double)output_float_values[i].value;
      sum_error+=MathAbs(delta);
      PrintFormat("%d output float %f = %s difference=%f",i,output_float_values[i].value,ArrayToHexString(output_float_values[i].uc),delta);
     }
//---
   PrintFormat("%s(%s): sum_error=%f\n",__FUNCTION__,EnumToString(fmt),sum_error);
   return(true);
  }
```
See also
[ArrayToFP8](/en/docs/array/arraytofp8), [ArrayCopy](/en/docs/array/arraycopy)
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB