Summary: Bitcoin SafeGrid EA is an MQL4 grid trading expert advisor for Bitcoin. Uses adaptive grid spacing based on ATR, martingale-free lot sizing, and equity drawdown protection. Suitable for H4.




Bitcoin SafeGrid EA implements a conservative grid trading strategy specifically adapted for Bitcoin's high volatility. Unlike risky martingale systems, this EA uses fixed lot sizing with a safety module that prevents grid over-expansion. Grid spacing is dynamically calculated using ATR to adapt to current market volatility. The EA includes a maximum grid level limit, equity-based drawdown protection, and a profit target that closes the entire grid. No pyramiding or exponential lot growth.

Recommended Timeframe: H4
Trading Logic:
1. Grid Setup: Places initial buy and sell limit orders at distance = ATR(14) * GridDistanceMultiplier from current price.
2. Grid Expansion: When price triggers an order, a new limit order is placed at the same distance in the same direction (no martingale).
3. Risk Control: Maximum grid levels limited (MaxGridLevels), daily loss limit, and total drawdown protection.
4. Take Profit: When total floating profit of all grid orders reaches GridProfitTarget, close entire grid and reset.

```mql4
//+------------------------------------------------------------------+
//| BitcoinSafeGridEA.mq4 |
//+------------------------------------------------------------------+
#property copyright ""
#property link ""
#property version "1.00"
#property strict

//--- input parameters with comments
input double BaseLotSize = 0.01; // Fixed lot size for each grid order (0.01 BTC)
input int GridDistanceATR = 14; // ATR period for dynamic grid distance
input double GridDistanceMultiplier = 1.5; // Grid distance multiplier (distance = ATR * this)
input int MaxGridLevels = 5; // Maximum number of grid orders per direction
input double GridProfitTarget = 50.0; // Profit target in account currency (USD) to close grid
input double MaxDrawdownPercent = 12.0; // Maximum allowed drawdown percentage (hard stop)
input double DailyLossLimit = 6.0; // Daily loss limit as percentage of balance
input int MagicNumber = 202417; // Unique EA identifier
input int MaxSpread = 100; // Maximum allowed spread in points (BTC spread wider)
input bool UseFridayClose = true; // Close all orders before Friday 20:00 GMT
input int Slippage = 50; // Maximum slippage in points

//--- global variables
double dailyStartBalance = 0;
datetime lastBarTime = 0;
bool fridayCloseExecuted = false;
bool drawdownStop = false;
double currentGridDistance = 0;
double lastATR = 0;

//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
dailyStartBalance = AccountBalance();
lastBarTime = 0;
fridayCloseExecuted = false;
drawdownStop = false;
lastATR = iATR(Symbol(), PERIOD_H4, GridDistanceATR, 1);
if(lastATR <= 0) lastATR = 500 * Point;
currentGridDistance = lastATR * GridDistanceMultiplier;
return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment("");
}

//+------------------------------------------------------------------+
//| Get current ATR-based grid distance |
//+------------------------------------------------------------------+
double GetGridDistance()
{
double atr = iATR(Symbol(), PERIOD_H4, GridDistanceATR, 1);
if(atr <= 0) atr = lastATR;
lastATR = atr;
return atr * GridDistanceMultiplier;
}

//+------------------------------------------------------------------+
//| Count grid orders (buy and sell separately) |
//+------------------------------------------------------------------+
void CountGridOrders(int &buyCount, int &sellCount)
{
buyCount = 0;
sellCount = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY) buyCount++;
if(OrderType() == OP_SELL) sellCount++;
}
}
}
}

//+------------------------------------------------------------------+
//| Calculate total floating profit of grid |
//+------------------------------------------------------------------+
double GetGridFloatingProfit()
{
double profit = 0;
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
profit += OrderProfit() + OrderSwap() + OrderCommission();
}
}
}
return profit;
}

//+------------------------------------------------------------------+
//| Check if grid needs to be expanded |
//+------------------------------------------------------------------+
void ManageGridExpansion()
{
int buyCount, sellCount;
CountGridOrders(buyCount, sellCount);

double currentPrice = Bid;
double ask = Ask;
double gridDist = GetGridDistance();

// Find the highest buy order price and lowest sell order price
double highestBuy = 0;
double lowestSell = 9999999;

for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY && OrderOpenPrice() > highestBuy)
highestBuy = OrderOpenPrice();
if(OrderType() == OP_SELL && OrderOpenPrice() < lowestSell)
lowestSell = OrderOpenPrice();
}
}
}

// Expand buy grid: if price rises above highest buy + distance and not at max
if(buyCount < MaxGridLevels)
{
double nextBuyPrice = (highestBuy > 0) ? highestBuy + gridDist : currentPrice + gridDist;
if(currentPrice >= nextBuyPrice - (gridDist * 0.3))
{
double sl = nextBuyPrice - (gridDist * 2);
double tp = nextBuyPrice + (gridDist * 3);
int ticket = OrderSend(Symbol(), OP_BUY, BaseLotSize, ask, Slippage, sl, tp, "Grid Buy", MagicNumber, 0, clrNONE);
if(ticket > 0)
Print("Buy grid level ", buyCount+1, " placed at ", nextBuyPrice);
}
}

// Expand sell grid: if price falls below lowest sell - distance and not at max
if(sellCount < MaxGridLevels)
{
double nextSellPrice = (lowestSell < 9999999) ? lowestSell - gridDist : currentPrice - gridDist;
if(currentPrice <= nextSellPrice + (gridDist * 0.3))
{
double sl = nextSellPrice + (gridDist * 2);
double tp = nextSellPrice - (gridDist * 3);
int ticket = OrderSend(Symbol(), OP_SELL, BaseLotSize, Bid, Slippage, sl, tp, "Grid Sell", MagicNumber, 0, clrNONE);
if(ticket > 0)
Print("Sell grid level ", sellCount+1, " placed at ", nextSellPrice);
}
}
}

//+------------------------------------------------------------------+
//| Close all grid orders |
//+------------------------------------------------------------------+
void CloseAllGridOrders()
{
for(int i = OrdersTotal()-1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
if(OrderType() == OP_BUY)
OrderClose(OrderTicket(), OrderLots(), Bid, Slippage, clrNONE);
else if(OrderType() == OP_SELL)
OrderClose(OrderTicket(), OrderLots(), Ask, Slippage, clrNONE);
}
}
}
}

//+------------------------------------------------------------------+
//| Initialize initial grid orders if none exist |
//+------------------------------------------------------------------+
void InitializeGrid()
{
int buyCount, sellCount;
CountGridOrders(buyCount, sellCount);

if(buyCount == 0 && sellCount == 0)
{
double gridDist = GetGridDistance();
double currentPrice = (Ask + Bid) / 2;

// Place first buy limit order below current price
double firstBuy = currentPrice - gridDist;
double buySL = firstBuy - (gridDist * 2);
double buyTP = firstBuy + (gridDist * 3);

// Place first sell limit order above current price
double firstSell = currentPrice + gridDist;
double sellSL = firstSell + (gridDist * 2);
double sellTP = firstSell - (gridDist * 3);

int buyTicket = OrderSend(Symbol(), OP_BUY, BaseLotSize, firstBuy, Slippage, buySL, buyTP, "Grid Buy", MagicNumber, 0, clrNONE);
int sellTicket = OrderSend(Symbol(), OP_SELL, BaseLotSize, firstSell, Slippage, sellSL, sellTP, "Grid Sell", MagicNumber, 0, clrNONE);

if(buyTicket > 0 && sellTicket > 0)
Print("Initial grid placed. Buy at ", firstBuy, " Sell at ", firstSell);
}
}

//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Daily equity protection
double currentEquity = AccountEquity();
double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
if(lossPercent >= DailyLossLimit)
{
Comment("Daily loss limit reached. No new grid expansion.");
if(GetGridFloatingProfit() < 0) return;
}

// Max drawdown protection (hard stop)
double drawdownPercent = (dailyStartBalance - currentEquity) / dailyStartBalance * 100;
if(drawdownPercent >= MaxDrawdownPercent && !drawdownStop)
{
CloseAllGridOrders();
drawdownStop = true;
Comment("Max drawdown reached. Grid stopped.");
return;
}

// Friday close before weekend
if(UseFridayClose && !fridayCloseExecuted)
{
datetime currentTime = TimeCurrent();
if(TimeDayOfWeek(currentTime) == 5 && TimeHour(currentTime) >= 20)
{
CloseAllGridOrders();
fridayCloseExecuted = true;
return;
}
if(TimeDayOfWeek(currentTime) != 5)
fridayCloseExecuted = false;
}

// Spread filter for Bitcoin
if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
{
Comment("Spread too high for BTC: ", MarketInfo(Symbol(), MODE_SPREAD));
return;
}

// New bar logic (H4)
if(Time[0] == lastBarTime)
return;
lastBarTime = Time[0];

// Check if grid exists, if not initialize
int buyCount, sellCount;
CountGridOrders(buyCount, sellCount);
if(buyCount == 0 && sellCount == 0 && !drawdownStop)
{
InitializeGrid();
return;
}

// Manage grid expansion based on price movement
ManageGridExpansion();

// Take profit: close entire grid when total profit reaches target
double totalProfit = GetGridFloatingProfit();
if(totalProfit >= GridProfitTarget)
{
CloseAllGridOrders();
Print("Grid profit target reached: ", totalProfit, ". Grid reset.");
// Reset drawdown flag for next cycle
drawdownStop = false;
dailyStartBalance = AccountBalance();
}

// Daily reset of start balance
if(TimeDayOfYear(TimeCurrent()) != TimeDayOfYear(TimeCurrent() - PeriodSeconds(PERIOD_D1)))
{
dailyStartBalance = AccountBalance();
}
}

//+------------------------------------------------------------------+
//| Expert tick function for pending order management |
//+------------------------------------------------------------------+
// Note: This EA uses instant execution orders (OP_BUY/OP_SELL) placed at limit prices.
// For pure limit order grid, modify OrderSend to use OP_BUYLIMIT/OP_SELLLIMIT.
// Current implementation sends marketable limit orders that execute immediately.
//+------------------------------------------------------------------+
```
Reference: Original MQL4 code for educational purposes.
Disclaimer: Bitcoin grid trading carries significant risk including total loss. This EA uses fixed lot sizes (no martingale) but grid strategies can still lose in strong trends. This EA is provided as-is without any guarantee of profit. Test thoroughly on demo for at least 2 months before live trading. Past performance does not guarantee future results.