What are Expert Magic Numbers?

Expert magic numbers are ids given to a particular expert advisor.

Care should be taken to ensure the ids are unique.

This is because the magic number is used by the MetaTrader back end to know which expert advisor to send information to. For example, when a trade notification is received, MetaTrader will have stamped the order with the expert advisor’s magic number, and it therefore knows which expert advisor to send the trade notification to.

It can therefore also be used to find out which expert advisor sent a particular order.

Advertisements

CSymbolInfo: obtain information about a symbol

To obtain information about a symbol, create a CSymbolInfo instance, and set it’s Name.

I’ve create a quick utility class which prints out all the members of the CSymbolInfo class:

#include <Trade/SymbolInfo.mqh>
#include "SymbolCalcMode.mqh"

struct SymbolInfo
{
    static void Print(const CSymbolInfo& symbol)
    {
        Print("Name: " + symbol.Name());
        Print("Selected in market watch: " + IntegerToString(symbol.Select()));
        Print("Synchronised: " + IntegerToString(symbol.IsSynchronized()));
        Print("Volume: " + IntegerToString(symbol.Volume()));
        Print("VolumeHigh: " + IntegerToString(symbol.VolumeHigh()));
        Print("VolumeLow: " + IntegerToString(symbol.VolumeLow()));
        Print("Time: " + TimeToString(symbol.Time()));
        Print("Spread: " + IntegerToString(symbol.Spread()));
        Print("SpreadFloat: " + IntegerToString(symbol.SpreadFloat()));
        Print("TicksBookDepth: " + IntegerToString(symbol.TicksBookDepth()));
        Print("StopsLevel: " + IntegerToString(symbol.StopsLevel()));
        Print("FreezeLevel: " + IntegerToString(symbol.FreezeLevel()));
        Print("Bid: " + DoubleToString(symbol.Bid()));
        Print("BidHigh: " + DoubleToString(symbol.BidHigh()));
        Print("BidLow: " + DoubleToString(symbol.BidLow()));
        Print("Ask: " + DoubleToString(symbol.Ask()));
        Print("AskHigh: " + DoubleToString(symbol.AskHigh()));
        Print("AskLow: " + DoubleToString(symbol.AskLow()));
        Print("Last: " + DoubleToString(symbol.Last()));
        Print("LastHigh: " + DoubleToString(symbol.LastHigh()));
        Print("LastLow: " + DoubleToString(symbol.LastLow()));
        Print("TradeCalcMode: " + SymbolCalcMode::ToString(symbol.TradeCalcMode()));
        Print("TradeCalcModeDesc: " + symbol.TradeCalcModeDescription());
        Print("MarginFormula: " + SymbolCalcMode::MarginFormula(symbol.TradeCalcMode()));
        Print("ProfitFormula: " + SymbolCalcMode::ProfitFormula(symbol.TradeCalcMode()));
        Print("TradeMode: " + IntegerToString(symbol.TradeMode()));
        Print("TradeModeDesc: " + symbol.TradeModeDescription());
        Print("TradeExecution: " + IntegerToString(symbol.TradeExecution()));
        Print("TradeExecutionDesc: " + symbol.TradeExecutionDescription());
        Print("SwapMode: " + IntegerToString(symbol.SwapMode()));
        Print("SwapModeDesc: " + symbol.SwapModeDescription());
        Print("SwapRollover3days: " + IntegerToString(symbol.SwapRollover3days()));
        Print("SwapRollover3daysDes: " + symbol.SwapRollover3daysDescription());
        Print("MarginInitial: " + DoubleToString(symbol.MarginInitial()));
        Print("MarginMaintenance: " + DoubleToString(symbol.MarginMaintenance()));
        Print("MarginLong: " + DoubleToString(symbol.MarginLong()));
        Print("MarginShort: " + DoubleToString(symbol.MarginShort()));
        Print("MarginLimit: " + DoubleToString(symbol.MarginLimit()));
        Print("MarginStop: " + DoubleToString(symbol.MarginStop()));
        Print("MarginStopLimit: " + DoubleToString(symbol.MarginStopLimit()));
        Print("TradeTimeFlags: " + IntegerToString(symbol.TradeTimeFlags()));
        Print("TradeFillFlags: " + IntegerToString(symbol.TradeFillFlags()));
        Print("Digits: " + IntegerToString(symbol.Digits()));
        Print("Point: " + DoubleToString(symbol.Point()));
        Print("TickValue: " + DoubleToString(symbol.TickValue()));
        Print("TickValueProfit: " + DoubleToString(symbol.TickValueProfit()));
        Print("TickValueLoss: " + DoubleToString(symbol.TickValueLoss()));
        Print("TickSize: " + DoubleToString(symbol.TickSize()));
        Print("ContractSize: " + DoubleToString(symbol.ContractSize()));
        Print("LotsMin: " + DoubleToString(symbol.LotsMin()));
        Print("LotsMax: " + DoubleToString(symbol.LotsMax()));
        Print("LotsStep: " + DoubleToString(symbol.LotsStep()));
        Print("LotsLimit: " + DoubleToString(symbol.LotsLimit()));
        Print("SwapLong: " + DoubleToString(symbol.SwapLong()));
        Print("SwapShort: " + DoubleToString(symbol.SwapShort()));
        Print("CurrencyBase: " + symbol.CurrencyBase());
        Print("CurrencyProfit: " + symbol.CurrencyProfit());
        Print("CurrencyMargin: " + symbol.CurrencyMargin());
        Print("Bank: " + symbol.Bank());
        Print("Description: " + symbol.Description());
        Print("Path: " + symbol.Path());
        Print("SessionDeals: " + IntegerToString(symbol.SessionDeals()));
        Print("SessionBuyOrders: " + IntegerToString(symbol.SessionBuyOrders()));
        Print("SessionSellOrders: " + IntegerToString(symbol.SessionSellOrders()));
        Print("SessionTurnover: " + DoubleToString(symbol.SessionTurnover()));
        Print("SessionInterest: " + DoubleToString(symbol.SessionInterest()));
        Print("SessionBuyOrdersVolume: " + DoubleToString(symbol.SessionBuyOrdersVolume()));
        Print("SessionSellOrdersVolume: " + DoubleToString(symbol.SessionSellOrdersVolume()));
        Print("SessionOpen: " + DoubleToString(symbol.SessionOpen()));
        Print("SessionClose: " + DoubleToString(symbol.SessionClose()));
        Print("SessionAW: " + DoubleToString(symbol.SessionAW()));
        Print("SessionPriceSettlement: " + DoubleToString(symbol.SessionPriceSettlement()));
        Print("SessionPriceLimitMin: " + DoubleToString(symbol.SessionPriceLimitMin()));
        Print("SessionPriceLimitMax: " + DoubleToString(symbol.SessionPriceLimitMax()));
    }
};

It uses another small utility class for printing out the symbol calculation mode

struct SymbolCalcMode
{
    static string ToString(int id)
    {
        switch (id)
        {
            case SYMBOL_CALC_MODE_FOREX:       return "Forex mode";
            case SYMBOL_CALC_MODE_FUTURES:     return "Futures mode";
            case SYMBOL_CALC_MODE_CFD:         return "CFD  mode";
            case SYMBOL_CALC_MODE_CFDINDEX:    return "CFD Index mode";
            case SYMBOL_CALC_MODE_CFDLEVERAGE: return "CFD Leverage mode";
        }
        return "unknown " + IntegerToString(id);
    }
    static string MarginFormula(int id)
    {
        switch (id)
        {
            case SYMBOL_CALC_MODE_FOREX:       return "lots * contract_size / leverage";
            case SYMBOL_CALC_MODE_FUTURES:     return "lots * initial_margin * percentage / 100";
            case SYMBOL_CALC_MODE_CFD:         return "lots * contract_size * market_price * percentage / 100";
            case SYMBOL_CALC_MODE_CFDINDEX:    return "(lots * contract_size * market_price) * tick_price / tick_size";
            case SYMBOL_CALC_MODE_CFDLEVERAGE: return "(lots * contract_size * market_price * percentage) / leverage";
        }
        return "unknown " + IntegerToString(id);
    }
    static string ProfitFormula(int id)
    {
        switch (id)
        {
            case SYMBOL_CALC_MODE_FOREX:       return "(close_price - open_price) * contract_size * lots";
            case SYMBOL_CALC_MODE_FUTURES:     return "(close_price - open_price) * tick_price / tick_size * lots";
            case SYMBOL_CALC_MODE_CFD:         return "(close_price - open_price) * contract_size * lots";
            case SYMBOL_CALC_MODE_CFDINDEX:    return "(close_price - open_price) * contract_size * lots";
            case SYMBOL_CALC_MODE_CFDLEVERAGE: return "(close_price - open_price) * contract_size * lots";
        }
        return "unknown " + IntegerToString(id);
    }
};

