Grid trading idea - comments sought

 

Greets,

Here's a quick implementation of a grid trading idea showcased here

I'm not sure if I've made a mistake, or if the concept simply doesn't work, but it always results in a loss in backtesting. Maybe someone can have a look, play with it, and possibly give ideas.

Since the strategy apparently does better in ranging conditions, I've added the ADX check. You can also decide to use a fixed point grid gap size, or use multiples of ATR.

Have a go and let me know.

#include <Trade/Trade.mqh>
#include <Trade/AccountInfo.mqh>
#include <errorDescription.mqh>

input int eaMagic = 989898;
input string eaComment = "Grid";
input int gridSpacePoints = 400;                // Default Grid Space in Points (0 to use ATR)
input double atrFactor = 1;                             // Fraction of ATR for Grid Spacing
input int atrPeriod = 100;                              // ATR Period
input int adxPeriod = 14;                               // ADX Period
input int adxLevel = 25;                                // ADX Level
input double lotSize = 0.01;                    // Lots per trade
input double riskPercent = 5;                   // Risk % per trade (or 0 to use lots)

CTrade trade;
CAccountInfo accountInfo;

int ATR;
int ADX;

int OnInit() {
        trade.SetExpertMagicNumber(eaMagic);
        
        ATR = iATR(_Symbol, _Period, atrPeriod);
        if (ATR == INVALID_HANDLE) {
                PrintFormat("Failed to create handle of the ATR indicator for the symbol %s/%s, error code %d", 
                        _Symbol, EnumToString(_Period), GetLastError()); 
                return INIT_FAILED;
        }
        ADX = iADX(_Symbol, _Period, adxPeriod);
        if (ADX == INVALID_HANDLE) {
                PrintFormat("Failed to create handle of the ADX indicator for the symbol %s/%s, error code %d", 
                        _Symbol, EnumToString(_Period), GetLastError()); 
                return INIT_FAILED;
        }
        return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {}

void OnTick() {
        if (!countPositions()) openPositions();
}

void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) {
        if(trans.deal == TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal)) {
                long reason = HistoryDealGetInteger(trans.deal, DEAL_REASON);
                if ((reason == DEAL_REASON_SL || reason == DEAL_REASON_TP) && HistoryDealGetInteger(trans.deal, DEAL_MAGIC) == eaMagic)
                        openPositions();
        }
}

void openPositions() {
        if (getADX() > adxLevel) return;
        
        double lots = gridSpacePoints > 0 ? calcLots(gridSpacePoints * _Point) : calcLots(getATR() * atrFactor);
        
        double spacing = gridSpacePoints > 0 ? gridSpacePoints * _Point : getATR() * atrFactor;
        double ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
        double tp = NormalizeDouble(ask + spacing, _Digits);
        double sl = NormalizeDouble(ask - spacing, _Digits);
        
        if (!trade.Buy(lots, _Symbol, ask, sl, tp, eaComment)) {
                Print("ERROR: Buy failed for ", _Symbol, " at ", ask, ". Error ", GetLastError());
                return;
        }
        
        double bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
        tp = NormalizeDouble(bid - spacing, _Digits);
        sl = NormalizeDouble(bid + spacing, _Digits);
        
        if (!trade.Sell(lots, _Symbol, bid, sl, tp, eaComment)) {
                Print("ERROR: Sell failed for ", _Symbol, " at ", bid, ". Error ", GetLastError());
                return;
        }
}

int countPositions() {
        int count = 0;
        for (int i=PositionsTotal()-1; i>=0; i--) {
                if (PositionSelectByTicket(PositionGetTicket(i)))
                        if (PositionGetInteger(POSITION_MAGIC) == eaMagic && 
                                (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY || PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)) count++;
        }
        return count;
}

double calcLots(double stopLoss) {
        // set default to be fixed lot size
        double lots = lotSize;
        
        // use risk percent if user sets in input
        if (riskPercent > 0) {
                double riskAmount = AccountInfoDouble(ACCOUNT_BALANCE)  *  (riskPercent/100);
                double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
          
                lots = (riskAmount / (stopLoss/_Point)) / tickValue;
        }
        lots = lots < SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) ? lotSize : lots;
        lots = lots > SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX) ? lotSize : lots;
        return NormalizeDouble(lots, 2);
}

