Elder-Ray (Bulls Power and Bears Power)
Introduction
Elder-Ray trading system was described by Alexander Elder in his book "Trading for a Living". It is based on Bulls Power and Bears Power oscillators, as well as Moving Average trend-following indicator (EMA — exponential averaging).
The system is both simple and complex:
- it is simple if we perceive it literally: buy if a trend is upwards (EMA) and Bears Power is below zero, but is increasing;
- it is complex, if we read more carefully and also look at the chart EMA and Bears Power are launched at: it turns out, it is not as simple as it seems since such conditions are few.
In this article, we will go through all the stages from simple to complex and check two types of trading system:
- all indicators on the same chart (and therefore, on a single timeframe);
- in combination with the "Triple choice" system.
The EAs in the article are meant for working with netting accounts only.
Key points
To grasp the idea behind the trading system, it is necessary to understand each Elder-Ray element: price, EMA, highs and lows of Bulls Power and Bears Power indicators at each bar, as well as the strength of bulls and bears.
- Price — current agreement on the value of an asset. All buys are made while expecting the price rise, while all sells are made in anticipation of the price fall. A dealer can be performed only when there are eager buyers and sellers.
- EMA — exponential moving average. It reflects the average agreement on an asset value for a certain period. For example, EMA(13) on D1 is an average asset value agreement for the last 13 days. Why is it better to use an exponential rather than a simple moving average? A. Elder answered this question in section 4.2 ("Moving Averages") of his book. In short, EMA is more sensitive to trend changes as compared to a simple average.
- Bulls Power maximum shows the maximum bulls strength on a given bar. When the price grows, bulls earn profit, therefore bulls buy till the price reaches the maximum level. Bulls Power maximum is a moment when bulls want to move the price higher, but they do not have money anymore.
- Bears Power minimum shows the maximum bears strength on a given bar. Bears make profit when the price goes down and, therefore, sell till the price reaches its minimum. Bears Power minimum is a moment when bears want to move the price further down but are no more capable of doing that.
- Bulls Power shows the ability of bulls to raise the price above the average agreement on the asset value. As a rule, the strength of bulls is above zero. If it is below zero, then this means the bulls are in panic and are about to lose their power.
- Bears Power reflects the ability of bears to lower the price below the average agreement on the asset value. Usually, the strength of bears is below zero. If it is above zero, then bulls are too strong, and bears are about to lose their power.
Option 1: All indicators on a single chart
We will explore futures and stocks on the D1 timeframe. All three indicators (Bulls Power, Bears Power and EMA) are placed on a single chart. The averaging period of all indicators is 13.
Buy rules
- trend goes up (according to EMA);
- Bears Power is below zero but goes up;
- pending Buy stop order is located above the maximum of the last two days, while protective stop loss is placed below the last minimum.
CAT, Daily Buy signals
Sell rules
- trend goes down (according to EMA);
- Bulls Power is above zero but goes down;
- pending Sell stop order is located below the minimum of the last two days, while protective stop loss is placed above the last maximum.
CAT, Daily Sell signals
Trading rules
Looking at Figures 1 and 2, we can see that in the option "All indicators on a single chart", the buy and sell rules are triggered at roll-backs on a stable trend. There are quite a few such favorable moments, especially since the analyzed timeframe is D1. Therefore, in the option "All indicators on a single chart", we need to analyze a very large number of instruments to increase the frequency of transactions on a trading account.
D1 charts also have a considerable advantage: analysis of EMA slope, as well as Bulls Power and Bears Power indicators, can be performed only once a day — when a new bar appears. This is exactly how the EA is to work: wait for a new bar on D1 at each specified symbol and look for possible entries after that.
Since futures and stocks are traded only in netting mode, oppositely directed positions (hedging) cannot be applied here, but we can increase position volume. The EA can trade on the current symbol only or on several symbols stored in the text file. While all is clear with the current symbol, selecting several symbols may present the following issues:
- we need to specify about a hundred symbols of one market (for example, securities only);
- we need to specify a lot of symbols from different markets (for example, futures and securities).
How to select all symbols from one market? Suppose, we have "CAT" symbol located at "Stock Markets\USA\NYSE/NASDAQ(SnP100)\CAT"
Suppose that this symbol suits us, and we want to select all other instruments from the "\NYSE/NASDAQ(SnP100)\" branch. In this case, we can do the following:
- open the chart of this symbol;
- launch the script (let's name it Symbols on the specified path.mq5) that will receive the symbol path (in the above example, for "CAT" symbol, it is "Stock Markets\USA\NYSE/NASDAQ(SnP100)") and save all symbols from the obtained path to the text file. The text file is to be saved to the Common Data Folder;
- it only remains to set the text file name in the EA settings.
Implementation of the Symbols on the specified path.mq5 script is to be described below.
Assembling the EA. Option 1: All indicators on a single chart
Symbols on the specified path.mq5 — script to be used for obtaining the text file with the symbols.
NOTE: only the text "Everything is fine. There are no errors" in the Experts tab guarantees that the script's work has been successful, and the obtained file with symbols can be used for the EA operation!
To shorten the code of file operations, the CFileTxt class is connected, and the work with the text file is performed by m_file_txt — CFileTxt class object. The script performs its work in seven steps:
//+------------------------------------------------------------------+ //| Symbols on the specified path.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.002" #property script_show_inputs //--- #include <Files\FileTxt.mqh> CFileTxt m_file_txt; // file txt object //--- input parameters input string InpFileName="Enter a unique name.txt"; // File name //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- STEP 1 string current_path=""; if(!SymbolInfoString(Symbol(),SYMBOL_PATH,current_path)) { Print("ERROR: SYMBOL_PATH"); return; } //--- STEP 2 string sep_="\\"; // A separator as a character ushort u_sep_; // The code of the separator character string result_[]; // An array to get strings //--- Get the separator code u_sep_=StringGetCharacter(sep_,0); //--- Split the string to substrings int k_=StringSplit(current_path,u_sep_,result_); //--- STEP 3 //--- Now output all obtained strings if(k_>0) { current_path=""; for(int i=0;i<k_-1;i++) current_path=current_path+result_[i]+sep_; } //--- STEP 4 string symbols_array[]; int symbols_total=SymbolsTotal(false); for(int i=0;i<symbols_total;i++) { string symbol_name=SymbolName(i,false); string symbol_path=""; if(!SymbolInfoString(symbol_name,SYMBOL_PATH,symbol_path)) continue; if(StringFind(symbol_path,current_path,0)==-1) continue; int size=ArraySize(symbols_array); ArrayResize(symbols_array,size+1,10); symbols_array[size]=symbol_name]; } //--- STEP 5 int size=ArraySize(symbols_array); if(size==0) { PrintFormat("ERROR: On path \"%s\" %d symbols",current_path,size); return; } PrintFormat("On path \"%s\" %d symbols",current_path,size); //--- STEP 6 if(m_file_txt.Open(InpFileName,FILE_WRITE|FILE_COMMON)==INVALID_HANDLE) { PrintFormat("ERROR: \"%s\" file in the Data Folder Common folder is not created",InpFileName); return; } //--- STEP 7 for(int i=0;i<size;i++) m_file_txt.WriteString(symbols_array[i]+"\r\n"); m_file_txt.Close(); Print("Everything is fine. There are no errors"); //--- } //+------------------------------------------------------------------+
Script operation algorithm:
- STEP 1: SYMBOL_PATH (path in the symbols tree) is defined for the current symbol;
- STEP 2: the obtained path is divided into substrings with the "\" separator;
- STEP 3: re-assemble the current path without the last substring, since it contains the symbol name;
- STEP 4: loop through all available symbols; if the symbol's path in the symbols tree matches the current one, select the symbol name and add it to the detected symbols array;
- STEP 5: check the size of the detected symbols array;
- STEP 6: create the file;
- STEP 7: write our array of detected symbols to the file and close it.
Elder-Ray 1 — EA (or several EAs) with 1.xxx version numbers to be traded according to the option 1: all indicators on a single chart.
How to set the position volume — the minimum lot may be different
Let's conduct a simple experiment: check the minimum lot size of futures and securities: go through all symbols located at the same path as the current one (similar to the Symbols on the specified path.mq5 script), but inside saving symbols to the file, display statistics on the minimum lot size.
Gets minimal volume.mq5 — script used to display statistics on the minimum volume of the symbol group. The script bypasses the symbol group and accumulates statistics (minimal volume to close a deal and counter) in the two-dimensional array:
//--- STEP 4 /* symbols_array[][2]: [*][minimal volume to close a deal] [*][counter] */
Full script code:
//+------------------------------------------------------------------+ //| Gets minimal volume.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- STEP 1 string current_path=""; if(!SymbolInfoString(Symbol(),SYMBOL_PATH,current_path)) { Print("ERROR: SYMBOL_PATH"); return; } //--- STEP 2 string sep_="\\"; // A separator as a character ushort u_sep_; // The code of the separator character string result_[]; // An array to get strings //--- Get the separator code u_sep_=StringGetCharacter(sep_,0); //--- Split the string to substrings int k_=StringSplit(current_path,u_sep_,result_); //--- STEP 3 //--- Now output all obtained strings if(k_>0) { current_path=""; for(int i=0;i<k_-1;i++) current_path=current_path+result_[i]+sep_; } //--- STEP 4 /* symbols_array[][2]: [*][minimal volume to close a deal] [*][counter] */ double symbols_array[][2]; int symbols_total=SymbolsTotal(false); for(int i=0;i<symbols_total;i++) { string symbol_name=SymbolName(i,false); string symbol_path=""; if(!SymbolInfoString(symbol_name,SYMBOL_PATH,symbol_path)) continue; if(StringFind(symbol_path,current_path,0)==-1) continue; double min_volume=0.0; if(!SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_MIN,min_volume)) continue; int size=ArrayRange(symbols_array,0); bool found=false; for(int j=0;j<size;j++) { if(symbols_array[j][0]==min_volume) { symbols_array[j][1]=symbols_array[j][1]+1; found=true; continue; } } if(!found) { ArrayResize(symbols_array,size+1,10); symbols_array[size][0]=min_volume; symbols_array[size][1]=1.0; } } //--- STEP 5 int size=ArrayRange(symbols_array,0); if(size==0) { PrintFormat("ERROR: On path \"%s\" %d symbols",current_path,size); return; } //--- STEP 6 for(int i=0;i<size;i++) PrintFormat("Minimal volume %.2f occurs %.1f times",symbols_array[i][0],symbols_array[i][1]); Print("Everything is fine. There are no errors"); //--- } //+------------------------------------------------------------------+
Script operation algorithm:
- STEP 1: SYMBOL_PATH (path in the symbols tree) is defined for the current symbol;
- STEP 2: the obtained path is divided into substrings with the "\" separator;
- STEP 3: re-assemble the current path without the last substring, since it contains the symbol name;
- STEP 4: loop through all available symbols; if the symbol's path in the symbols tree matches the current one, get the minimum symbol volume and perform a search in the symbols array. If such a value is already present, increase the counter. If there is no such value, add to the array and set the counter to "1.0";
- STEP 5: check the size of the detected symbols array;
- STEP 6: display statistics.
The result of launching on securities:
Gets minimal volume (CAT,D1) Minimal volume 1.00 occurs 100.0 times
and on futures:
Gets minimal volume (RTSRIU8,D1) Minimal volume 1.00 occurs 77.0 times
- on the two markets, the lot size is the same — 1.0.
Thus, let's not over-complicate the system and set "1.0" as the minimum lot.
Visualizing applied indicators
When you launch a visualized test in the tester, you can see the indicators applied by the EA. But when the EA is launched on the terminal chart, the indicators are not displayed. In this trading system, I want to see these indicators on the chart for visual control of the EA's work. It should look like this:
As you can see, here I used custom color and line width settings for all indicators (of course, this was done manually). For auto visualization of the indicators applied on the terminal chart, we need to slightly re-write Moving Average, Bulls Power and Bears Power indicators. I already implemented a similar thing in the Custom Moving Average Input Color code — the indicator color was included into the inputs: this input parameter is available when creating an indicator from an EA. Now, we just need to develop three more similar indicators.
You can download these indicators (Custom Moving Average Inputs, Custom Bulls Power Inputs and Custom Bears Power Inputs) in CodeBase. Place the downloaded indicators to the root of [data folder]\MQL5\Indicators\.
Elder-Ray 1.001.mq5 — visualizing the applied indicators you can set the color and width for. It works both in the strategy tester and when launching the EA on the chart:
How is this implemented?
The main condition is the presence of the Custom Moving Average Inputs, Custom Bulls Power Inputs and Custom Bears Power Inputs indicators in [data folder]\MQL5\Indicators\
The indicators' look is managed and the period is set in the inputs, while for working with the indicators, three variables the indicator handles are to be stored in are declared (handle_iCustom_MA, handle_iCustom_Bulls and handle_iCustom_Bears).
//+------------------------------------------------------------------+ //| Elder-Ray 1.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" //--- //--- enum ENUM_INPUT_SYMBOLS { INPUT_SYMBOLS_CURRENT=0, // current symbol INPUT_SYMBOLS_FILE=1, // text file }; //--- input parameters input ENUM_INPUT_SYMBOLS InpInputSymbol = INPUT_SYMBOLS_FILE; // works on ... input uint InpNumberMinLots = 1; // Number of minimum lots //--- Custom Moving Average Inputs input int Inp_MA_ma_period = 13; // MA: averaging period input int Inp_MA_ma_shift = 0; // MA: horizontal shift input ENUM_MA_METHOD Inp_MA_ma_method = MODE_EMA; // MA: smoothing type input ENUM_APPLIED_PRICE Inp_MA_applied_price = PRICE_CLOSE; // MA: type of price input color Inp_MA_Color = clrChartreuse; // MA: Color input int Inp_MA_Width = 2; // MA: Width //--- Custom Bulls Power Inputs input int Inp_Bulls_ma_period = 13; // Bulls Power: averaging period input color Inp_Bulls_Color = clrBlue; // Bulls Power: Color input int Inp_Bulls_Width = 2; // Bulls Power: Width //--- Custom Bears Power Inputs input int Inp_Bears_ma_period = 13; // Bears Power: averaging period input color Inp_Bears_Color = clrRed; // Bears Power: Color input int Inp_Bears_Width = 2; // Bears Power: Width int handle_iCustom_MA; // variable for storing the handle of the iCustom indicator int handle_iCustom_Bulls; // variable for storing the handle of the iCustom indicator int handle_iCustom_Bears; // variable for storing the handle of the iCustom indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit()
In OnInit(), we create handles of custom indicators (iCustom is applied), and created indicators are added to the chart (ChartIndicatorAdd is applied).
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create handle of the indicator iCustom handle_iCustom_MA=iCustom(Symbol(),Period(),"Custom Moving Average Inputs", Inp_MA_ma_period, Inp_MA_ma_shift, Inp_MA_ma_method, Inp_MA_Color, Inp_MA_Width, Inp_MA_applied_price); //--- if the handle is not created if(handle_iCustom_MA==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iCustom indicator (\"Custom Moving Average Inputs\") for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //--- create handle of the indicator iCustom handle_iCustom_Bulls=iCustom(Symbol(),Period(),"Custom Bulls Power Inputs", Inp_Bulls_ma_period, Inp_Bulls_Color, Inp_Bulls_Width); //--- if the handle is not created if(handle_iCustom_Bulls==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iCustom indicator (\"Custom Bulls Power Inputs\") for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } //--- create handle of the indicator iCustom handle_iCustom_Bears=iCustom(Symbol(),Period(),"Custom Bears Power Inputs", Inp_Bears_ma_period, Inp_Bears_Color, Inp_Bears_Width); //--- if the handle is not created if(handle_iCustom_Bears==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iCustom indicator (\"Custom Bears Power Inputs\") for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- the indicator is stopped early return(INIT_FAILED); } ChartIndicatorAdd(0,0,handle_iCustom_MA); int windows_total=(int)ChartGetInteger(0,CHART_WINDOWS_TOTAL); ChartIndicatorAdd(0,windows_total,handle_iCustom_Bulls); ChartIndicatorAdd(0,windows_total+1,handle_iCustom_Bears); //--- return(INIT_SUCCEEDED); }
Saving resources. Elder-Ray 1.010.mq5
There may be about a hundred analyzed symbols of a single group. Thus, the issue of saving RAM becomes relevant, since each chart is to contain three indicators. Like with a minimum lot, the best thing to do is to check the resource consumption by the EA. At the same time, we will also make a little progress in assembling our EA by adding the code for reading symbol names of one group from the text file and working with them.
The CFileTxt class (we already applied it in the script Symbols on the specified path.mq5) is included to the EA. Its m_file_txt object is responsible for accessing the text file and reading data from the file. We also include the CSymbolInfo class. Its object m_symbol is responsible for checking the existence of a symbol and adding it to the Market Watch window. Why did I choose CSymbolInfo, rather than implementing the function via SymbolInfoInteger and SymbolSelect? All is simple: in the CSymbolInfo class, all the code for checking, adding or informing of errors is hidden inside the class, and we need only to add the following three strings in the EA:
if(!m_symbol.Name(name)) // sets symbol name { m_file_txt.Close(); return(INIT_FAILED); }
Here we should recall that all symbols of a multi-currency EA should be added to the Market Watch window: Enable required symbols in Market Watch for multi-currency Expert Advisors.
Thus, the EA works according to the following algorithm: a text file is opened in OnInit(), a symbol is read and an attempt is immediately made to create three custom indicators (Custom Moving Average Inputs, Custom Bulls Power Inputs and Custom Bears Power Inputs) for the read symbol on the current timeframe. If that attempt fails (for example, due to insufficient number of bars for creating Custom Moving Average Inputs), move on further along the loop. If the indicators are created, write the symbol name to m_symbols_array, while the handles of three indicators are sent to the three-dimensional m_handles_array. Thus, by the first dimension, both arrays synchronously contain data on the symbol name and symbol's handles:
//--- if(m_file_txt.Open(InpFileName,FILE_READ|FILE_COMMON)==INVALID_HANDLE) { PrintFormat("ERROR: \"%s\" file in the Data Folder Common folder is not open: %d",InpFileName,GetLastError()); return(INIT_FAILED); } //--- symbol info object OR file txt object if(InpInputSymbol==INPUT_SYMBOLS_FILE) { //--- read data from the file int counter=0; while(!m_file_txt.IsEnding()) { counter++; Print("Iteration ",counter); string name=m_file_txt.ReadString(); if(!m_symbol.Name(name)) // sets symbol name { m_file_txt.Close(); return(INIT_FAILED); } int MA,Bulls,Bears; if(!CreateHandles(name,Period(),MA,Bulls,Bears)) continue; //return(INIT_FAILED); int size=ArraySize(m_symbols_array); ArrayResize(m_symbols_array,size+1,10); ArrayResize(m_handles_array,size+1,10); m_symbols_array[size]=name; m_handles_array[size][0]=MA; m_handles_array[size][1]=Bulls; m_handles_array[size][2]=Bears; } m_file_txt.Close(); } else { if(!m_symbol.Name(Symbol())) // sets symbol name return(INIT_FAILED); } //--- ChartIndicatorAdd(0,0,handle_iCustom_MA);
Indicator handles are created in CreateHandles().
Thus, memory consumption was measured via TERMINAL_MEMORY_USED and visually in the Windows 10 task manager. To define memory consumption by steps, some strings were deliberately disabled (commented out) in the version 1.010. In the final version 1.010, all the strings for adding symbols and creating indicators were uncommented.
- The EA is launched in the usual way — by placing it to the chart:
- launching the terminal (symbols from the text file have not yet been added to the Market Watch window) — TERMINAL_MEMORY_USED 345 MB, task manager from 26 to 90 MB;
- adding about a hundred symbols to the Market Watch window — TERMINAL_MEMORY_USED 433 MB, task manager + 10 MB;
- creating three indicators per each symbol — TERMINAL_MEMORY_USED 5523 MB, task manager 300 MB.
- Launching the tester (no visualization) — TERMINAL_MEMORY_USED 420 MB, while in the task manager — 5 GB.
Conclusion: TERMINAL_MEMORY_USED shows the total consumption of RAM and disk space. Since the RAM consumption in the normal mode does not exceed 300 MB, there is no need to save resources.
Trend (EMA) is...
The main EA objective is to define the trend (EMA) direction. A trend cannot be determined by a single bar. We need data from several bars. Let's mark this parameter as "bars". Below are three securities charts — CAT, MCD and V. The trend is to be defined as follows: "+1" for uptrend, "0" for no trend and "—1" for downtrend
"CAT" chart shows trend "0" (4 bullish and 4 bearish bars, while the change is insignificant on the remaining bars), "MCD" shows trend "—1" (8 bearish bars, while others are in the uncertain state). Finally, "V" displays trend "0" (6 bullish bars, while 2 or 3 are bearish ones). We may need the 'different' parameter — minimum difference between the indicator readings on neighboring bars.
Defining trend. Elder-Ray 1.020.mq5
Trend existence conditions: EMA along bars should be in a certain direction. The two additional parameters should probably be checked afterwards:
- different — minimum difference between the indicator readings on neighboring bars;
- trend percentage — minimum percentage of the indicator readings in a single direction (on the screenshot: CAT symbol — EMA indicator is oppositely directed on the bars segment, while on MCD, all (or almost all) EMA indicator readings have a certain direction).
Additions and removals in the version 1.020:
- the different parameter is not implemented — minimum difference between the indicator readings on neighboring bars;
- "—" enum ENUM_INPUT_SYMBOLS enumeration — the EA is to work only with symbols from the text file;
- "+" parameter number of bars for identifying the trend — number of bars for identifying a trend by EMA;
- "+" parameter minimum percentage of the trend — minimum trend quality (certain direction);
- "+" m_prev_bars array — for storing previous bar open time;
- "+" 60 sec timer — check for a new bar.
The block for catching a new bar and defining a trend direction
In OnTimer(), go through the symbols array (m_symbols_array) downloaded from the text file once per 60 seconds and catch a new bar on a symbol from the array. Get EMA indicator data sufficient to determine a trend to the ema_array array. Perform calculation: number of bars the indicator moved upwards and downwards. Print detected patterns.
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { //--- int size=ArraySize(m_symbols_array); for(int i=0;i<size;i++) { //--- we work only at the time of the birth of new bar datetime PrevBars=m_prev_bars[i]; datetime time_0=iTime(m_symbols_array[i],Period(),0); if(time_0==PrevBars) continue; m_prev_bars[i]=time_0; double ema_array[]; ArraySetAsSeries(ema_array,true); /* m_handles_array[*][MA][Bulls][Bears] */ if(!iMAGetArray(m_handles_array[i][0],0,InpBarsEMA+2,ema_array)) continue; int upwards=0; int downward=0; for(int j=1;j<InpBarsEMA+1;j++) { if(ema_array[j]>ema_array[j+1]) upwards++; else if(ema_array[j]<ema_array[j+1]) downward++; } if((double)upwards>InpBarsEMA*InpMinTrendPercentage/100.0) Print("On ",m_symbols_array[i]," trend UP!"); else if((double)downward>InpBarsEMA*InpMinTrendPercentage/100.0) Print("On ",m_symbols_array[i]," trend DOWN!"); else Print("On ",m_symbols_array[i]," trend NONE!"); } }
Trend search result. Settings: number of bars for identifying the trend — 6, minimum percentage of the trend — 75%. Keep in mind that a zero bar is not taken into account when working at the moment a new bar appears:
Setting pending orders (Buy stop or Sell stop). Elder-Ray 1.030.mq5
Is it possible to avoid the lack of funds error when opening a position? Since we work with pending orders, the answer is "No". There may be half-way measures, but nothing can be guaranteed. The main reason is that no one knows at what point a pending order is activated and whether it is activated at all.
Now that the EA knows how to define a trend, we need to use the Buy and Sell rules to find points suitable for setting a pending order. For BUY, there will be a simple check: Bears Power on bar #1 should be less than zero and greater than Bears Power on bar #2. For SELL, the condition is the opposite: Bulls Power on bar #1 should exceed zero and less than Bears Power on bar #2.
Describing the strategy, Alexander Elder pointed out for opening a buy position, protective stop loss should be placed below the last Low, while for opening a sell position, it should be placed above the last High. The very concept of "Last" is rather blurred, so I checked the two options:
- setting a stop loss by bar #1 prices and
- searching for the nearest extreme point.
The option 1 turned out to be inviable - stop loss activations were too frequent. Therefore, I implemented the option 2 (searching for the nearest extremum) in the Elder-Ray 1.030.mq5 EA code.
Searching for the nearest extremum
The function look for the nearest extremum:
If no extremum is found or an error is detected, 'false' is returned:
//+------------------------------------------------------------------+ //| Find the nearest extremum | //+------------------------------------------------------------------+ bool NearestExtremum(ENUM_SERIESMODE type,double &price) { if(type==MODE_LOW) { //--- search for the nearest minimum double low_array[]; ArraySetAsSeries(low_array,true); int copy=CopyLow(m_symbol.Name(),Period(),0,100,low_array); if(copy==-1) return(false); double low=DBL_MAX; for(int k=0;k<copy;k++) { if(low_array[k]<low) low=low_array[k]; else if(low_array[k]>low) break; } if(low!=DBL_MAX) { price=low; return(true); } } else if(type==MODE_HIGH) { //--- search for the nearest maximum double high_array[]; ArraySetAsSeries(high_array,true); int copy=CopyHigh(m_symbol.Name(),Period(),0,100,high_array); if(copy==-1) return(false); double high=DBL_MIN; for(int k=0;k<copy;k++) { if(high_array[k]>high) high=high_array[k]; else if(high_array[k]<high) break; } if(high!=DBL_MIN) { price=high; return(true); } } //--- return(false); }
Additions and removals in the version 1.030:
- "+" CPositionInfo trading class (and m_position is its object);
- "+" CTrade trading class (and m_trade is its object);
- "+" COrderInfo trading class (and m_order is its object);
- "+" trailing (Trailing Stop and Trailing Step parameters);
- "+" magic number — unique EA identifier;
- "+" OnInit() — check the account type: if this is a hedging account, disable trading and unload with the error;
- OnInit() — visualization order changed: if the strategy tester is launched and the current symbol (the one the EA is launched on) is present in the text file, no indicators are added to the current symbol (ChartIndicatorAdd is not applied);
- OnTimer() — added signal confirmation code and trading operations for setting pending Buy Stop and Sell Stop orders;
- OnTradeTransaction() — added the compensation mechanism if there was a position reversal or partial closing;
- "+" when the Compensation Algorithm is activated, a stop loss is NOT SET in OnTradeTransaction(). Instead, the trailing function is improved: if a position without a stop loss is detected when checking positions, a stop loss is set according to the rule of searching for the nearest extreme point;
- "+" added the m_magic_compensation variable — compensation trades identifier.
To deal with situations when a pending order opposite to the current position is triggered, we need to consider three typical situations after a Buy Stop pending order is triggered:
# | Existing position, volume | Pending order activated, volume | Resulting position, volume | Note to a pending order trigger moment | Position's previous magic | Compensation algorithm (NB: magic is set to m_magic_compensation before the compensation) | Position's new magic |
---|---|---|---|---|---|---|---|
1 | Sell 1.0 | Buy Stop 3.0 | Buy 2.0 | Position reversal (DEAL_ENTRY_INOUT deal direction) | m_magic | Open additional Buy with the volume of 3.0 — 2.0 = 1.0 | m_magic_compensation |
2 | Sell 1.0 | Buy Stop 1.0 | --- | Closing position in full (DEAL_ENTRY_OUT deal direction) | m_magic | Searching for a position. If there is no position, open Buy with the volume of 1.0 | m_magic_compensation |
3 | Sell 2.0 | Buy Stop 1.0 | Sell 1.0 | Closing position partially (DEAL_ENTRY_OUT deal direction) | m_magic | Searching for a position. If a position is present and it is opposite to Buy, open Buy with the volume of 1.0 + 1.0 = 2.0 | m_magic_compensation |
For each of the three cases, I prepared a printout of deals and orders (test on a real netting account, as well as in the tester). To generate a report on deals and orders, I used the code from the History Deals and Orders script.
#1: Sell 1.0 -> Buy Stop 3.0
Sell 1.0, Buy Stop 3.0 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |1 |0 |2016.12.08 00:00:00 |1481155200000 |DEAL_TYPE_BALANCE |DEAL_ENTRY_IN |0 |DEAL_REASON_CLIENT |0 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |0.00 |0.00000 |0.00 |0.00 |50000.00 | | | Order 0 is not found in the trade history between the dates 2010.08.07 11:06:20 and 2018.08.10 00:00:00 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |2 |2 |2016.12.08 00:00:00 |1481155200100 |DEAL_TYPE_SELL |DEAL_ENTRY_IN |15489 |DEAL_REASON_EXPERT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |79.31 |0.00 |0.00 |0.00 |V | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |2 |2016.12.08 00:00:00 |ORDER_TYPE_SELL |ORDER_STATE_FILLED |2016.12.08 00:00:00 |2016.12.08 00:00:00 |1481155200100 |1481155200100 |ORDER_FILLING_FOK |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |15489 |ORDER_REASON_EXPERT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |1.00 |0.00 |79.31 |0.00 |0.00 |79.39 |0.00 |Symbol |Comment |Extarnal id |V | | Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |3 |3 |2016.12.08 17:27:37 |1481218057877 |DEAL_TYPE_BUY |DEAL_ENTRY_INOUT |15489 |DEAL_REASON_EXPERT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |3.00 |79.75 |0.00 |0.00 |-0.44 |V | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |3 |2016.12.08 14:30:00 |ORDER_TYPE_BUY_STOP |ORDER_STATE_FILLED |2016.12.08 14:30:00 |2016.12.08 17:27:37 |1481207400100 |1481218057877 |ORDER_FILLING_RETURN |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |15489 |ORDER_REASON_EXPERT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |3.00 |0.00 |79.74 |75.17 |0.00 |79.74 |0.00 |Symbol |Comment |Extarnal id |V | | Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |4 |4 |2016.12.09 23:59:00 |1481327940000 |DEAL_TYPE_SELL |DEAL_ENTRY_OUT |0 |DEAL_REASON_CLIENT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |2.00 |79.13 |0.00 |0.00 |-1.24 |V |end of test | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |4 |2016.12.09 23:59:00 |ORDER_TYPE_SELL |ORDER_STATE_FILLED |2016.12.09 23:59:00 |2016.12.09 23:59:00 |1481327940000 |1481327940000 |ORDER_FILLING_FOK |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |0 |ORDER_REASON_CLIENT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |2.00 |0.00 |79.13 |0.00 |0.00 |79.13 |0.00 |Symbol |Comment |Extarnal id |V |end of test |
#2: Sell 1.0 -> Buy Stop 1.0
Sell 1.0, Buy Stop 1.0 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |1 |0 |2016.12.08 00:00:00 |1481155200000 |DEAL_TYPE_BALANCE |DEAL_ENTRY_IN |0 |DEAL_REASON_CLIENT |0 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |0.00 |0.00000 |0.00 |0.00 |50000.00 | | | Order 0 is not found in the trade history between the dates 2010.08.07 11:06:20 and 2018.08.10 00:00:00 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |2 |2 |2016.12.08 00:00:00 |1481155200100 |DEAL_TYPE_SELL |DEAL_ENTRY_IN |15489 |DEAL_REASON_EXPERT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |79.31 |0.00 |0.00 |0.00 |V | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |2 |2016.12.08 00:00:00 |ORDER_TYPE_SELL |ORDER_STATE_FILLED |2016.12.08 00:00:00 |2016.12.08 00:00:00 |1481155200100 |1481155200100 |ORDER_FILLING_FOK |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |15489 |ORDER_REASON_EXPERT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |1.00 |0.00 |79.31 |0.00 |0.00 |79.39 |0.00 |Symbol |Comment |Extarnal id |V | | Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |3 |3 |2016.12.08 17:27:37 |1481218057877 |DEAL_TYPE_BUY |DEAL_ENTRY_OUT |15489 |DEAL_REASON_EXPERT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |79.75 |0.00 |0.00 |-0.44 |V | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |3 |2016.12.08 14:30:00 |ORDER_TYPE_BUY_STOP |ORDER_STATE_FILLED |2016.12.08 14:30:00 |2016.12.08 17:27:37 |1481207400100 |1481218057877 |ORDER_FILLING_RETURN |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |15489 |ORDER_REASON_EXPERT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |1.00 |0.00 |79.74 |75.17 |0.00 |79.74 |0.00 |Symbol |Comment |Extarnal id |V | |
#3: Sell 2.0 -> Buy Stop 1.0
Sell 2.0, Buy Stop 1.0 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |1 |0 |2016.12.08 00:00:00 |1481155200000 |DEAL_TYPE_BALANCE |DEAL_ENTRY_IN |0 |DEAL_REASON_CLIENT |0 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |0.00 |0.00000 |0.00 |0.00 |50000.00 | | | Order 0 is not found in the trade history between the dates 2010.08.07 11:06:20 and 2018.08.10 00:00:00 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |2 |2 |2016.12.08 00:00:00 |1481155200100 |DEAL_TYPE_SELL |DEAL_ENTRY_IN |15489 |DEAL_REASON_EXPERT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |2.00 |79.31 |0.00 |0.00 |0.00 |V | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |2 |2016.12.08 00:00:00 |ORDER_TYPE_SELL |ORDER_STATE_FILLED |2016.12.08 00:00:00 |2016.12.08 00:00:00 |1481155200100 |1481155200100 |ORDER_FILLING_FOK |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |15489 |ORDER_REASON_EXPERT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |2.00 |0.00 |79.31 |0.00 |0.00 |79.39 |0.00 |Symbol |Comment |Extarnal id |V | | Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |3 |3 |2016.12.08 17:27:37 |1481218057877 |DEAL_TYPE_BUY |DEAL_ENTRY_OUT |15489 |DEAL_REASON_EXPERT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |79.75 |0.00 |0.00 |-0.44 |V | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |3 |2016.12.08 14:30:00 |ORDER_TYPE_BUY_STOP |ORDER_STATE_FILLED |2016.12.08 14:30:00 |2016.12.08 17:27:37 |1481207400100 |1481218057877 |ORDER_FILLING_RETURN |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |15489 |ORDER_REASON_EXPERT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |1.00 |0.00 |79.74 |75.17 |0.00 |79.74 |0.00 |Symbol |Comment |Extarnal id |V | | Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |4 |4 |2016.12.09 23:59:00 |1481327940000 |DEAL_TYPE_BUY |DEAL_ENTRY_OUT |0 |DEAL_REASON_CLIENT |2 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |79.13 |0.00 |0.00 |0.18 |V |end of test | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |4 |2016.12.09 23:59:00 |ORDER_TYPE_BUY |ORDER_STATE_FILLED |2016.12.09 23:59:00 |2016.12.09 23:59:00 |1481327940000 |1481327940000 |ORDER_FILLING_FOK |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |0 |ORDER_REASON_CLIENT |2 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |1.00 |0.00 |79.13 |0.00 |0.00 |79.13 |0.00 |Symbol |Comment |Extarnal id |V |end of test |
Below is yet another case on a real account and real time (not in the tester): the market Buy order with the volume of 2.0 (Buy trading order generated two deals with the volumes of 1.0 — 20087494 and 20087495), then Sell limit with the volume of 2.0 was placed to take the profit and close the position. A bit later, that Sell limit was completed in two parts (deals 20088091 and 20088145). The printout:
Buy 2.0, Sell limit 2.0 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |20087494 |29080489 |2018.08.10 07:23:34 |1533885814000 |DEAL_TYPE_BUY |DEAL_ENTRY_IN |0 |DEAL_REASON_CLIENT |29080489 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |14595 |-0.10 |0.00 |0.00 |RTSGZU8 | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |29080489 |2018.08.10 07:23:34 |ORDER_TYPE_BUY |ORDER_STATE_FILLED |2018.08.10 07:23:34 |2018.08.10 07:23:34 |1533885814000 |1533885814000 |ORDER_FILLING_RETURN |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |0 |ORDER_REASON_CLIENT |29080489 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |2.00 |0.00 |0 |0 |0 |14588 |0 |Symbol |Comment |External id |RTSGZU8 | |13_31861873584 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |20087495 |29080489 |2018.08.10 07:23:34 |1533885814000 |DEAL_TYPE_BUY |DEAL_ENTRY_IN |0 |DEAL_REASON_CLIENT |29080489 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |14595 |-0.10 |0.00 |0.00 |RTSGZU8 | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |29080489 |2018.08.10 07:23:34 |ORDER_TYPE_BUY |ORDER_STATE_FILLED |2018.08.10 07:23:34 |2018.08.10 07:23:34 |1533885814000 |1533885814000 |ORDER_FILLING_RETURN |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |0 |ORDER_REASON_CLIENT |29080489 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |2.00 |0.00 |0 |0 |0 |14588 |0 |Symbol |Comment |External id |RTSGZU8 | |13_31861873584 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |20088091 |29080662 |2018.08.10 08:03:08 |1533888188000 |DEAL_TYPE_SELL |DEAL_ENTRY_OUT |0 |DEAL_REASON_CLIENT |29080489 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |14626 |-0.10 |0.00 |0.46 |RTSGZU8 | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |29080662 |2018.08.10 07:27:19 |ORDER_TYPE_SELL_LIMIT |ORDER_STATE_FILLED |2018.08.10 07:27:19 |2018.08.10 08:05:42 |1533886039000 |1533888342000 |ORDER_FILLING_RETURN |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |0 |ORDER_REASON_CLIENT |29080489 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |2.00 |0.00 |14626 |0 |0 |14624 |0 |Symbol |Comment |External id |RTSGZU8 | |13_31862155871 Deal: |Ticket |Order |Time |Time msc |Type |Entry |Magic |Reason |Position ID |20088145 |29080662 |2018.08.10 08:05:42 |1533888342000 |DEAL_TYPE_SELL |DEAL_ENTRY_OUT |0 |DEAL_REASON_CLIENT |29080489 |Volume |Price |Commission |Swap |Profit |Symbol |Comment |External ID |1.00 |14626 |-0.10 |0.00 |0.46 |RTSGZU8 | | Order: |Ticket |Time setup |Type |State |Time expiration |Time done |Time setup msc |Time done msc |Type filling |29080662 |2018.08.10 07:27:19 |ORDER_TYPE_SELL_LIMIT |ORDER_STATE_FILLED |2018.08.10 07:27:19 |2018.08.10 08:05:42 |1533886039000 |1533888342000 |ORDER_FILLING_RETURN |Type time |Magic |Reason |Position id |Position by id |1970.01.01 00:00:00 |0 |ORDER_REASON_CLIENT |29080489 |0 |Volume initial |Volume current |Open price |sl |tp |Price current |Price stoplimit |2.00 |0.00 |14626 |0 |0 |14624 |0 |Symbol |Comment |External id |RTSGZU8 | |13_31862155871
Tips on testing 1.xxx
- Try to keep securities of approximately the same value in the text file.
- Leaving a small number of symbols is preferable when testing in the text file. The most perfect case is to leave a single symbol and perform a test on it.
Option 2: in combination with the "Triple choice" system
In the Option 1 (all indicators on a single chart), a trend indicator was on the same timeframe. In the Option 2, the trend indicator will be located on a higher timeframe. Thus, only one new parameter is added — trend timeframe (Trend timeframe).
The Option 2 is implemented in the Elder-Ray 2.000.mq5 EA.
Files attached to the article:
Name | File type | Description |
---|---|---|
Symbols on the specified path.mq5 | Script | Forms a text file with the symbols of the group and is saved in the Common Data Folder |
Gets minimal volume.mq5 | Script | Displays statistics on the minimum volume of the group. |
Elder-Ray 1.001.mq5 | Expert Advisor | Shows visualization of applied indicators |
Elder-Ray 1.010.mq5 | Expert Advisor | Start working with the text file and create indicators for symbols from the file. The EA is used to monitor memory consumption |
Elder-Ray 1.020.mq5 | Expert Advisor | Define a trend. Check the trend definition validity |
Elder-Ray 1.030.mq5 | Expert Advisor | Run-time version for Option 1: all indicators on a single chart |
Elder-Ray 2.000.mq5 | Expert Advisor | Option 2: in combination with the "Triple choice" system |
Conclusion
Elder-Ray (Bulls Power and Bears Power) trading system is viable enough, especially in combination with the "Triple choice" system when EMA trend indicator is calculated on a higher timeframe than Bulls Power and Bears Power.
When testing the EA, we should keep in mind that the prepared text file may contain up to 100 symbols. Considering this number, you may need up to 10 minutes to launch the test, as well as up to 5 GB of memory during the test.
Regardless of your trust to the EA, you will surely want to intervene the process, like I did while writing the article and testing the EA versions:
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/5014
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
Hello,
thank you for the article!
Can you tell me what broker did you use to build this article?
Best regards,
Romeu Bertho.
Hello,
thank you for the article!
Can you tell me what broker did you use to build this article?
Best regards,
Romeu Bertho.
Sorry, discussion and advertising of brokers, dealers and trading organizations is prohibited.
Wow. This is a fantastic write up, @Vladimir Karputov. Thank you for taking the time to write so much detail.
I just learned about Elder-Ray and now I am going to study this article and code very deeply. I am considering combining it with other indicators that might boost probability.
Do you have any suggestions on what indicators might be good to combine with the Elder Ray? I was thinking about volume indicators and maybe IKH. What do you think?