Here I initialise an object with the current symbol and display the details in the OnInit method of an expert advisor.

#include "../../Include/SymbolInfo.mqh"
#include <Trade/SymbolInfo.mqh>

int OnInit()
{
    CSymbolInfo symbol;
    symbol.Name(_Symbol);
    SymbolInfo::Print(symbol);
    
    return INIT_SUCCEEDED;
}

Trading off a moving average

Using the examples provided in the MQL5 source tree, I found one which traded using a moving average.

…\MQL5\Experts\Examples\Moving Average\Moving Average.mq5

I took this and played around with it in order to further understand the mechanics of the provided MQL5 object hierarchy.

The idea is simple: Apply a simple moving average to the chart in order to average out the noise and see an overall trend.

By shifting the moving average horizontally to the right, when price action crosses the average it signals a reverse in trend.

When a new bar is opened (ie: on the first tick of the new bar the tick volume in the bar is 1), we look at what happened in the previous bar.

If price crosses the trend moving upwards, this is a signal to go long.

// the previous bar opened below the average and closed above the average
if (rates[0].open < ma[0] && rates[0].close > ma[0])
    order = ORDER_TYPE_BUY; // price crosses up - go long

If price crosses the trend moving downwards, this is a signal to go short.

// the previous bar opened above the average and closed below the average
if (rates[0].open > ma[0] && rates[0].close < ma[0]) 
    order = ORDER_TYPE_SELL; // price crosses down - go short

I keep track of whether I have opened my first position. Once this has happened I double my lot size. The reason for this is so that when the trend reverses, I will enter an opposite position for double the number of lots; this will exit my previous position and enter a new position in the opposite direction.

#include <Trade\Trade.mqh>

input double          LotSize        =  1;  // Number of lots to order
input int             MovingPeriod   =  12; // Moving average period
input int             MovingShift    =  6;  // Moving average shift

int      MovingAvg = 0;
MqlRates rates[2];
double   ma[1];
bool     tradedFirstLot = false;
double   NumLots = LotSize;

int OnInit()
{
    if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))
    {
        printf("Trading not allowed");
        return -1;
    }

    MovingAvg = iMA(_Symbol, _Period, MovingPeriod, MovingShift, MODE_SMA, PRICE_CLOSE);
    if (MovingAvg == INVALID_HANDLE)
    {
        printf("Error creating MA indicator");
        return -1;
    }
    return 0;
}
//--------------------------------------------------------------------------