double getATR() {
        double buff[];
        
        if (CopyBuffer(ATR, 0, 0, 1, buff) < 0)
                Print("ERROR: CopyBuffer() for ATR failed: ", GetLastError());
        return buff[0];
}

double getADX() {
        double buff[];
        
        if (CopyBuffer(ADX, 0, 0, 1, buff) < 0)
                Print("ERROR: CopyBuffer() for ADX failed: ", GetLastError());
        return buff[0];
}
100 Percent success Grid Trading Explained. Trade with no charts. Market direction is not important.
100 Percent success Grid Trading Explained. Trade with no charts. Market direction is not important.
  • 2021.01.24
  • www.youtube.com
10% a Month - DO OUR 14-DAY LINKED/COPY SERVICE TRIAL: https://bestforexrobots.reviews/Linked-14Day-TrialYOUTUBE SPECIAL APRIL 2023, 50% OFF Trading Robots -...
 
HenZen:

Greets,

Here's a quick implementation of a grid trading idea showcased here

I'm not sure if I've made a mistake, or if the concept simply doesn't work, but it always results in a loss in backtesting. Maybe someone can have a look, play with it, and possibly give ideas.

Since the strategy apparently does better in ranging conditions, I've added the ADX check. You can also decide to use a fixed point grid gap size, or use multiples of ATR.

Have a go and let me know.

Of course it doesn’t work … none of them grid/martingale works 
 

@HenZen

There are many ways to create a grid and/or make the distance between a grid to be dynamic, and do NOT believe the dooms dayers that put down grid trading. a grid can be very helpful in trading. The only reason that grid trading has a bad name is the same reason that most traders who use eas lose: because they dont monitor their account and rely on the ea to trade for them. EAs are not meant to do the trading for you; but to, only: assist the trader.

However, grid trading is the most risky of all trading systems there are. But if you close the trades to avoid bad markets, as you should with any ea, and do not try to take too much profit; dont be greedy, and monitor every trade, every day, then you will learn when to "hit and run' and when it is a bad day to trade.

I suggest that you search codebase. There are hundreds of different grid eas on there, and even more free ones on the marketplace, all with lots of trading options and even more options to close trades. Do not try to reinvent the wheel. Use one from codebase and make changes to the code to suite your trading style.

 

EDIT-

I've just noticed now that this is a few months old thread,

Who got 'bumped' because of another user's comment.

I'm guessing it is probably irrelevant by now,

But I leave my comment below anyway, in case it is still needed.

Cheers.



Hey mate,


First I just want to say that I personally don't believe that grid trading is profitable (maybe only on a very short run, and in very specific markets conditions),


I am not an native English speaker, so I'll try to keep it short and clear,

If you need more explanation just tell me.


I haven't gone thoroughly thru your code,

But at a quick glance few things popped into my eyes-


1-

The 'strategy' shown in the video does not consist of setting a S/L, but letting 'losing' trades just 'float' until it evens with grid.

Your T/P and S/L are (supposedly) at the same levels,

So your code always generate zero-sum transactions,

But because of fees, your code always lose money when hits T/P and S/L.

(If assuming 0 spread, for simplicity)


2-

I might be mistaken, since I haven't tested it,

But from a quick glance I think that your code doubles the amount of positions each time.

You call openPositions() if you hit T/P level and also if you hit S/L level.

So, for example, you open the first buy and sell orders (lets assume 0 spread and instant execution, for simplicity),

So you reach first level (lets say that price went up),

So your buy hit T/P and your code calls openPositions(),

But also your sell hit S/L and your code calls openPositions() again,

So now you have 2 buy + 2 sell,

And, again, each of the above positions (2 buy + 2 sell = 4 total positions) will open 1 buy and one sell (4 total positions opening 1 buy and 1 sell each),

So next time you will have 4 buy + 4 sell,

etc'

etc'

etc'



Cheers.