Gandolf PRO EA Discussion

Discussion in 'Forex' started by ElectricSavant, Dec 3, 2010.

  1. Gandolf PRO holds it's head above water...thus far.

    [​IMG]
     
  2. Here is the current trade Gandalf PRO is in:

    [​IMG]
     
  3. I cranked up the risk on this $300.00 demo account...Do not tell Wifey

    [​IMG]
     
  4. No one is trading just now as tomorrow is NFP...If I were trading real money I would turn the EA off until Monday...

    ES
     
  5. Good1

    Good1

    What i'd like to do is look at the code and understand what exactly this EA is doing. 16 trades isn't very many to get a good idea of it's potential. But the way i see it, this EA fails if i had to use the chart shown earlier to judge by. If i knew what it was doing, i might be able to guess why it fails, and if the equity curve could be made more better.

    The code is here:
    http://www.elitetrader.com/vb/showthread.php?s=&threadid=211295

    I'd like to start by pointing out the custom functions. It looks like there are four of them:

    Code:
    void Trade_BUY(int mn,int num,double factor1,double factor2,int sl)
    void Trade_SELL(int mn,int num,double factor1,double factor2,int sl)
    double Out(int n,double l1,double l2)
    double lot(int R)
    
    So there a buy function and a sell function. There's also a function - "Out" - that calculates when/where to get out of the trade. The function "lot" calcs how many lots to use.

    Besides the usual culprits, here are the significant built-in functions are:
    Code:
    lm=iMA(NULL,0,n,0,MODE_LWMA,PRICE_CLOSE,1),
    sm=iMA(NULL,0,n,0, MODE_SMA,PRICE_CLOSE,1); 
    
    So it appears to me he's using a long and short moving average. But how he uses them i'm not sure because the code is, well, NOT commented. So i MIGHT decide to comment it here.
     
  6. Good1

    Good1

    So here's the main function that get's called every tick:

    Code:
    //+------------------------------------------------------------------+
    //| expert start function |
    //+------------------------------------------------------------------+
    int start()
    {
    //oooooooooooooooooooooooooooooooooooooooooooooooooooo
    if (Time[0] == prevtime) return(0);
    prevtime = Time[0];
    if (!IsTradeAllowed()) {
    prevtime=Time[1]; MathSrand(TimeCurrent());Sleep(30000 + MathRand());
    }
    //oooooooooooooooooooooooooooooooooooooooooooooooooooo
    if( In_BUY)Trade_BUY ( Magic_BUY, Count_buy,w_price,w_trend, SL_buy);
    if(In_SELL)Trade_SELL(Magic_SELL,Count_sell,m_price,m_trend,SL_sell);
    //oooooooooooooooooooooooooooooooooooooooooooooooooooo
    return(0);
    }
    
    There's two parts:

    The first part is a filter to decide IF the Trade_BUY and Trade_SELL functions will be called. The first part is set up to trade at the opening of a new bar only. Suppose it's the first tick of a new bar, it will then see if it is able to trade. If unable, for example, because there is another trade (perhaps on some other EA) in the "trade context" (http://www.metatrader4.com/forum/1528) then its going to wait anywhere from 30 to 62 seconds to continue. It looks like after 30 to 62 seconds, it WILL continue to the Trade_BUY and Trade_SELL functions. It will filter one more time to see if the trader is allowing only long, only short, or both long and short trades.

    Next, let's look closer at Trade_BUY:

    Code:
    //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    void Trade_BUY(int mn,int num,double factor1,double factor2,int sl) {
    
    int total=OrdersTotal();
    
    for (int i = 0; i < total; i++) { OrderSelect(i, SELECT_BY_POS, MODE_TRADES);
    
    if (OrderSymbol() == Symbol() && OrderMagicNumber() == mn) { return(0);
    }
    }
    //ooooooooooooooooooooooooooooooooooooooooooooooooooo
    ticket = -1;
    
    double target=Out(num,factor1,factor2);
    
    if (target>(Bid+15*x*Point) && IsTradeAllowed()) {
    
    ticket= OrderSend(Symbol(), OP_BUY,lot(Risk_buy),Ask,5,Bid-x*sl*Point,target,DoubleToStr(mn,0),mn,0,Blue);
    
    
    RefreshRates();
    
    if ( ticket < 0) { Sleep(30000); prevtime = Time[1]; }
    
    } //-- Exit ---
    
    return(0); }
    
    
    First, let's look at what Trade_BUY want for parameters, and then what we are generally going to give it:
    Code:
    What it wants        >>> Trade_BUY(int mn,int num,double factor1,double factor2,int sl)
    What we're giving it >>> Trade_BUY( Magic_BUY, Count_buy,w_price,w_trend, SL_buy)
    
    So given the extern (external) values at the beginning of the code, here's how we can interpret what we are feeding the Trade_BUY function:


    mn = Magic_BUY = 123
    num = Count_buy = 24
    factor1 = w_price = 0.18
    factor2 = w_trend = 0.18
    sl = SL_buy = 62

    So it's going to look like this:

    Code:
    Trade_BUY(123,24,0.18,0.18,62)
    
    The magic number - 123 - will first be used to filter once more. If, among any previous orders, there's an order whose number is 123, then this code will not allow another. It will exit and wait for another tick at the open of a new bar. But if there's no order like this that's already open or pending, then its going to give the next three of these parameters to the function Out() to calculate where to take profits so it can give a "target" to the OrderSend() function. Finally, it'll feed OrderSend() the last parameter - 62 - to be used as it's Stop Loss value. It's going to risk 62 points.

    Before we can understand exactly what's going to be fed to the OrderSend() function, we need to examine the Out() and lot() functions. I'll do that in the next thread.
     
  7. Good1

    Good1

    So here's how it plans on calculating a take-profit target:

    Code:
    //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    double Out(int n,double l1,double l2) { double t[120],
    s[120],
    lm=iMA(NULL,0,n,0,MODE_LWMA,PRICE_CLOSE,1),
    sm=iMA(NULL,0,n,0, MODE_SMA,PRICE_CLOSE,1);
    //ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
    t[n]=(6*lm-6*sm)/(n-1);s[n]=4*sm-3*lm-t[n];
    for (int k = n-1; k>0; k--) {
    s[k]=l1*Close[k]+(1-l1)*(s[k+1]+t[k+1]);
    t[k]=l2*(s[k]-s[k+1])+(1-l2)*t[k+1];
    }//--end--for-
    return (NormalizeDouble(s[1]+t[1],MarketInfo(Symbol(),MODE_DIGITS)));}
    
    Here's what were giving it:

    n = Count_buy = 24
    l1 = w_price = 0.18
    l2 = w_trend = 0.18

    The first thing Out() does is initialize four doubles:
    t[]
    s[]
    lm
    sm

    The first two are arrays dimensioned with 120 elements each. But it looks like, in this case, we are only going to use up to 24 each. The second two are the output of moving average functions that use Count_buy (24) as there averaging period. lm is a linear weighted moving average, and sm is a simple moving average, both based on the closing price of each bar.

    Here's where it get's as confusing as a battle scene in Lord of the Rings. I'll be substituting the known values when i can:

    Code:
    t[24]=(6*lm-6*sm)/(24-1);
    s[24]=4*sm-3*lm-t[24];
    
    Now, it that wasn't nebulous enough, let's loop backwards from s[24-1] to s[1] and then from t[24-1] to t[1]:

    Code:
    for (int k = 23; k>0; k--)
    {
        s[k] = 0.18 * Close[k] + (1 - 0.18) * (s[k+1]+t[k+1]);
        t[k] = 0.18 * (s[k] - s[k+1]) + (1 - 0.18) * t[k+1];
    }
    
    So, while we know we are going to risk 62 points, the take-profit target is based on some magical spell that only Gandolf would understand. At this juncture, i have no clue how these calculations actually work. What is the principle?

    Most likely, we've got really abnormal doubles to deal with now, so we have to normalize them before offering them to the SendOrder() function:

    Code:
     return (NormalizeDouble(s[1] + t[1],MarketInfo(Symbol(),MODE_DIGITS)));
    
    Is this somebody's idea of magic?
     
  8. Good1

    Good1

    Before we send these spells, i mean these codes to the SendOrder() function, let's find out how the trader (Gandolf) plans to size the trade (lot size).

    Code:
    //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    double lot(int R) { double minlot = MarketInfo(Symbol(), MODE_MINLOT);
    int o = MathAbs(MathLog(minlot) *0.4343) + 0.5;
    double lot = minlot;
    //ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
    lot = NormalizeDouble(AccountFreeMargin() * 0.00001*R, o);//---
    if (AccountFreeMargin() < lot * MarketInfo(Symbol(), MODE_MARGINREQUIRED)) {
    lot = NormalizeDouble(AccountFreeMargin() / MarketInfo(Symbol(), MODE_MARGINREQUIRED), o);
    }
    //ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
    if(lot < minlot) lot = minlot;
    double maxlot =MarketInfo(Symbol(), MODE_MAXLOT);
    if(lot > maxlot) lot = maxlot;
    return(lot); }
    
    We're going to feed this function the integer R, which is our Risk_buy value, which is currently set to 0. The first thing it does is initialize three numbers:

    minlot
    o
    lot

    Initially, it set's lot (lot size) to minlot (minimum number of lots possible), but that could change:

    Code:
    lot = NormalizeDouble(AccountFreeMargin() * 0.00001 * R, o);
    
    Because Risk-buy (R) is set to 0, lot is going to equal 0. That's ok, cause later, if lot is less than the minimum number of lot's possible, it'll just be reset to the minimum number of lot's possible:

    Code:
    if(lot < minlot) lot = minlot;
    
    If Risk_buy is set to anything above 0, then Gandolf will proportion the lot size relative to the accounts free margin. It'll grow in size till it hits a ceiling that's set by MarketInfo(symbol(),MODE_MAXLOT). But unless we can actually understand what this EA actually does, we're not likely to get there any time soon.

    With Risk_buy set to 0, we're trading with minimum lots, regardless the size of the account. I won't discuss how o is calculated while we are trading with minimum lots.
     
  9. Good1

    Good1

    So now we've called Trade_BUY, and have enough parameters to pass to SendOrder(). We know our stop loss, and we have a vague idea about our target (take-profit) price. But first, let's pass through one more filter:

    Code:
      if (target > (Bid + 15 * x * Point)  && IsTradeAllowed())
    
    We're still checking if we're allowed to trade because it's possible, though not probable, that other SendOrder() functions are being processed by other EAs that may be co-operating in this instance of the Metatrader terminal.

    Also, we want to know if the target is at least 15 points. I would hope so, given a standard risk of 62 points.

    Finally:

    Code:
    ticket = OrderSend(Symbol(), //Whatever symbol is on the same chart as the EA
    OP_BUY,                      //Buying position
    lot(Risk_buy),               //This is a function call that resolves to a minimum number of lots while Risk_buy is set to 0
    Ask,                         //We're buying, so we have to go with what the market is asking. 
    5,                           //I guess we're going to allow 5 pips slippage
    Bid-x*62*Point,              //Stop loss.  x will be either 1 or 10 depending on if the broker is four or five digits.  
    target,                      // At least we know it's more than 15 pips! 
    DoubleToStr(mn,0),           //Comments will quote the magic number as a string. 
    mn,                          //The magic number = 123
    0,                           //No order expiration
    Blue);                       //We want a blue arrow on the chart showing where this order was filled
    
    So, what i can gather so far is this EA will be buying and/or selling regardless of any criteria, on the opening of the next bar. It has a fixed stop loss and sets it's take profit target relative to some mumbo jumbo having to do with two different kinds of moving averages, both set with a look-back of 24. The stop loss could be much larger than the target. So its average win size is probably going to be much smaller than the average loss size. There will be many more wins than losses, if the target is consistently smaller than the stop loss. If it's profitable, it has something to do with how the target is calculated, and perhaps what the lot size will be relative to account margin.

    Currently, i'm not seeing how this could be a winner. I would think that it needs to be set to either buy or sell (not both) depending on another criteria, such as the perception of a trend (buying while trend is up etc.).
     
  10. Good1

    Good1

    So i tested this code for the year of Jan 2007 to Jan 2008.
    As is, this code does not look good.

    Strategy Tester Report
    gandalf_pro
    (Build 406)

    Symbol EURUSD (Euro vs US Dollar)
    Period 4 Hours (H4) 2007.12.31 00:00 - 2008.12.26 20:00 (2007.12.30 - 2008.12.28)
    Model Every tick (the most precise method based on all available least timeframes)
    Parameters In_BUY=true; Count_buy=24; w_price=0.18; w_trend=0.18; SL_buy=62; Risk_buy=0; In_SELL=true; Count_sell=24; m_price=0.18; m_trend=0.18; SL_sell=62; Risk_sell=0;
    Bars in test 2540
    Ticks modelled 4063991
    Modelling quality 90.00%
    Mismatched charts errors 0
    Initial deposit 10000.00
    Total net profit -2601.86
    Gross profit 15234.25
    Gross loss -17836.11
    Profit factor 0.85
    Expected payoff -4.56
    Absolute drawdown 2812.27
    Maximal drawdown 2916.69 (28.87%)
    Relative drawdown 28.87% (2916.69)
    Total trades 570
    Short positions (won %) 290 (53.45%)
    Long positions (won %) 280 (48.21%)
    Profit trades (% of total) 290 (50.88%)
    Loss trades (% of total) 280 (49.12%)
    Largest profit trade 297.21
    Largest loss trade -65.58
    Average profit trade 52.53
    Average loss trade -63.70


    [​IMG]
     
    #10     Oct 12, 2011