void OnTick()
{
    // copy rates starting from the latest, going back 2 periods
    if (CopyRates(_Symbol, _Period, 0, 2, rates) != 2)
    {
        Print("CopyRates of ",_Symbol," failed, no history");
        return;
    }
    
    // go trading only for first tick of new bar
    if (rates[1].tick_volume > 1)
        return;
    
    // get current moving average 
    if (CopyBuffer(MovingAvg, 0, 0, 1, ma) != 1)
    {
        Print("CopyBuffer from iMA failed, no data");
        return;
    }

    // calculate whether price has crossed the moving average
    ENUM_ORDER_TYPE order;
    if (rates[0].open < ma[0] && rates[0].close > ma[0]) 
        order = ORDER_TYPE_BUY; // price crosses up - go long
    else
    if (rates[0].open > ma[0] && rates[0].close < ma[0]) 
        order = ORDER_TYPE_SELL; // price crosses down - go short
    else
        return; // price moving sideways

    CTrade trade;
    trade.PositionOpen(_Symbol,
                       order,
                       NumLots,
                       SymbolInfoDouble(_Symbol, 
                                        order == ORDER_TYPE_SELL 
                                            ? SYMBOL_BID 
                                            : SYMBOL_ASK),
                       0,  // no stop loss
                       0); // no take profit
                       
    if (!tradedFirstLot)
    {
        // once we have a position on, we need to double the lot size so that
        // our next order closes the first position and then opens the new one
        tradedFirstLot = true;
        NumLots *= 2;           
    }
}
//--------------------------------------------------------------------------

void OnDeinit(const int reason)
{
}
//--------------------------------------------------------------------------

Displaying more information

The OnChartEvent callback returns a number of parameters. I wrote another small utility class to display the details of these parameters.

struct ChartEvent
{
    static string ToString(int id)
    {
        switch (id)
        {
            case CHARTEVENT_KEYDOWN:       return "keystroke";
            case CHARTEVENT_MOUSE_MOVE:    return "mouse event (move or click)";
            case CHARTEVENT_OBJECT_CREATE: return "graphical object creation";
            case CHARTEVENT_OBJECT_CHANGE: return "object property change via the properties dialog";
            case CHARTEVENT_OBJECT_DELETE: return "graphical object deletion";
            case CHARTEVENT_CLICK:         return "mouse click";
            case CHARTEVENT_OBJECT_CLICK:  return "graphical object mouse click";
            case CHARTEVENT_OBJECT_DRAG:   return "graphical object mouse drag";
            case CHARTEVENT_OBJECT_ENDEDIT:return "text box entry complete";
            case CHARTEVENT_CHART_CHANGE:  return "chart change";
            case CHARTEVENT_CUSTOM_LAST:   return "custom id max";
            default:                       return "custom id " + IntegerToString(id);
        }
        return "unknown";
    }
    
    static string DisplayInfo(int id, long l, double d, const string& s)
    {
        string ret = ToString(id) + " ";
        switch (id)
        {
            case CHARTEVENT_KEYDOWN:       ret += KeyEvent(l);                   break;
            case CHARTEVENT_MOUSE_MOVE:    ret += MouseMove(l,(long)d,s);        break;
            case CHARTEVENT_OBJECT_CREATE: ret += ObjectCreated(s);              break;
            case CHARTEVENT_OBJECT_CHANGE: ret += ObjectChanged(s);              break;
            case CHARTEVENT_OBJECT_DELETE: ret += ObjectDeleted(s);              break;
            case CHARTEVENT_CLICK:         ret += ChartClick(l,(long)d);         break;
            case CHARTEVENT_OBJECT_CLICK:  ret += ChartObjectClick(l,(long)d,s); break;
            case CHARTEVENT_OBJECT_DRAG:   ret += ObjectDragged(s);              break;
            case CHARTEVENT_OBJECT_ENDEDIT:ret += ObjectEndEdit(s);              break;
            case CHARTEVENT_CHART_CHANGE:                                        break;
            case CHARTEVENT_CUSTOM_LAST:                                         break;
            default:                                                             break;
        }
        return ret;
    }
    
    static string KeyEvent(long l)
    {
        return "key_code=" + IntegerToString(l);
    }
    
    static string MouseMove(long x, long y, const string& s)
    {
        return "x=" + IntegerToString(x) + ", y=" + IntegerToString(y) + ", status=" + s;
    }
    
    static string ObjectCreated(const string& s)
    {
        return "object=" + s;
    }
    
    static string ObjectChanged(const string& s)
    {
        return "object=" + s;
    }
    
    static string ObjectDeleted(const string& s)
    {
        return "object=" + s;
    }
    
    static string ChartClick(long x, long y)
    {
        return "x=" + IntegerToString(x) + ", y=" + IntegerToString(y);
    }
    
    static string ChartObjectClick(long x, long y, const string& s)
    {
        return "x=" + IntegerToString(x) + ", y=" + IntegerToString(y) + ", object=" + s;
    }
    
    static string ObjectDragged(const string& s)
    {
        return "object=" + s;
    }
    
    static string ObjectEndEdit(const string& s)
    {
        return "object=" + s;
    }
};        

I then display the details information in my callback:

void OnChartEvent(const int     id, // Event ID
                  const long&   l,  // Parameter of type long event
                  const double& d,  // Parameter of type double event
                  const string& s)  // Parameter of type string event
{
    Print(__FUNCTION__ + " " + ChartEvent::DisplayInfo(id, l, d, s));
}

Displaying some information

I now add some information about the status of the EA, and the parameters passed to the callbacks.

The call to OnDeinit passes an integer value called ‘reason’. I wrote a simple class with a static function which will translate the integer into a string.

struct UninitReason
{
    static string ToString(int id)
    {
        switch (id)
        {
            case REASON_ACCOUNT:     return "account was changed";
            case REASON_CHARTCHANGE: return "symbol or timeframe was changed";
            case REASON_CHARTCLOSE:  return "chart was closed";
            case REASON_PARAMETERS:  return "input-parameter was changed";
            case REASON_RECOMPILE:   return "program was recompiled";
            case REASON_REMOVE:      return "program was removed from chart";
            case REASON_TEMPLATE:    return "new template was applied to chart";
        }
        return "unknown " + IntegerToString(id);
    }
};

I then called the static function ToString from my OnDeinit callback function:

void OnDeinit(const int reason)
{
    Print(__FUNCTION__ + "; reason=" + UninitReason::ToString(reason));
}

Getting started

Creating my first Expert Advisor ever… daunting!

I opened the integrated MetaEditor IDE by clicking on Tools –¬†MetaQuotes Language Editor [F4]

Once it had opened, I selected New – Expert Advisor (template). I chose “Test” as my first EA’s name, and¬†selected all the available callback functions / event handlers.

I then added the following code to each event handler, which would show me which functions were called at what point when I ran the EA.

Print(__FUNCTION__);

If you then compile (F7), and run (F5), you will be returned to MetaTrader, and asked to confirm you wish to run the EA. Click on OK.

Now when you click on the chart, or a new tick arrives, etc, you will see the callback function’s name being printed (It is displayed in Experts tab of the Toolbox – if the toolbox is not visible, Ctrl-T will bring it up)

To stop the EA, either right click on the EA icon in the top right corner of the main chart window and click Remove, or return to MetaEditor (F4) and click on Stop debugging (Shift-F5).

This is the source code:

int OnInit()
{
    Print(__FUNCTION__);
    return INIT_SUCCEEDED;
}
//-----------------------------------------------------------

void OnDeinit(const int reason)
{
    Print(__FUNCTION__);
}
//-----------------------------------------------------------

void OnTick()
{
    Print(__FUNCTION__);
}
//-----------------------------------------------------------

void OnTrade()
{
    Print(__FUNCTION__);
}
//-----------------------------------------------------------

void OnTimer()
{
    Print(__FUNCTION__);
}
//-----------------------------------------------------------

double OnTester()
{
    Print(__FUNCTION__);
    return 1.0;
}
//-----------------------------------------------------------

void OnChartEvent(const int     id, // Event ID
                  const long&   l,  // Parameter of type long event
                  const double& d,  // Parameter of type double event
                  const string& s)  // Parameter of type string event
{
    Print(__FUNCTION__);
}
//-----------------------------------------------------------

void OnBookEvent (const string& symbol)                  
{
    Print(__FUNCTION__);
}
//-----------------------------------------------------------