Skip to content

Instantly share code, notes, and snippets.

@currencysecrets
Last active May 13, 2017 19:21
Show Gist options
  • Save currencysecrets/5377088 to your computer and use it in GitHub Desktop.
Save currencysecrets/5377088 to your computer and use it in GitHub Desktop.
This was the simple breakout system designed for the series on creating a MetaTrader 4 system in Sublime Text's text editor. The series can be found on our website http://www.currencysecrets.com.
//+------------------------------------------------------------------+
//| Simple Channel Breakout System.mq4
//| Copyright 2013, Ryan Sheehy
//| http://www.currencysecrets.com
//| v1.1 - errors fixed (getLots & multipleOrders)
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, Ryan Sheehy"
#property link "http://www.currencysecrets.com"
//--- input parameters
extern int ExtParam1=0;
extern int ExtParam2=0;
extern int extSlippage = 50; // slippage for entry orders
extern double extRiskPerTrade = 100; // risk amount in base currency per trade
datetime now;
//+------------------------------------------------------------------+
//| expert initialization function |
//+------------------------------------------------------------------+
int init()
{
//----
//----
return(0);
}
//+------------------------------------------------------------------+
//| expert deinitialization function |
//+------------------------------------------------------------------+
int deinit()
{
//----
//----
return(0);
}
//+------------------------------------------------------------------+
//| expert start function |
//+------------------------------------------------------------------+
int start()
{
//----
int ord;
// running this area on a change of every bar on the chart
if ( now != Time[0] ) {
// checking entry orders
ord = checkEntryOrders(Symbol());
if ( ord == 0 ) {
// we need to create LONG & SHORT orders
placeNewOrder( OP_BUYSTOP, Symbol() );
placeNewOrder( OP_SELLSTOP, Symbol() );
} else if ( ord == 1 ) {
// need to place SHORT order only
placeNewOrder( OP_SELLSTOP, Symbol() );
} else if ( ord == 2 ) {
// need to place LONG order only
placeNewOrder( OP_BUYSTOP, Symbol() );
}
now = Time[0];
} else {
// running every tick
// check trailing & initial stops on any orders that are active
// for the active symbol
checkStops(Symbol());
}
//----
return(0);
}
//+------------------------------------------------------------------+
void checkStops(string sym) {
// in this function we will be checking the open trade to this
// trailing stop function
// if the price of the currency has moved beyond the trailing stop - exit
// if not leave alone
// if the trailing stop is beyond the opening price, move the stop to
// breakeven
int tot = OrdersTotal();
int trade;
double trStop;
for ( int i = 0; i < tot; i++ ) {
if ( OrderSelect( i, SELECT_BY_POS, MODE_TRADES)) {
if ( OrderSymbol() == sym ) {
if ( OrderType() == OP_BUY ) {
// get trailing stop for LONG orders
trStop = getTrailingStop( sym, OrderOpenTime(), true );
// check for breakeven stop
if ( trStop >= OrderOpenPrice() && OrderStopLoss() < OrderOpenPrice() ) {
trade = _IsTradeAllowed();
if ( trade < 0 ) {
break;
} else if ( trade == 0 ) {
RefreshRates();
}
// stop loss moves to breakeven - the order's open price
OrderModify( OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), OrderExpiration() );
}
// check if price has moved below trailing stop, if so exit
if ( MarketInfo( sym, MODE_BID ) < trStop ) {
trade = _IsTradeAllowed();
if ( trade < 0 ) {
break;
} else if ( trade == 0 ) {
RefreshRates();
}
OrderClose( OrderTicket(), OrderLots(), MarketInfo( sym, MODE_BID ), extSlippage );
}
}
if ( OrderType() == OP_SELL ) {
trStop = getTrailingStop( sym, OrderOpenTime(), false );
// check for breakeven stop
if ( trStop <= OrderOpenPrice() && OrderStopLoss() > OrderOpenPrice() ) {
trade = _IsTradeAllowed();
if ( trade < 0 ) {
break;
} else if ( trade == 0 ) {
RefreshRates();
}
// stop loss moves to breakeven - the order's open price
OrderModify( OrderTicket(), OrderOpenPrice(), OrderOpenPrice(), OrderTakeProfit(), OrderExpiration() );
}
// check if price has moved below trailing stop, if so exit
if ( MarketInfo( sym, MODE_ASK ) > trStop ) {
trade = _IsTradeAllowed();
if ( trade < 0 ) {
break;
} else if ( trade == 0 ) {
RefreshRates();
}
OrderClose( OrderTicket(), OrderLots(), MarketInfo( sym, MODE_ASK ), extSlippage );
}
}
}
}
}
}
double getTrailingStop( string sym, datetime openTime, bool dir ) {
// this function will return the trailing stop of the symbol
double result;
if ( dir == true ) {
result = 0;
} else {
result = 10000;
}
int startBar;
startBar = getOpenBar( sym, openTime );
for ( int i = startBar; i < Bars; i++ ) {
if ( dir == true ) {
// for LONG orders
// loop through the bars since opentime
result = MathMax( High[i] - (5 * iATR( sym, 0, 30, i )), result );
}
if ( dir == false ) {
// for SHORT orders
result = MathMin( Low[i] + (5 * iATR( sym, 0, 30, i)), result);
}
}
return( result );
}
int getOpenBar( string sym, datetime openTime ) {
// each bar has the opening time at the start of the bar
// eg if openTime is 12:01PM we will know at 4:00PM (when the
// bar is greater than the opening time of trade)
for ( int i = 0; i < Bars; i++ ) {
if ( openTime > Time[i] ) {
if ( i == 0 ) {
return( 0 );
} else {
return( i + 1 );
}
}
}
}
void checkMultipleOrders( int ord, int tkt, string sym ) {
// check for multiple orders for the sym currency
// remove any pending orders OR open orders
int tot = OrdersTotal();
for( int i = 0; i < tot; i++ ) {
if ( OrderSelect( i, SELECT_BY_POS, MODE_TRADES) ) {
if ( OrderSymbol() == sym ) {
if ( OrderTicket() != tkt ) {
if ( ord == OP_BUY ) {
if ( OrderType() == OP_BUYSTOP ) {
OrderDelete( OrderTicket() );
}
if ( OrderType() == OP_BUY ) {
OrderClose( OrderTicket(), OrderLots(), Bid, extSlippage );
}
} else if ( ord == OP_SELL ) {
if ( OrderType() == OP_SELLSTOP ) {
OrderDelete( OrderTicket() );
}
if ( OrderType() == OP_SELL ) {
OrderClose( OrderTicket(), OrderLots(), Ask, extSlippage );
}
}
}
}
}
}
return( 0 );
}
int checkEntryOrders(string sym) {
// check whether there is an entry order active on the symbol
// if there is we want to check whether it needs to be moved
// if so, then we want to amend the order accordingly
// if not - do place a new entry order
// if JUST long = 1
// if JUST short = 2
// if both LONG & SHORT = 3
// if neither = 0
double upperChannel, lowerChannel, iniStop, vol;
int trade, result = 0, tot = OrdersTotal();
for( int i = 0; i < tot; i++ ){
if ( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) {
if ( OrderSymbol() == sym ) {
// we have an active order on this symbol!
if ( OrderType() == OP_BUYSTOP ) {
// check for multiple orders and pending orders
checkMultipleOrders( OrderType(), OrderTicket(), sym );
result += 1;
upperChannel = getChannel( true, sym );
iniStop = getInitialStop( true, sym, upperChannel );
if ( upperChannel != OrderOpenPrice() ) {
// amend order
trade = _IsTradeAllowed();
if ( trade < 0 ) {
break;
} else if ( trade == 0 ) {
RefreshRates();
}
OrderModify( OrderTicket(), OrderOpenPrice(), iniStop, OrderTakeProfit(), OrderExpiration(), CLR_NONE );
}
} else if ( OrderType() == OP_SELLSTOP ) {
checkMultipleOrders( OrderType(), OrderTicket(), sym );
result += 2;
lowerChannel = getChannel( false, sym );
iniStop = getInitialStop( false, sym, lowerChannel );
if ( lowerChannel != OrderOpenPrice() ) {
// amend order
trade = _IsTradeAllowed();
if ( trade < 0 ) {
break;
} else if ( trade == 0 ) {
RefreshRates();
}
OrderModify( OrderTicket(), OrderOpenPrice(), iniStop, OrderTakeProfit(), OrderExpiration(), CLR_NONE );
}
}
}
}
}
return( result );
}
int placeNewOrder( int ordType, string sym ) {
double channel, iniStop, vol;
int tkt, trade;
// if no long entry order is active then create new long order
if ( ordType == OP_BUYSTOP ) {
channel = getChannel( true, sym );
iniStop = getInitialStop( true, sym, channel );
vol = getLots( sym, channel, iniStop, extRiskPerTrade );
trade = _IsTradeAllowed();
if ( trade < 0 ) {
return( 0 );
} else if ( trade == 0 ) {
RefreshRates();
}
tkt = OrderSend( sym, OP_BUYSTOP, vol, channel, extSlippage, iniStop, 0 );
if ( tkt <= 0 ) {
Print( "ERR sending BUYSTOP order for " + sym + " err: " + GetLastError());
} else {
return (tkt);
}
}
// if no short entry order is active then create new short order
if ( ordType == OP_SELLSTOP ) {
channel = getChannel( false, sym );
iniStop = getInitialStop( false, sym, channel );
vol = getLots( sym, channel, iniStop, extRiskPerTrade );
trade = _IsTradeAllowed();
if ( trade < 0 ) {
return( 0 );
} else if ( trade == 0 ) {
RefreshRates();
}
tkt = OrderSend( sym, OP_SELLSTOP, vol, channel, extSlippage, iniStop, 0 );
if ( tkt <= 0 ) {
Print( "ERR sending SELLSTOP order for " + sym + " err: " + GetLastError());
} else {
return ( tkt );
}
}
return( 0 );
}
double getLots( string sym, double entryPrice, double stopLoss, double riskAmount ) {
double stopDist, result;
stopDist = MathAbs( entryPrice - stopLoss ) + ( MarketInfo( sym, MODE_SPREAD ) * MarketInfo( sym, MODE_POINT ) );
stopDist *= MarketInfo( sym, MODE_TICKVALUE );
// 100 / 0.0100 = 10000 contracts, 1 std contract = 100,000: 1 mini = 1,000 contracts
result = MathFloor( riskAmount / stopDist );
result = NormalizeDouble( result / MathPow( 10, MarketInfo( sym, MODE_DIGITS ) ), 2);
return ( result );
}
// this function gets the highest and lowest prices over the last 120 bars
// and applies a volatility amount (1.5 ATR(30))
double getChannel( bool upper, string sym ) {
// look back over the previous X candles to determine channel boundaries
double result;
if ( upper ) {
result = High[iHighest( sym, 0, MODE_HIGH, 120, 1 )];
result += iATR( sym, 0, 30, 1 ) * 1.5;
} else {
result = Low[iLowest( sym, 0, MODE_LOW, 120, 1 )];
result -= iATR( sym, 0, 30, 1 ) * 1.5;
}
// Normalize value
result = NormalizeDouble( result, MarketInfo( sym, MODE_DIGITS ) );
return( result );
}
// this function will get the initial stop value of the active symbol
double getInitialStop( bool long, string sym, double entry ) {
double result;
if ( long ) {
result = entry - iATR( sym, 0, 30, 1 ) * 1.5;
} else {
result = entry + iATR( sym, 0, 30, 1 ) * 1.5;
}
result = NormalizeDouble( result, MarketInfo( sym, MODE_DIGITS ) );
return( result );
}
/////////////////////////////////////////////////////////////////////////////////
// int _IsTradeAllowed( int MaxWaiting_sec = 30 )
//
// the function checks the trade context status. Return codes:
// 1 - trade context is free, trade allowed
// 0 - trade context was busy, but became free. Trade is allowed only after
// the market info has been refreshed.
// -1 - trade context is busy, waiting interrupted by the user (expert was removed from
// the chart, terminal was shut down, the chart period and/or symbol was changed, etc.)
// -2 - trade context is busy, the waiting limit is reached (MaxWaiting_sec).
// Possibly, the expert is not allowed to trade (checkbox "Allow live trading"
// in the expert settings).
//
// MaxWaiting_sec - time (in seconds) within which the function will wait
// until the trade context is free (if it is busy). By default,30.
/////////////////////////////////////////////////////////////////////////////////
int _IsTradeAllowed(int MaxWaiting_sec = 30)
{
// check whether the trade context is free
if(!IsTradeAllowed())
{
int StartWaitingTime = GetTickCount();
Print("Trade context is busy! Wait until it is free...");
// infinite loop
while(true)
{
// if the expert was terminated by the user, stop operation
if(IsStopped())
{
Print("The expert was terminated by the user!");
return(-1);
}
// if the waiting time exceeds the time specified in the
// MaxWaiting_sec variable, stop operation, as well
if(GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000)
{
Print("The waiting limit exceeded (" + MaxWaiting_sec + " ???.)!");
return(-2);
}
// if the trade context has become free,
if(IsTradeAllowed())
{
Print("Trade context has become free!");
return(0);
}
// if no loop breaking condition has been met, "wait" for 0.1
// second and then restart checking
Sleep(100);
}
}
else
{
Print("Trade context is free!");
return(1);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment