Last active
May 13, 2017 19:21
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//+------------------------------------------------------------------+ | |
//| 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