Last active
August 22, 2018 13:10
-
-
Save currencysecrets/9672694 to your computer and use it in GitHub Desktop.
Entries and exits now in one script
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
string VERSION = "TL BO 1.0"; | |
extern int EXT_ATR = 300; // ATR(x) | |
extern double EXT_ATR_INI_STOP = 0.1; // additional buffer to add beyond the initial stop | |
extern int EXT_LOOKBACK = 3; // number of bars to find highest high/lowest low | |
extern double EXT_RISK_MINAMT = 50; // dollar value of the minimum amount to risk per trade | |
extern double EXT_RISK_DIVISOR = 10; // AccountBalance/X = risk per trade | |
extern int EXT_MAX_SLIP = 10; // maximum points of slippage allowed for order 10 = 1 pip | |
extern double EXT_ATR_TRAILSTOP = 2; // multiple for trailing the market ATR(EXT_ATR) * X | |
// global variables | |
int GATE_ACTIVE, TRADE_TKT; | |
double EXIT_PRICE, TRADE_LOTS; | |
//+------------------------------------------------------------------+ | |
//| expert initialization function | | |
//+------------------------------------------------------------------+ | |
int init() | |
{ | |
//---- | |
string sym = Symbol(); | |
// check if currency is active | |
GATE_ACTIVE = isActive( sym, false ); | |
//---- | |
return(0); | |
} | |
//+------------------------------------------------------------------+ | |
//| expert deinitialization function | | |
//+------------------------------------------------------------------+ | |
int deinit() | |
{ | |
//---- | |
//---- | |
return(0); | |
} | |
//+------------------------------------------------------------------+ | |
//| expert start function | | |
//+------------------------------------------------------------------+ | |
int start() | |
{ | |
//---- | |
string sym = Symbol(); | |
int per = Period(); | |
if ( GATE_ACTIVE == -1 ) { | |
if ( Bid > iHigh( sym, per, 1 ) + iATR( sym, per, EXT_ATR, 1 ) ) doBuy( sym, per ); | |
if ( Ask < iLow( sym, per, 1 ) - iATR( sym, per, EXT_ATR, 1 ) ) doSell( sym, per ); | |
} else { | |
// first we'll check to see if the exit price has been hit | |
// LONG trades | |
if ( GATE_ACTIVE == OP_BUY && EXIT_PRICE > Bid && EXIT_PRICE > 0 ) | |
exitNow( sym, TRADE_TKT, TRADE_LOTS, Bid ); | |
// SHORT trades | |
if ( GATE_ACTIVE == OP_SELL && Ask > EXIT_PRICE && EXIT_PRICE > 0 ) | |
exitNow( sym, TRADE_TKT, TRADE_LOTS, Ask ); | |
// if the gate is active let's analyse whether it's stop loss needs amending | |
GATE_ACTIVE = isActive( sym, true ); | |
} | |
//---- | |
return(0); | |
} | |
//+------------------------------------------------------------------+ | |
// placing an at-market sell order | |
int doSell( string sym, int per ) { | |
// check whether the system is allowed to place a trade | |
int trade = checkIsTradeAllowed(), tkt = 0; | |
if ( trade == 0 ) RefreshRates(); | |
if ( trade > 0 ) { | |
// order type | |
int ord = OP_SELL; | |
// get entry price | |
double entry = Bid; | |
// get spread as SELL orders pay the spread on their exit | |
double spread = MarketInfo( sym, MODE_SPREAD ) * MarketInfo( sym, MODE_POINT ); | |
// get initial stop loss - we will use the highest high over X periods plus a buffer | |
double exit = N( iHigh( sym, per, iHighest( sym, per, MODE_HIGH, EXT_LOOKBACK, 0 ) ) + ( EXT_ATR_INI_STOP * iATR( sym, per, EXT_ATR, 1 ) ) ); | |
// add spread to initial stop loss value | |
exit += spread; | |
// check if stop loss is beyond the currency's stop level | |
double stopPt = MarketInfo( sym, MODE_STOPLEVEL ) * MarketInfo( sym, MODE_POINT ); | |
// if not amend the exit to meet stop level requirements | |
if ( MathAbs( entry - exit ) < stopPt ) exit = entry + stopPt; | |
// get the amount to risk per trade | |
double riskAmt = getRiskAmount(); | |
// now calculate what the amount risked per trade equates to in lot size | |
double lots = MathMax( getLots( sym, entry, exit, riskAmt ), MarketInfo( sym, MODE_LOTSTEP ) ); | |
// as we have an at market ordering process we don't need an expiry value | |
datetime expy = 0; | |
// storing a value for the MagicNumber can be subjective, you can do whatever you want, I will just use | |
// the active chart's Period() value | |
int magic = per; | |
// our last minute check to see that we DON'T have an active open order on this currency | |
// if we do exit this function with 0 | |
if ( isActive( sym, false ) ) return( 0 ); | |
// submit AT MARKET sell order | |
tkt = OrderSend( sym, ord, lots, entry, EXT_MAX_SLIP, exit, 0, VERSION, magic, expy, 0 ); | |
if ( tkt > 0 ) { | |
// change the active currency's GATE_ACTIVE flag to true | |
GATE_ACTIVE = ord; | |
// set global exit price | |
EXIT_PRICE = exit; | |
// our entry order has been placed let's now notify ourselves of the order | |
SendMail( "NEW SELL Order " + sym, | |
"Entry = " + D( entry ) + "\n" + | |
"StopLoss = " + D( exit ) + "\n" + | |
"Initial Risk = " + D( riskAmt, 2 ) + "\n" + | |
"Actual Risk = " + D( profitAtStop( sym, entry, exit, lots, OP_SELL ), 2 ) + "\n" + | |
"Lots = " + D( lots, 2 ) + "\n" + | |
"Expiry = " + TimeToStr( expy ) + "\n" + | |
"MagicNum = " + magic + "\n" + | |
"HHV = " + D( iHigh( sym, per, iHighest( sym, per, MODE_HIGH, EXT_LOOKBACK, 0 ) ) ) + "\n" + | |
"Version = " + VERSION + "\n" | |
); | |
} else if ( tkt == -1 ) { | |
// here's what we will do when something goes wrong - try to return as much details as possible | |
if ( GetLastError() > 0 ) { | |
SendMail( "ERR with NEW SELL Order " + sym, | |
"Entry = " + D( entry ) + "\n" + | |
"StopLoss = " + D( exit ) + "\n" + | |
"Initial Risk = " + D( riskAmt, 2 ) + "\n" + | |
"Actual Risk = " + D( profitAtStop( sym, entry, exit, lots, OP_SELL ), 2 ) + "\n" + | |
"Lots = " + D( lots, 2 ) + "\n" + | |
"Expiry = " + TimeToStr( expy ) + "\n" + | |
"MagicNum = " + magic + "\n" + | |
"HHV = " + D( iHigh( sym, per, iHighest( sym, per, MODE_HIGH, EXT_LOOKBACK, 0 ) ) ) + "\n" + | |
"Version = " + VERSION + "\n" | |
); | |
} | |
} | |
} | |
return ( tkt ); | |
} | |
int doBuy( string sym, int per ) { | |
int trade = checkIsTradeAllowed(), tkt = 0; | |
if ( trade == 0 ) RefreshRates(); | |
if ( trade > 0 ) { | |
int ord = OP_BUY; | |
double entry = Ask; // spread paid at entry | |
double exit = N( iLow( sym, per, iLowest( sym, per, MODE_LOW, EXT_LOOKBACK, 0 ) ) - ( EXT_ATR_INI_STOP * iATR( sym, per, EXT_ATR, 1 )) ); // bid price | |
double stopPt = MarketInfo( sym, MODE_STOPLEVEL ) * MarketInfo( sym, MODE_POINT ); | |
if ( MathAbs( entry - exit ) < stopPt ) exit = entry - stopPt; | |
double riskAmt = getRiskAmount(); | |
double lots = MathMax( getLots( sym, entry, exit, riskAmt ), MarketInfo( sym, MODE_LOTSTEP ) ); | |
datetime expy = 0; | |
int magic = per; | |
if ( isActive( sym, false ) ) return( 0 ); // last minute check! | |
// submit AT MARKET entry order | |
tkt = OrderSend( sym, ord, lots, entry, EXT_MAX_SLIP, exit, 0, VERSION, magic, expy, 0 ); | |
if ( tkt > 0 ) { | |
GATE_ACTIVE = ord; | |
EXIT_PRICE = exit; | |
SendMail( "NEW BUY Order for " + sym, | |
"Entry = " + D( entry ) + "\n" + | |
"StopLoss = " + D( exit ) + "\n" + | |
"Initial Risk = " + D( riskAmt, 2 ) + "\n" + | |
"Actual Risk = " + D( profitAtStop( sym, entry, exit, lots, OP_BUY ), 2 ) + "\n" + | |
"Lots = " + D( lots, 2 ) + "\n" + | |
"Expiry = " + TimeToStr( expy ) + "\n" + | |
"MagicNum = " + magic + "\n" + | |
"LLV = " + D( iLow( sym, per, iLowest( sym, per, MODE_LOW, EXT_LOOKBACK, 0 ) ) ) + "\n" + | |
"Version = " + VERSION | |
); | |
} else if ( tkt == -1 ) { | |
if ( GetLastError() > 0 ) { | |
SendMail( "ERR with NEW BUY Order for " + sym, | |
"Entry = " + D( entry ) + "\n" + | |
"StopLoss = " + D( exit ) + "\n" + | |
"Initial Risk = " + D( riskAmt, 2 ) + "\n" + | |
"Actual Risk = " + D( profitAtStop( sym, entry, exit, lots, OP_BUY ), 2 ) + "\n" + | |
"Lots = " + D( lots, 2 ) + "\n" + | |
"Expiry = " + TimeToStr( expy ) + "\n" + | |
"MagicNum = " + magic + "\n" + | |
"LLV = " + D( iLow( sym, per, iLowest( sym, per, MODE_LOW, EXT_LOOKBACK, 0 ) ) ) + "\n" + | |
"Version = " + VERSION | |
); | |
} | |
} | |
} | |
return( tkt ); | |
} | |
// amended from http://articles.mql4.com/141 | |
int checkIsTradeAllowed( uint MaxWaiting_sec = 30 ) { | |
// check firstly whether it's the end of the forex day | |
// Pepperstone will not allow us to trade between 17:00 - 17:05 NY EST | |
if ( Hour() == 0 && Minute() < 5 ) { | |
Print( "Cannot trade during this time, closing market" ); | |
return( -1 ); | |
} | |
if ( !IsTradeAllowed() ) { | |
uint StartWaitingTime = GetTickCount(); | |
while( true ) { | |
if ( IsStopped() ) { | |
Print("The expert was terminated by the user!"); | |
return( -1 ); | |
} | |
if ( GetTickCount() - StartWaitingTime > MaxWaiting_sec * 1000 ) { | |
Print("Waiting limit exceeded"); | |
return( -2 ); | |
} | |
if ( IsTradeAllowed() ) return( 0 ); | |
Sleep( 100 ); | |
} | |
} else { | |
return( 1 ); | |
} | |
return( -1 ); | |
} | |
// calculates the amount of money to risk per trade | |
double getRiskAmount() { | |
return( MathMax( EXT_RISK_MINAMT, ( AccountBalance() + getStopPL() ) / EXT_RISK_DIVISOR ) ); | |
} | |
// calculates the total profit/loss of all open positions as though every open trade were to | |
// exit immediately at their current stop prices | |
double getStopPL() { | |
int tot = OrdersTotal(); | |
double result = 0; | |
for( int i = tot; i >= 0; i -= 1 ) { | |
if ( OrderSelect( i, SELECT_BY_POS, MODE_TRADES) ) { | |
result += profitAtStop( OrderSymbol(), OrderOpenPrice(), OrderStopLoss(), OrderLots(), OrderType() ); | |
} | |
} | |
return( result ); | |
} | |
// calculates the profit/loss of the trade if it's stop is hit | |
double profitAtStop( string sym, double open, double stop, double lots, int type ) { | |
double result = lots * MarketInfo( sym, MODE_TICKVALUE ) / MarketInfo( sym, MODE_TICKSIZE ); | |
if ( type == OP_BUY ) { | |
return( N( ( stop - open ) * result, 2 ) ); | |
} else if ( type == OP_SELL ) { | |
return( N( ( open - stop ) * result, 2 ) ); | |
} | |
return ( 0 ); | |
} | |
// shortcut function to NormalizeDouble | |
double N( double d, int dig = 0 ) { | |
if ( dig == 0 ) dig = Digits; | |
return( NormalizeDouble( d, dig ) ); | |
} | |
// calculates the position size of the trade according to entry, initial stop loss and risk | |
double getLots( string sym, double entry, double stop, double risk ) { | |
double dist = MathAbs( entry - stop ), | |
result = ( risk * MarketInfo( sym, MODE_TICKSIZE ) ) / ( dist * MarketInfo( sym, MODE_TICKVALUE ) ); | |
return( N( result, 2 ) ); | |
} | |
// shortcut function to convert double to string | |
string D( double d, int dig = 0 ) { | |
if ( dig == 0 ) dig = Digits; | |
return( DoubleToStr( d, dig ) ); | |
} | |
// check if the symbol currently has an open position | |
int isActive( string sym, bool checkStop ) { | |
int tots = OrdersTotal(); | |
double stop; | |
for ( int i = tots; i >= 0; i -= 1 ) { | |
if ( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) ) { | |
if ( OrderSymbol() == sym ) { | |
// check if we are checking the trailing stop on an active order | |
if ( checkStop && OrderType() < 2 ) { | |
// as we are checking stops let's check this one | |
// notice here how when we stored the OrderMagicNumber we used it to store the Period | |
// the purpose of this is to help us differentiate what time period was used in case we | |
// use multiple time frames for this EA on the same currency | |
EXIT_PRICE = getTrailingStop( sym, OrderOpenPrice(), OrderStopLoss(), OrderOpenTime(), OrderMagicNumber(), OrderType() ); | |
TRADE_TKT = OrderTicket(); | |
TRADE_LOTS = OrderLots(); | |
} | |
return ( OrderType() ); | |
} | |
} | |
} | |
return ( -1 ); | |
} | |
// this function requires the following parameters: | |
// sym = currency | |
// op = opening price of the current trade | |
// sl = stop loss price of the current trade | |
// ot = opening datetime of the current trade | |
// per = active chart's period - for calculating ATR distance properly | |
// type = order type of the current trade | |
double getTrailingStop( string sym, double op, double sl, datetime ot, int per, int type ) { | |
double temp; | |
// make the result for this function initially equal the stop loss | |
double result = sl; | |
// calculate the trailing stop distance | |
double atr = iATR( sym, per, EXT_ATR, 1 ) * EXT_ATR_TRAILSTOP; | |
// check if we are operating in the bar of entry, if so move to minute charts to find out | |
// what the highest high or lowest low is since entry (rather than using the whole bar) | |
// As Pepperstone timestamp their bars with the opening time of that bar we can frame | |
// our logic by checking if the current bar is less than or equal to the current bar | |
// if it is, then the current bar *IS* the bar of entry. | |
if ( iTime( sym, per, 0 ) <= ot ) { | |
temp = getMinutePrice( sym, ot, atr, type ); | |
if ( type == OP_BUY && temp > result ) result = temp; | |
if ( type == OP_SELL && temp < result ) result = temp; | |
} | |
// get bars of current periods' chart | |
int b = iBars( sym, per ); | |
// let's loop through the bars now, starting with the most recent bar | |
for ( int i = 0; i < b; i += 1 ) { | |
// if the time of the active chart is LESS than the time of when the trade opened, end | |
if ( iTime( sym, per, i ) < ot ) break; | |
// calculate the ATR trailing distance - using active chart's period | |
// and always using the previous bar's ATR as the current bar is still | |
// forming | |
atr = iATR( sym, per, EXT_ATR, i+1 ) * EXT_ATR_TRAILSTOP; | |
// for long trades | |
if ( type == OP_BUY ) { | |
// using the high of the minute chart subtract the ATR distance | |
temp = iHigh( sym, per, i ) - atr; | |
// compare against the current best result, and if it is better modify result | |
if ( temp > result ) result = temp; | |
// for short trades | |
} else if ( type == OP_SELL ) { | |
// using the low of the minute chart add the ATR distance | |
temp = iLow( sym, per, i ) + atr; | |
// compare against the current best result, and if it is better modify result | |
if ( temp < result ) result = temp; | |
} | |
} | |
// return the result, normalizing the double | |
return ( N( result ) ); | |
} | |
// this function will return the highest high or lowest low in the minute chart | |
// parameters of this function: | |
// sym = currency's symbol | |
// ot = opening datetime of the trade | |
// atr = atr value used as trailing distance | |
// type = order type of the trade | |
double getMinutePrice( string sym, datetime ot, double atr, int type ) { | |
int per = PERIOD_M1; | |
int b = iBars( sym, per ); | |
// better to start with a value, rather than 0 | |
double result = iClose( sym, per, 0 ); | |
for ( int i = 0; i < b; i += 1 ) { | |
// once the active bar is less than the opening time then we are analysing | |
// bars PRIOR to entry, therefore exit | |
if ( iTime( sym, per, i ) < ot ) break; | |
if ( type == OP_BUY && iHigh( sym, per, i ) > result ) result = iHigh( sym, per, i ); | |
if ( type == OP_SELL && iLow( sym, per, i ) < result ) result = iLow( sym, per, i ); | |
} | |
// now that we have found the highest value since entry let's subtract the atr distance | |
if ( type == OP_BUY ) return( result - atr ); | |
// now that we hav found the lowest value since entry let's add the atr distance | |
if ( type == OP_SELL ) return ( result + atr ); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment