├── Experts ├── ttn-201703-breakout_reversal.mq4 ├── ttn-201705-cci_breakout_reversal.mq4 ├── ttn-201705-plateau_reversal.mq4 ├── ttn3Screen.mq4 ├── ttnATRbreakout.mq4 ├── ttnBollingerBreakout.mq4 ├── ttnDonchianBreakout.mq4 ├── ttnFullTurtle.mq4 ├── ttnTrendRider.mq4 └── ttnTrueSymphonie.mq4 ├── Include ├── ttnCommon.mqh └── ttnCommonFuncs.mqh ├── Indicators ├── ttn-201712-market_weather_dual_ma_cross.mq4 └── ttn-201712-market_weather_price_ma_cross.mq4 ├── LICENSE ├── Libraries └── ttnCommonFuncs.mq4 └── README.md /Experts/ttn-201703-breakout_reversal.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnCommon.mqh | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #property copyright "tickelton@gmail.com" 31 | #property link "https://github.com/tickelton" 32 | #property strict 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | extern int MagicNumber = ttnMagicNumber201703breakoutreversal; 40 | extern eLogLevel LogLevel = LevelError; 41 | 42 | int ticketNr = 0; 43 | 44 | datetime cond1Time; 45 | double cond2Level = 0; 46 | double cond1Base = 0; 47 | bool cond2Bull = false; 48 | double trailingSL = 0; 49 | 50 | double lineM = 0; 51 | double lineB = 0; 52 | 53 | const string eaName = "ttn-201703-breakout_reversal"; 54 | datetime CurrentTimestamp; 55 | 56 | int OnInit() 57 | { 58 | CurrentTimestamp = Time[0]; 59 | 60 | 61 | return(INIT_SUCCEEDED); 62 | } 63 | 64 | void OnTick() 65 | { 66 | bool NewBar = false; 67 | 68 | if(CurrentTimestamp != Time[0]) { 69 | CurrentTimestamp = Time[0]; 70 | NewBar = true; 71 | } 72 | 73 | if(NewBar) { 74 | MqlDateTime timeStr; 75 | TimeToStruct(CurrentTimestamp, timeStr); 76 | 77 | if(ticketNr == 0) { 78 | if(timeStr.hour >= 7 && timeStr.hour <=16) { 79 | if(cond2Level == 0) { 80 | CheckCondition1(); 81 | } else { 82 | CheckCondition2(); 83 | } 84 | } 85 | } else { 86 | CheckExit(); 87 | } 88 | } 89 | 90 | 91 | } 92 | 93 | void CheckCondition1() 94 | { 95 | double lowCur = Close[1]; 96 | double highCur = Close[1]; 97 | double low1 = Close[6]; 98 | double high1 = Close[6]; 99 | double low2 = Close[21]; 100 | double high2 = Close[21]; 101 | 102 | for(int i=2; i<6; i++) { 103 | if(Close[i] < lowCur) { 104 | lowCur = Close[i]; 105 | } 106 | if(Close[i] > highCur) { 107 | highCur = Close[i]; 108 | } 109 | } 110 | for(int i=1; i<15; i++) { 111 | if(Close[6+i] < low1) { 112 | low1 = Close[6+i]; 113 | } 114 | if(Close[21+i] < low2) { 115 | low2 = Close[21+i]; 116 | } 117 | if(Close[6+i] > high1) { 118 | high1 = Close[6+i]; 119 | } 120 | if(Close[21+i] > high2) { 121 | high2 = Close[21+i]; 122 | } 123 | } 124 | 125 | double rangeCur = highCur - lowCur; 126 | 127 | double range1 = high1 - low1; 128 | double range2 = high2 - low2; 129 | double rangeAvg = (range1 + range2)/2.0; 130 | 131 | linereg(36, 6); 132 | 133 | bool bullishCur = false; 134 | bool bullish1 = false; 135 | if(Close[1] > Close[5]) { 136 | bullishCur = true; 137 | } 138 | if(lineM > 0) { 139 | bullish1 = true; 140 | } 141 | 142 | 143 | if(lineM != 0.0 && lineM < 0.00001 && lineM > -0.00001 && bullish1 == bullishCur) { 144 | if(rangeCur > (rangeAvg * 3.0)) { 145 | cond1Time = CurrentTimestamp; 146 | if(bullishCur == true) { 147 | cond2Bull = true; 148 | cond2Level = lowCur + (rangeAvg * 2.0); 149 | cond1Base = lowCur; 150 | } else { 151 | cond2Bull = false; 152 | cond2Level = highCur - (rangeAvg * 2.0); 153 | cond1Base = highCur; 154 | } 155 | } 156 | } 157 | } 158 | 159 | void CheckCondition2() 160 | { 161 | int timeDiff = getTimeDiff(cond1Time); 162 | 163 | if(timeDiff >= 5) { 164 | cond2Level = 0; 165 | return; 166 | } 167 | 168 | double tmpMA = iMA(NULL, 0, 5, 0, MODE_SMA, PRICE_CLOSE, 1); 169 | 170 | int orderOP; 171 | double orderPrice; 172 | double stopLoss; 173 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 174 | 175 | 176 | if(cond2Bull == true) { 177 | orderOP = OP_SELL; 178 | orderPrice = Bid; 179 | stopLoss = NormalizeDouble(Bid + ((Bid - cond1Base) / 2), Digits); 180 | if(Bid < cond2Level) { 181 | cond2Level = 0; 182 | return; 183 | } 184 | if(tmpMA < Bid) { 185 | return; 186 | } 187 | } else { 188 | orderOP = OP_BUY; 189 | orderPrice = Ask; 190 | stopLoss = NormalizeDouble(Ask - ((cond1Base - Ask) / 2), Digits); 191 | if(Bid > cond2Level) { 192 | cond2Level = 0; 193 | return; 194 | } 195 | if(tmpMA > Bid) { 196 | return; 197 | } 198 | } 199 | 200 | ticketNr = OrderSend( 201 | Symbol(), 202 | orderOP, 203 | 1.0, 204 | orderPrice, 205 | 3, 206 | 0, 207 | 0, 208 | eaName, 209 | MagicNumber, 210 | 0, 211 | clrBlue 212 | ); 213 | 214 | if(ticketNr == -1) { 215 | PrintFormat("EnterPosition: Error opening order: %s", 216 | ErrorDescription(GetLastError())); 217 | ticketNr = 0; 218 | } 219 | 220 | } 221 | 222 | int getTimeDiff(datetime refTime) 223 | { 224 | int timeDiff = (int)(CurrentTimestamp - refTime); 225 | int tfCurrent = Period(); 226 | if(tfCurrent == PERIOD_M1) { 227 | timeDiff = timeDiff / 60; 228 | } else if(tfCurrent == PERIOD_M5) { 229 | timeDiff = timeDiff / 300; 230 | } else if(tfCurrent == PERIOD_M15) { 231 | timeDiff = timeDiff / 900; 232 | } else if(tfCurrent == PERIOD_M30) { 233 | timeDiff = timeDiff / 1800; 234 | } else if(tfCurrent == PERIOD_H1) { 235 | timeDiff = timeDiff / 3600; 236 | } else if(tfCurrent == PERIOD_H4) { 237 | timeDiff = timeDiff / 14400; 238 | } else if(tfCurrent == PERIOD_D1) { 239 | timeDiff = timeDiff / 86400; 240 | } 241 | 242 | return timeDiff; 243 | } 244 | 245 | void CheckExit() 246 | { 247 | OrderSelect(ticketNr, SELECT_BY_TICKET); 248 | int timeDiff = getTimeDiff(OrderOpenTime()); 249 | int orderType = OrderType(); 250 | 251 | if(trailingSL != 0) { 252 | if(orderType == OP_BUY) { 253 | if(Bid < trailingSL) { 254 | doClose(Bid, OrderLots()); 255 | return; 256 | } 257 | } else { 258 | if(Bid > trailingSL) { 259 | doClose(Ask, OrderLots()); 260 | return; 261 | } 262 | } 263 | } 264 | 265 | if(timeDiff < 15) { 266 | if(orderType == OP_BUY) { 267 | if(Bid >= cond1Base) { 268 | doClose(Bid, OrderLots()); 269 | return; 270 | } 271 | } else { 272 | if(Bid <= cond1Base) { 273 | doClose(Ask, OrderLots()); 274 | return; 275 | } 276 | } 277 | 278 | if(trailingSL == 0) { 279 | if(orderType == OP_BUY) { 280 | if(Bid >= cond1Base - ((cond1Base - OrderOpenPrice()) / 2)) { 281 | trailingSL = Bid - ((cond1Base - OrderOpenPrice()) / 4); 282 | return; 283 | } 284 | } else { 285 | if(Bid <= cond1Base + ((OrderOpenPrice() - cond1Base) / 2)) { 286 | trailingSL = Bid + ((OrderOpenPrice() - cond1Base) / 4); 287 | return; 288 | } 289 | } 290 | } else { 291 | if(orderType == OP_BUY) { 292 | if(Bid >= Bid - ((cond1Base - OrderOpenPrice()) / 4)) { 293 | trailingSL = Bid - ((cond1Base - OrderOpenPrice()) / 4); 294 | return; 295 | } 296 | } else { 297 | if(Bid <= Bid + ((OrderOpenPrice() - cond1Base) / 4)) { 298 | trailingSL = Bid + ((OrderOpenPrice() - cond1Base) / 4); 299 | return; 300 | } 301 | } 302 | } 303 | } else if(timeDiff < 30) { 304 | if(orderType == OP_BUY) { 305 | if(Bid >= cond1Base - ((cond1Base - OrderOpenPrice()) / 2)) { 306 | doClose(Bid, OrderLots()); 307 | return; 308 | } 309 | } else { 310 | if(Bid <= cond1Base + ((OrderOpenPrice() - cond1Base) / 2)) { 311 | doClose(Ask, OrderLots()); 312 | return; 313 | } 314 | } 315 | } else if(timeDiff >= 30) { 316 | if(orderType == OP_BUY) { 317 | doClose(Bid, OrderLots()); 318 | return; 319 | } else { 320 | doClose(Ask, OrderLots()); 321 | return; 322 | } 323 | } 324 | } 325 | 326 | void doClose(double price, double lots) 327 | { 328 | OrderClose(ticketNr, lots, price, 0, clrRed); 329 | ticketNr = 0; 330 | trailingSL = 0; 331 | } 332 | 333 | void drawTrendline() 334 | { 335 | linereg(20, 5); 336 | 337 | 338 | if(!ObjectCreate(0,"foobar",OBJ_TREND,0,Time[20],lineM * 0 + lineB,Time[5],lineM * 16 + lineB)) 339 | { 340 | Print(__FUNCTION__, 341 | ": failed to create a trend line! Error code = ",GetLastError()); 342 | } 343 | ObjectSetInteger(0,"foobar",OBJPROP_RAY_RIGHT,0); 344 | } 345 | 346 | bool linereg(int n, int m) 347 | { 348 | double count = n - m + 1.0; 349 | 350 | lineM = 0; 351 | lineB = 0; 352 | 353 | double sumx = 0; 354 | double sumx2 = 0; 355 | double sumxy = 0; 356 | double sumy = 0; 357 | double sumy2 = 0; 358 | 359 | double j = 0; 360 | for(int i=n; i>=m; i--) { 361 | sumx += j; 362 | sumx2 += j*j; 363 | sumxy += j * Close[i]; 364 | sumy += Close[i]; 365 | sumy2 += Close[i] * Close[i]; 366 | j++; 367 | } 368 | 369 | double denom = (count * sumx2 - (sumx * sumx)); 370 | if(denom == 0) { 371 | return false; 372 | } 373 | lineM = (count * sumxy - sumx * sumy) / denom; 374 | lineB = (sumy * sumx2 - sumx * sumxy) / denom; 375 | 376 | return true; 377 | } 378 | 379 | -------------------------------------------------------------------------------- /Experts/ttn-201705-cci_breakout_reversal.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttn-201705-cci_breakout_reversal.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #property copyright "tickelton@gmail.com" 31 | #property link "https://github.com/tickelton" 32 | #property strict 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | extern int MagicNumber = ttnMagicNumber201705CCIbreakoutReversal; 41 | extern eLogLevel LogLevel = LevelError; 42 | extern double MinATR = 0.00050; 43 | extern double MaxATRfactor = 6.0; 44 | extern int MinCCI = 100; 45 | extern int CCIbreakoutBars = 32; 46 | extern double SignalATRmultiplier = 1.5; 47 | extern double StopATRmultiplier = 0.5; 48 | extern double TrailingATRmultiplier = 0.5; 49 | extern double ProfitFactor = 0.5; 50 | 51 | int CCIcutoff = 100; 52 | double MaxATR; 53 | 54 | int TicketNr1 = 0; 55 | int TicketNr2 = 0; 56 | 57 | bool NoLong = false; 58 | bool NoShort = false; 59 | 60 | const string eaName = "ttn-201705-cci_breakout_reversal"; 61 | datetime CurrentTimestamp; 62 | 63 | struct sTradeData { 64 | int op; 65 | double openPrice; 66 | double stopLoss; 67 | double takeProfit; 68 | double trailingSL; 69 | }; 70 | 71 | sTradeData TradeData; 72 | 73 | int OnInit() 74 | { 75 | CurrentTimestamp = Time[0]; 76 | CCIcutoff = MinCCI; 77 | MaxATR = MinATR * MaxATRfactor; 78 | resetTradeData(); 79 | 80 | return(INIT_SUCCEEDED); 81 | } 82 | 83 | void OnTick() 84 | { 85 | bool NewBar = false; 86 | 87 | if(CurrentTimestamp != Time[0]) { 88 | CurrentTimestamp = Time[0]; 89 | NewBar = true; 90 | } 91 | 92 | if(NewBar) { 93 | if(TicketNr1 == 0 && 94 | TicketNr2 == 0) 95 | checkSetup(); 96 | } 97 | 98 | if(TicketNr1 != 0 || 99 | TicketNr2 != 0) 100 | checkExits(); 101 | } 102 | 103 | void checkSetup() 104 | { 105 | double curATR = iATR(NULL, 0, 14, 1); 106 | double curCCI = iCCI(NULL, 0, 14, PRICE_TYPICAL, 1); 107 | 108 | if(curATR < MinATR || curATR > MaxATR) 109 | return; 110 | if(curCCI < CCIcutoff && curCCI > -CCIcutoff) 111 | return; 112 | if(directionLocked(curCCI)) 113 | return; 114 | if(!gotCCIbreakout(curCCI)) 115 | return; 116 | if(!gotEntryCandle(curATR, curCCI)) 117 | return; 118 | if(trendingCCI()) 119 | return; 120 | 121 | enterTrade(curCCI, curATR); 122 | } 123 | 124 | bool directionLocked(double curCCI) 125 | { 126 | if(curCCI > 0 && NoLong) 127 | return true; 128 | if(curCCI < 0 && NoShort) 129 | return true; 130 | return false; 131 | } 132 | 133 | bool gotCCIbreakout(double curCCI) 134 | { 135 | for(int i=2; i<=CCIbreakoutBars; i++) { 136 | double tmpCCI = iCCI(NULL, 0, 14, PRICE_TYPICAL, i); 137 | 138 | if(curCCI > 0 && curCCI < tmpCCI) 139 | return false; 140 | if(curCCI < 0 && curCCI > tmpCCI) 141 | return false; 142 | } 143 | 144 | if(LogLevel >= LevelDebug) { 145 | PrintFormat("gotCCIbreakout: got breakout cur=%f", 146 | curCCI 147 | ); 148 | } 149 | return true; 150 | } 151 | 152 | bool gotEntryCandle(double curATR, double curCCI) 153 | { 154 | if(curCCI > 0) { 155 | if(Close[1] <= Open[1]) 156 | return false; 157 | } else { 158 | if(Close[1] >= Open[1]) 159 | return false; 160 | } 161 | 162 | if(MathAbs(Close[1] - Open[1]) < 163 | (curATR * SignalATRmultiplier)) 164 | return false; 165 | 166 | return true; 167 | } 168 | 169 | void resetTradeData() 170 | { 171 | TradeData.openPrice = 0.0; 172 | TradeData.stopLoss = 0.0; 173 | TradeData.takeProfit = 0.0; 174 | TradeData.op = OP_NONE; 175 | TradeData.trailingSL = 0.0; 176 | } 177 | 178 | bool trendingCCI() 179 | { 180 | return false; 181 | } 182 | 183 | void enterTrade(double curCCI, double curATR) 184 | { 185 | color arrowColor; 186 | 187 | if(TradeData.openPrice != 0.0 || 188 | TradeData.stopLoss != 0.0 || 189 | TradeData.takeProfit != 0.0 || 190 | TradeData.trailingSL != 0.0 || 191 | TradeData.op != OP_NONE) { 192 | if(LogLevel >= LevelError) { 193 | PrintFormat("enterTrade: invalid CCI: %f", 194 | curCCI 195 | ); 196 | } 197 | } 198 | 199 | if(curCCI < 0) { 200 | TradeData.op = OP_BUY; 201 | TradeData.openPrice = Ask; 202 | TradeData.stopLoss = Low[1] - (curATR * StopATRmultiplier); 203 | TradeData.takeProfit = Open[1] - ((Open[1] - Close[1]) * ProfitFactor); 204 | arrowColor = clrBlue; 205 | } else if(curCCI > 0) { 206 | TradeData.op = OP_SELL; 207 | TradeData.openPrice = Bid; 208 | TradeData.stopLoss = High[1] + (curATR * StopATRmultiplier); 209 | TradeData.takeProfit = Open[1] + ((Close[1] - Open[1]) * ProfitFactor); 210 | arrowColor = clrRed; 211 | } else { 212 | if(LogLevel >= LevelError) { 213 | PrintFormat("enterTrade: invalid CCI: %f", 214 | curCCI 215 | ); 216 | } 217 | return; 218 | } 219 | 220 | if(LogLevel >= LevelDebug) { 221 | PrintFormat("enterTrade: Open op=%d price=%f SL=%f TP=%f ATR=%f CCI=%f", 222 | TradeData.op, 223 | TradeData.openPrice, 224 | TradeData.stopLoss, 225 | TradeData.takeProfit, 226 | curATR, 227 | curCCI 228 | ); 229 | } 230 | TicketNr1 = OrderSend( 231 | Symbol(), 232 | TradeData.op, 233 | 0.5, 234 | TradeData.openPrice, 235 | 3, 236 | 0, 237 | 0, 238 | eaName, 239 | MagicNumber, 240 | 0, 241 | arrowColor 242 | ); 243 | 244 | if(TicketNr1 == -1) { 245 | PrintFormat("EnterPosition: Error opening order: %s", 246 | ErrorDescription(GetLastError())); 247 | TicketNr1 = 0; 248 | return; 249 | } 250 | 251 | TicketNr2 = OrderSend( 252 | Symbol(), 253 | TradeData.op, 254 | 0.5, 255 | TradeData.openPrice, 256 | 3, 257 | 0, 258 | 0, 259 | eaName, 260 | MagicNumber, 261 | 0, 262 | arrowColor 263 | ); 264 | 265 | if(TicketNr2 == -1) { 266 | PrintFormat("EnterPosition: Error opening order: %s", 267 | ErrorDescription(GetLastError())); 268 | TicketNr2 = 0; 269 | closeTicket(TicketNr1); 270 | TicketNr1 = 0; 271 | return; 272 | } 273 | } 274 | 275 | void closeTicket(int nr) 276 | { 277 | if(TradeData.op == OP_BUY) { 278 | if(!OrderClose(nr, 0.5, Bid, 0, clrRed)) { 279 | PrintFormat("EnterPosition: Error closing order: %s", 280 | ErrorDescription(GetLastError())); 281 | } 282 | } else if(TradeData.op == OP_SELL) { 283 | if(!OrderClose(nr, 0.5, Ask, 0, clrRed)) { 284 | PrintFormat("EnterPosition: Error closing order: %s", 285 | ErrorDescription(GetLastError())); 286 | } 287 | } 288 | 289 | if(nr == TicketNr2) 290 | resetTradeData(); 291 | } 292 | 293 | void setTrailingSL() 294 | { 295 | double curATR = iATR(NULL, 0, 14, 1); 296 | 297 | if(TradeData.op == OP_BUY) { 298 | double slPrice = NormalizeDouble( 299 | Bid - (TrailingATRmultiplier * curATR), 300 | Digits 301 | ); 302 | 303 | if(slPrice <= TradeData.openPrice) 304 | return; 305 | if(TradeData.trailingSL != 0.0 && 306 | slPrice <= TradeData.trailingSL) 307 | return; 308 | TradeData.trailingSL = slPrice; 309 | } else if(TradeData.op == OP_SELL) { 310 | double slPrice = NormalizeDouble( 311 | Ask + (TrailingATRmultiplier * curATR), 312 | Digits 313 | ); 314 | 315 | if(slPrice >= TradeData.openPrice) 316 | return; 317 | if(TradeData.trailingSL != 0.0 && 318 | slPrice >= TradeData.trailingSL) 319 | return; 320 | TradeData.trailingSL = slPrice; 321 | } 322 | } 323 | 324 | void checkExits() 325 | { 326 | if(TicketNr1 != 0) { 327 | if(TradeData.op == OP_BUY) { 328 | if(Bid <= TradeData.stopLoss) { 329 | closeTicket(TicketNr1); 330 | TicketNr1 = 0; 331 | } else if(Bid >= TradeData.takeProfit) { 332 | closeTicket(TicketNr1); 333 | TicketNr1 = 0; 334 | setTrailingSL(); 335 | } 336 | } else if(TradeData.op == OP_SELL) { 337 | if(Ask >= TradeData.stopLoss) { 338 | closeTicket(TicketNr1); 339 | TicketNr1 = 0; 340 | } else if (Ask <= TradeData.takeProfit) { 341 | closeTicket(TicketNr1); 342 | TicketNr1 = 0; 343 | setTrailingSL(); 344 | } 345 | } 346 | } else { 347 | setTrailingSL(); 348 | } 349 | 350 | if(TicketNr2 != 0) { 351 | if(TradeData.op == OP_BUY) { 352 | if(Bid <= TradeData.stopLoss || 353 | (TradeData.trailingSL != 0.0 && Bid <= TradeData.trailingSL)) { 354 | closeTicket(TicketNr2); 355 | TicketNr2 = 0; 356 | resetTradeData(); 357 | } 358 | } else if(TradeData.op == OP_SELL) { 359 | if(Ask >= TradeData.stopLoss || 360 | (TradeData.trailingSL != 0.0 && Ask >= TradeData.trailingSL)) { 361 | closeTicket(TicketNr2); 362 | TicketNr2 = 0; 363 | resetTradeData(); 364 | } 365 | } 366 | } 367 | } 368 | -------------------------------------------------------------------------------- /Experts/ttn-201705-plateau_reversal.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttn-201705-plateau_reversal.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #property copyright "tickelton@gmail.com" 31 | #property link "https://github.com/tickelton" 32 | #property strict 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | 40 | extern int MagicNumber = ttnMagicNumber201705plateauReversal; 41 | extern eLogLevel LogLevel = LevelError; 42 | extern int PlateauLength1 = 60; 43 | extern int PlateauLength2 = 30; 44 | extern double MinPriceDiff = 0.00100; 45 | extern double ExtremeDiffMultiplier = 2.0; 46 | extern int BreakoutTime = 15; 47 | extern double MaxPlateauSlope = 0.000001; 48 | extern int EntryConfirmationBars = 10; 49 | extern int PeriodATR = 14; 50 | extern double TrailingSLDivisor = 0.25; 51 | extern bool AdaptiveShortTP = true; 52 | extern double StopATRmultiplier = 1.0; 53 | extern int TimeExit = 90; 54 | 55 | int ticketNr1 = 0; 56 | int ticketNr2 = 0; 57 | 58 | double takeProfitLevel = 0.0; 59 | double trailingStopLevel = 0.0; 60 | 61 | const string eaName = "ttn-201705-plateau_reversal"; 62 | datetime CurrentTimestamp; 63 | 64 | struct sEntryData { 65 | int dir; 66 | s_linereg p1; 67 | s_linereg p2; 68 | double stopLevel; 69 | double trailingSL; 70 | double entrySecondQuarter; 71 | datetime triggerTime; 72 | }; 73 | sEntryData EntryData; 74 | 75 | int OnInit() 76 | { 77 | CurrentTimestamp = Time[0]; 78 | resetEntryData(); 79 | 80 | return(INIT_SUCCEEDED); 81 | } 82 | 83 | void OnTick() 84 | { 85 | bool NewBar = false; 86 | 87 | if(CurrentTimestamp != Time[0]) { 88 | CurrentTimestamp = Time[0]; 89 | NewBar = true; 90 | } 91 | 92 | if(NewBar) { 93 | if(ticketNr1 != 0 || 94 | ticketNr2 != 0) { 95 | checkTimeExit(); 96 | } else if(EntryData.dir != OP_NONE) { 97 | if(getTimeDiff(CurrentTimestamp, EntryData.triggerTime) > EntryConfirmationBars) { 98 | EntryData.dir = OP_NONE; 99 | EntryData.stopLevel = 0.0; 100 | } else { 101 | checkEntryTrigger(); 102 | } 103 | } else { 104 | checkPlateau(); 105 | } 106 | } 107 | 108 | if(ticketNr1 != 0 || 109 | ticketNr2 != 0) { 110 | checkPriceExits(); 111 | } 112 | } 113 | 114 | void resetEntryData() 115 | { 116 | EntryData.dir = OP_NONE; 117 | EntryData.stopLevel = 0.0; 118 | EntryData.trailingSL = 0.0; 119 | } 120 | 121 | void checkTimeExit() 122 | { 123 | if(ticketNr1 == 0) 124 | return; 125 | 126 | OrderSelect(ticketNr1, SELECT_BY_TICKET); 127 | int timeDiff = getTimeDiff(CurrentTimestamp, OrderOpenTime()); 128 | 129 | if(timeDiff >= TimeExit) { 130 | closeTicket(ticketNr1); 131 | ticketNr1 = 0; 132 | closeTicket(ticketNr2); 133 | ticketNr2 = 0; 134 | resetEntryData(); 135 | } 136 | } 137 | 138 | void checkPriceExits() 139 | { 140 | if(ticketNr1 != 0) { 141 | if(EntryData.dir == OP_BUY) { 142 | if(Bid <= EntryData.stopLevel) { 143 | LogTerminal(LevelDebug, 144 | LogLevel, 145 | "checkPriceExits: 1 Bid <= EntryData.stopLevel" 146 | ); 147 | closeTicket(ticketNr1); 148 | ticketNr1 = 0; 149 | } else if(Bid >= takeProfitLevel) { 150 | LogTerminal(LevelDebug, 151 | LogLevel, 152 | "checkPriceExits: 1 Bid >= takeProfitLevel" 153 | ); 154 | closeTicket(ticketNr1); 155 | ticketNr1 = 0; 156 | trailingStopLevel = NormalizeDouble( 157 | Bid - EntryData.trailingSL, 158 | Digits 159 | ); 160 | if(LogLevel >= LevelDebug) 161 | PrintFormat( 162 | "checkPriceExits: set TSL=%f", 163 | trailingStopLevel 164 | ); 165 | } 166 | } else if(EntryData.dir == OP_SELL) { 167 | if(Ask >= EntryData.stopLevel) { 168 | LogTerminal(LevelDebug, 169 | LogLevel, 170 | "checkPriceExits: 1 Ask >= EntryData.stopLevel" 171 | ); 172 | closeTicket(ticketNr1); 173 | ticketNr1 = 0; 174 | } else if (Ask <= takeProfitLevel) { 175 | LogTerminal(LevelDebug, 176 | LogLevel, 177 | "checkPriceExits: 1 Ask <= takeProfitLevel" 178 | ); 179 | closeTicket(ticketNr1); 180 | ticketNr1 = 0; 181 | trailingStopLevel = NormalizeDouble( 182 | Ask + EntryData.trailingSL, 183 | Digits 184 | ); 185 | if(LogLevel >= LevelDebug) 186 | PrintFormat( 187 | "checkPriceExits: set TSL=%f", 188 | trailingStopLevel 189 | ); 190 | } 191 | } 192 | } else { 193 | if(EntryData.dir == OP_BUY) { 194 | double newTrailingStopLevel = NormalizeDouble( 195 | Bid - EntryData.trailingSL, 196 | Digits 197 | ); 198 | if(newTrailingStopLevel > trailingStopLevel) { 199 | trailingStopLevel = newTrailingStopLevel; 200 | if(LogLevel >= LevelTrace) 201 | PrintFormat( 202 | "checkPriceExits: re-set TSL=%f", 203 | trailingStopLevel 204 | ); 205 | } 206 | } else if(EntryData.dir == OP_SELL) { 207 | double newTrailingStopLevel = NormalizeDouble( 208 | Ask + EntryData.trailingSL, 209 | Digits 210 | ); 211 | if(newTrailingStopLevel < trailingStopLevel) { 212 | trailingStopLevel = newTrailingStopLevel; 213 | if(LogLevel >= LevelTrace) 214 | PrintFormat( 215 | "checkPriceExits: re-set TSL=%f", 216 | trailingStopLevel 217 | ); 218 | } 219 | } 220 | } 221 | 222 | if(ticketNr2 != 0) { 223 | if(EntryData.dir == OP_BUY) { 224 | if(Bid <= EntryData.stopLevel || 225 | (trailingStopLevel != 0.0 && Bid <= trailingStopLevel)) { 226 | LogTerminal(LevelDebug, 227 | LogLevel, 228 | "checkPriceExits: 2 Bid <= EntryData.stopLevel" 229 | ); 230 | closeTicket(ticketNr2); 231 | ticketNr2 = 0; 232 | resetEntryData(); 233 | } 234 | } else if(EntryData.dir == OP_SELL) { 235 | if(Ask >= EntryData.stopLevel || 236 | (trailingStopLevel != 0.0 && Ask >= trailingStopLevel)) { 237 | LogTerminal(LevelDebug, 238 | LogLevel, 239 | "checkPriceExits: 2 Ask >= EntryData.stopLevel" 240 | ); 241 | closeTicket(ticketNr2); 242 | ticketNr2 = 0; 243 | resetEntryData(); 244 | } 245 | } 246 | } 247 | } 248 | 249 | void checkPlateau() 250 | { 251 | LogTerminal(LevelTrace, LogLevel, ">checkPlateau()"); 252 | s_linereg plateau1 = linereg(PlateauLength1+PlateauLength2+BreakoutTime+1, 253 | PlateauLength2+BreakoutTime+1); 254 | s_linereg plateau2 = linereg(PlateauLength2, 1); 255 | 256 | double cDiff = closeDiff(PlateauLength2+BreakoutTime, 257 | PlateauLength2); 258 | double eDiff = extremeDiff(PlateauLength2+BreakoutTime, 259 | PlateauLength2); 260 | double lineDiff = 0.0; 261 | if(plateau1.b > plateau2.b) 262 | lineDiff = plateau1.b - plateau2.b; 263 | else 264 | lineDiff = plateau2.b - plateau1.b; 265 | 266 | if(plateau1.m < MaxPlateauSlope && 267 | plateau2.m < MaxPlateauSlope && 268 | plateau1.m > -MaxPlateauSlope && 269 | plateau2.m > -MaxPlateauSlope && 270 | lineDiff >= MinPriceDiff && 271 | eDiff <= lineDiff * ExtremeDiffMultiplier) { 272 | 273 | if(plateau1.b > plateau2.b) { 274 | double low1 = getRangeMax( 275 | PlateauLength2+BreakoutTime+1, 276 | PlateauLength1+PlateauLength2+BreakoutTime+1, 277 | PRICE_LOW); 278 | double high2 = getRangeMax( 279 | 1, 280 | PlateauLength2, 281 | PRICE_HIGH); 282 | if(low1 < plateau2.b || 283 | high2 > plateau1.b) { 284 | if(LogLevel >= LevelDebug) 285 | PrintFormat( 286 | "checkPlateau: Buy: plateau levels crossing: b1=%f b2=%f low1=%f high2=%f", 287 | plateau1.b, plateau2.b, low1, high2 288 | ); 289 | return; 290 | } 291 | } else { 292 | double high1 = getRangeMax( 293 | PlateauLength2+BreakoutTime+1, 294 | PlateauLength1+PlateauLength2+BreakoutTime+1, 295 | PRICE_HIGH); 296 | double low2 = getRangeMax( 297 | 1, 298 | PlateauLength2, 299 | PRICE_LOW); 300 | if(high1 > plateau2.b || 301 | low2 < plateau1.b) { 302 | if(LogLevel >= LevelDebug) 303 | PrintFormat( 304 | "checkPlateau: Sell: plateau levels crossing: b1=%f b2=%f high1=%f low2=%f", 305 | plateau1.b, plateau2.b, high1, low2 306 | ); 307 | return; 308 | } 309 | } 310 | drawPlateau(plateau1, plateau2); 311 | setEntryTrigger(plateau1, plateau2); 312 | } 313 | 314 | LogTerminal(LevelTrace, LogLevel, " max) 325 | max = High[i]; 326 | } 327 | } else if(applied_price == PRICE_LOW) { 328 | max = Low[end]; 329 | for(int i = begin; i < end; i++) { 330 | if(Low[i] < max) 331 | max = Low[i]; 332 | } 333 | } else if(applied_price == PRICE_CLOSE) { 334 | max = Close[end]; 335 | for(int i = begin; i < end; i++) { 336 | if(Close[i] < max) 337 | max = Close[i]; 338 | } 339 | } 340 | 341 | return max; 342 | } 343 | 344 | void setEntryTrigger(s_linereg &plateau1, s_linereg &plateau2) 345 | { 346 | LogTerminal(LevelTrace, LogLevel, ">setEntryTrigger()"); 347 | 348 | double curATR = iATR(NULL, 0, PeriodATR, 1); 349 | 350 | if(plateau1.b < plateau2.b) { 351 | EntryData.dir = OP_SELL; 352 | EntryData.stopLevel = getRangeMax(1, PlateauLength2, PRICE_HIGH) + curATR*StopATRmultiplier; 353 | EntryData.trailingSL = NormalizeDouble( 354 | (plateau2.b - plateau1.b) * TrailingSLDivisor, 355 | Digits 356 | ); 357 | takeProfitLevel = NormalizeDouble( 358 | plateau1.b + ((plateau2.b - plateau1.b)/2.0), 359 | Digits 360 | ); 361 | EntryData.entrySecondQuarter = NormalizeDouble( 362 | plateau2.b - ((plateau2.b - plateau1.b)/4.0), 363 | Digits 364 | ); 365 | if(LogLevel >= LevelDebug) { 366 | PrintFormat("setEntryTrigger: Sell SL=%f TSL=%f TP=%f", 367 | EntryData.stopLevel, 368 | EntryData.trailingSL, 369 | takeProfitLevel 370 | ); 371 | } 372 | } else { 373 | EntryData.dir = OP_BUY; 374 | EntryData.stopLevel = getRangeMax(1, PlateauLength2, PRICE_LOW) - curATR*StopATRmultiplier; 375 | EntryData.trailingSL = NormalizeDouble( 376 | (plateau1.b - plateau2.b) * TrailingSLDivisor, 377 | Digits 378 | ); 379 | takeProfitLevel = NormalizeDouble( 380 | plateau1.b - ((plateau1.b - plateau2.b)/2.0), 381 | Digits 382 | ); 383 | EntryData.entrySecondQuarter = NormalizeDouble( 384 | plateau2.b + ((plateau1.b - plateau2.b)/4.0), 385 | Digits 386 | ); 387 | if(LogLevel >= LevelDebug) { 388 | PrintFormat("setEntryTrigger: Buy SL=%f TSL=%f TP=%f", 389 | EntryData.stopLevel, 390 | EntryData.trailingSL, 391 | takeProfitLevel 392 | ); 393 | } 394 | } 395 | 396 | EntryData.p1 = plateau1; 397 | EntryData.p2 = plateau2; 398 | EntryData.triggerTime = CurrentTimestamp; 399 | 400 | LogTerminal(LevelTrace, LogLevel, "checkEntryTrigger()"); 406 | 407 | if(EntryData.dir == OP_BUY) { 408 | if(Close[1] > Open[1] && 409 | Close[1] > EntryData.p2.b) { 410 | if(Close[1] >= takeProfitLevel) { 411 | resetEntryData(); 412 | } else { 413 | /* ONLY SEEMS TO WORK ON THE SHORT SIDE 414 | WARNING: possibly curve fitting ! 415 | if(Close[1] > EntryData.entrySecondQuarter) { 416 | if(LogLevel >= LevelDebug) { 417 | PrintFormat("checkEntryTrigger: Buy re-setting TF from %f to %f (sq=%f)", 418 | takeProfitLevel, 419 | NormalizeDouble( 420 | EntryData.p1.b - ((EntryData.p1.b - EntryData.p2.b)/4.0), 421 | Digits 422 | ), 423 | EntryData.entrySecondQuarter 424 | ); 425 | } 426 | takeProfitLevel = NormalizeDouble( 427 | EntryData.p1.b - ((EntryData.p1.b - EntryData.p2.b)/4.0), 428 | Digits 429 | ); 430 | } */ 431 | if(LogLevel >= LevelDebug) { 432 | PrintFormat("checkEntryTrigger: Buy SLwidth=%.5f PlateuDiff=%.5f", 433 | Ask - EntryData.stopLevel, 434 | EntryData.p1.b - EntryData.p2.b 435 | ); 436 | } 437 | enterTrade(); 438 | } 439 | } 440 | } else { 441 | if(Close[1] < Open[1] && 442 | Close[1] < EntryData.p2.b) { 443 | if(Close[1] <= takeProfitLevel) { 444 | resetEntryData(); 445 | } else { 446 | if(AdaptiveShortTP && Close[1] < EntryData.entrySecondQuarter) { 447 | if(LogLevel >= LevelDebug) { 448 | PrintFormat("checkEntryTrigger: Sell re-setting TF from %f to %f (sq=%f)", 449 | takeProfitLevel, 450 | NormalizeDouble( 451 | EntryData.p1.b + ((EntryData.p2.b - EntryData.p1.b)/4.0), 452 | Digits 453 | ), 454 | EntryData.entrySecondQuarter 455 | ); 456 | } 457 | takeProfitLevel = NormalizeDouble( 458 | EntryData.p1.b + ((EntryData.p2.b - EntryData.p1.b)/4.0), 459 | Digits 460 | ); 461 | } 462 | if(LogLevel >= LevelDebug) { 463 | PrintFormat("checkEntryTrigger: Sell SLwidth=%.5f PlateuDiff=%.5f", 464 | EntryData.stopLevel - Bid, 465 | EntryData.p2.b - EntryData.p1.b 466 | ); 467 | } 468 | enterTrade(); 469 | } 470 | } 471 | } 472 | 473 | LogTerminal(LevelTrace, LogLevel, " close1) 562 | return close2 - close1; 563 | else 564 | return close1 - close2; 565 | } 566 | 567 | double extremeDiff(int begin, int end) 568 | { 569 | double maxHigh = High[begin]; 570 | double maxLow = Low[end]; 571 | 572 | for(int i=begin+1; i<=end; i++) { 573 | if(High[i] > maxHigh) 574 | maxHigh = High[i]; 575 | if(Low[i] < maxLow) 576 | maxLow = Low[i]; 577 | } 578 | 579 | return maxHigh - maxLow; 580 | } 581 | 582 | void drawPlateau(s_linereg &plateau1, s_linereg &plateau2) 583 | { 584 | static int count = 1; 585 | 586 | string lineName1 = "l_plateau1_"; 587 | string lineName2 = "l_plateau2_"; 588 | 589 | if(LogLevel >= LevelDebug) 590 | PrintFormat("Plateau1: m=%f b=%f r=%f", plateau1.m, plateau1.b, plateau1.r); 591 | if(!ObjectCreate(0, 592 | StringConcatenate(lineName1, IntegerToString(count)), 593 | OBJ_TREND, 594 | 0, 595 | Time[PlateauLength1+PlateauLength2+BreakoutTime+1], 596 | plateau1.m * 0 + plateau1.b, 597 | Time[PlateauLength2+BreakoutTime+1], 598 | plateau1.m * 19 + plateau1.b 599 | )) { 600 | Print(__FUNCTION__, 601 | ": failed to create a trend line! Error code = ",GetLastError()); 602 | } else { 603 | ObjectSetInteger(0, 604 | StringConcatenate(lineName1, IntegerToString(count)), 605 | OBJPROP_RAY_RIGHT, 606 | 0 607 | ); 608 | } 609 | 610 | if(LogLevel >= LevelDebug) 611 | PrintFormat("Plateau2: m=%f b=%f r=%f", plateau2.m, plateau2.b, plateau2.r); 612 | if(!ObjectCreate(0, 613 | StringConcatenate(lineName2, IntegerToString(count)), 614 | OBJ_TREND, 615 | 0, 616 | Time[PlateauLength2], 617 | plateau2.m * 0 + plateau2.b, 618 | Time[1], 619 | plateau2.m * PlateauLength2 + plateau2.b 620 | )) { 621 | Print(__FUNCTION__, 622 | ": failed to create a trend line! Error code = ",GetLastError()); 623 | } else { 624 | ObjectSetInteger(0, 625 | StringConcatenate(lineName2, IntegerToString(count)), 626 | OBJPROP_RAY_RIGHT, 627 | 0 628 | ); 629 | } 630 | 631 | count++; 632 | } 633 | -------------------------------------------------------------------------------- /Experts/ttn3Screen.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttn3Screen.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #property copyright "tickelton@gmail.com" 31 | #property link "https://github.com/tickelton" 32 | #property version "1.00" 33 | #property strict 34 | 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | extern int MagicNumber = 5131; 41 | 42 | extern double EquityPerTrade = 1.0; 43 | extern bool MACDonlyDiverging = true; 44 | extern int MACDFastEMA = 12; 45 | extern int MACDSlowEMA = 26; 46 | extern int MACDSMA = 9; 47 | enum eUseOscillator { 48 | OscForce = 1, 49 | OscStoch = 2, 50 | OscBoth = 3, 51 | }; 52 | extern eUseOscillator UseOscillator = OscForce; 53 | extern int OscForcePeriod = 13; 54 | extern int OscStochK = 5; 55 | extern int OscStochD = 3; 56 | extern int OscStochSlow = 3; 57 | extern int OscPeriodMax = 20; 58 | enum eExitSignal { 59 | ExitMACD = 1, 60 | ExitOsc = 2, 61 | }; 62 | extern eExitSignal ExitSignal = ExitMACD; 63 | extern bool ExitOnSingleOsc = true; 64 | enum eTrailSLMode { 65 | TrailNoTrail = 1, 66 | TrailToBreakEven = 2, 67 | Trail50Pct = 3, 68 | }; 69 | extern eTrailSLMode TrailSLMode = TrailToBreakEven; 70 | extern bool TrailSLOnTick = true; 71 | enum eStopSizeMode { 72 | StopSizePrice, 73 | StopSizeATR, 74 | StopSizeBoth, 75 | }; 76 | extern eStopSizeMode StopSizeMode = StopSizeBoth; 77 | extern double StopATR = 2.0; 78 | extern int ATRPeriod = 20; 79 | enum eLogLevel { 80 | LevelCritical = 0, 81 | LevelError = 1, 82 | LevelWarning = 2, 83 | LevelInfo = 3, 84 | LevelDebug = 4, 85 | LevelTrace = 5, 86 | }; 87 | extern eLogLevel LogLevel = LevelError; 88 | 89 | int PositionOpen = 0; 90 | int CurrentTicket = 0; 91 | double StopSize = 0; 92 | bool doEnter = false; 93 | datetime CurrentTimestamp; 94 | enum TradeDirection { 95 | DirectionInit, 96 | DirectionBuy, 97 | DirectionSell, 98 | DirectionNoTrade, 99 | }; 100 | TradeDirection Direction = DirectionInit; 101 | ENUM_TIMEFRAMES TFHigher = PERIOD_CURRENT; 102 | const int DefaultSlippage = 3; 103 | const string eaName = "ttn3Screen"; 104 | enum eSignalType { 105 | SignalEntry, 106 | SignalExit, 107 | }; 108 | enum ePeriodMaxType { 109 | PeriodLow, 110 | PeriodHigh, 111 | }; 112 | 113 | 114 | int OnInit() 115 | { 116 | int tfCurrent = Period(); 117 | 118 | if(tfCurrent == PERIOD_M1) { 119 | TFHigher = PERIOD_M5; 120 | } else if(tfCurrent == PERIOD_M5) { 121 | TFHigher = PERIOD_M30; 122 | } else if(tfCurrent == PERIOD_M15) { 123 | TFHigher = PERIOD_H1; 124 | } else if(tfCurrent == PERIOD_M30) { 125 | TFHigher = PERIOD_H4; 126 | } else if(tfCurrent == PERIOD_H1) { 127 | TFHigher = PERIOD_D1; 128 | } else if(tfCurrent == PERIOD_H4) { 129 | TFHigher = PERIOD_D1; 130 | } else if(tfCurrent == PERIOD_D1) { 131 | TFHigher = PERIOD_W1; 132 | } else { 133 | PrintFormat("Timeframe %d not supported", tfCurrent); 134 | return(INIT_FAILED); 135 | } 136 | 137 | if(LogLevel >= LevelDebug) { 138 | PrintFormat("Init complete tfCurrent=%d tfHigh=%d", tfCurrent, TFHigher); 139 | } 140 | 141 | return(INIT_SUCCEEDED); 142 | } 143 | 144 | void OnDeinit(const int reason) 145 | { 146 | 147 | 148 | } 149 | 150 | void OnTick() 151 | { 152 | 153 | bool NewBar = false; 154 | 155 | CheckEvents(MagicNumber); 156 | 157 | if(eventBuyClosed_SL > 0 || 158 | eventSellClosed_SL > 0 || 159 | eventBuyClosed_TP > 0 || 160 | eventSellClosed_TP > 0) { 161 | PositionOpen = 0; 162 | CurrentTicket = 0; 163 | Direction = DirectionInit; 164 | doEnter = false; 165 | StopSize = 0; 166 | } 167 | 168 | if(CurrentTimestamp != Time[0]) { 169 | CurrentTimestamp = Time[0]; 170 | NewBar = true; 171 | } 172 | 173 | if(NewBar) { 174 | GetDirection(); 175 | GetSignal(); 176 | if(PositionOpen) { 177 | TrailSL(); 178 | } else if(doEnter) { 179 | CheckEntry(); 180 | } 181 | } else { 182 | if(PositionOpen) { 183 | if(TrailSLOnTick) { 184 | TrailSL(); 185 | } 186 | } else if(doEnter) { 187 | CheckEntry(); 188 | } 189 | } 190 | } 191 | 192 | void GetDirection() 193 | { 194 | double maHighPrev = iMACD(NULL, TFHigher, MACDFastEMA, MACDSlowEMA, MACDSMA, PRICE_CLOSE, MODE_MAIN, 2); 195 | double maHighCur = iMACD(NULL, TFHigher, MACDFastEMA, MACDSlowEMA, MACDSMA, PRICE_CLOSE, MODE_MAIN, 1); 196 | if(LogLevel >= LevelTrace) { 197 | PrintFormat("GetDirection: maHighPrev=%f maHighCur=%f prevDirection=%d", maHighPrev, maHighCur, Direction); 198 | } 199 | 200 | if(!PositionOpen) { 201 | if(maHighCur < maHighPrev) { 202 | if(MACDonlyDiverging) { 203 | if(maHighCur < 0.0 || 204 | maHighPrev < 0.0) { 205 | Direction = DirectionNoTrade; 206 | return; 207 | } 208 | } 209 | Direction = DirectionSell; 210 | return; 211 | } else if(maHighCur > maHighPrev) { 212 | if(MACDonlyDiverging) { 213 | if(maHighCur > 0.0 || 214 | maHighPrev > 0.0) { 215 | Direction = DirectionNoTrade; 216 | return; 217 | } 218 | } 219 | Direction = DirectionBuy; 220 | return; 221 | } else { 222 | Direction = DirectionNoTrade; 223 | return; 224 | } 225 | } else if(ExitSignal == ExitMACD){ 226 | if(maHighCur < maHighPrev && 227 | Direction == DirectionBuy) { 228 | ClosePosition(); 229 | return; 230 | } else if(maHighCur > maHighPrev && 231 | Direction == DirectionSell) { 232 | ClosePosition(); 233 | return; 234 | } else { 235 | return; 236 | } 237 | } else if(ExitSignal == ExitOsc) { 238 | return; 239 | } 240 | 241 | Direction = DirectionInit; 242 | } 243 | 244 | void GetSignal() 245 | { 246 | if(Direction == DirectionInit || 247 | Direction == DirectionNoTrade) { 248 | doEnter = false; 249 | return; 250 | } 251 | 252 | if(PositionOpen) { 253 | if(ExitSignal == ExitOsc) { 254 | if(GetSignal(SignalExit)) { 255 | ClosePosition(); 256 | } 257 | } 258 | } else if(GetSignal(SignalEntry)) { 259 | doEnter = true; 260 | return; 261 | } 262 | 263 | doEnter = false; 264 | } 265 | 266 | bool GetSignal(eSignalType signalType) 267 | { 268 | bool signalForce = false; 269 | bool signalStoch = false; 270 | 271 | if(UseOscillator == OscForce) { 272 | signalStoch = true; 273 | signalForce = GetSignalForce(signalType); 274 | } else if(UseOscillator == OscStoch) { 275 | signalForce = true; 276 | signalStoch = GetSignalStoch(signalType); 277 | } else if(UseOscillator == OscBoth) { 278 | signalForce = GetSignalForce(signalType); 279 | signalStoch = GetSignalStoch(signalType); 280 | } else { 281 | return false; 282 | } 283 | 284 | if(LogLevel >= LevelTrace) { 285 | PrintFormat("GetSignal: type=%d signalForce=%d signalStoch=%d", signalType, signalForce, signalStoch); 286 | } 287 | 288 | if(signalType == SignalEntry) { 289 | if(signalForce && signalStoch) { 290 | return true; 291 | } 292 | } else { 293 | if(UseOscillator == OscBoth && 294 | ExitOnSingleOsc) { 295 | if(signalForce || signalStoch) { 296 | return true; 297 | } 298 | } else { 299 | if(signalForce && signalStoch) { 300 | return true; 301 | } 302 | } 303 | } 304 | 305 | return false; 306 | } 307 | 308 | bool GetSignalForce(eSignalType signalType) 309 | { 310 | double valForce = iForce(NULL, 0, OscForcePeriod, MODE_SMA, PRICE_CLOSE, 1); 311 | 312 | if(LogLevel >= LevelTrace) { 313 | PrintFormat("GetSignalForce: val=%f Direction=%d", valForce, Direction); 314 | } 315 | 316 | if(signalType == SignalEntry) { 317 | if(Direction == DirectionBuy) { 318 | double periodLow = GetForcePeriodMax(OscPeriodMax, PeriodLow); 319 | if(periodLow == -1.0) { 320 | return false; 321 | } 322 | if(valForce < 0.0 && 323 | valForce > periodLow) { 324 | return true; 325 | } 326 | } else if(Direction == DirectionSell) { 327 | double periodHigh = GetForcePeriodMax(OscPeriodMax, PeriodHigh); 328 | if(periodHigh == -1.0) { 329 | return false; 330 | } 331 | if(valForce > 0.0 && 332 | valForce < periodHigh) { 333 | return true; 334 | } 335 | } else { 336 | return false; 337 | } 338 | } else { 339 | if(Direction == DirectionBuy) { 340 | if(valForce > 0.0) { 341 | return true; 342 | } 343 | } else if(Direction == DirectionSell) { 344 | if(valForce < 0.0) { 345 | return true; 346 | } 347 | } 348 | } 349 | 350 | return false; 351 | } 352 | 353 | double GetForcePeriodMax(int periodLen, ePeriodMaxType type) 354 | { 355 | if(type == PeriodHigh) { 356 | double valForce = iForce(NULL, 0, OscForcePeriod, MODE_SMA, PRICE_CLOSE, 2); 357 | for(int i=3; i<=periodLen; i++) { 358 | double valForceTmp = iForce(NULL, 0, OscForcePeriod, MODE_SMA, PRICE_CLOSE, i); 359 | if(valForceTmp > valForce) { 360 | valForce = valForceTmp; 361 | } 362 | } 363 | return valForce; 364 | } else if(type == PeriodLow) { 365 | double valForce = iForce(NULL, 0, OscForcePeriod, MODE_SMA, PRICE_CLOSE, 2); 366 | for(int i=3; i<=periodLen; i++) { 367 | double valForceTmp = iForce(NULL, 0, OscForcePeriod, MODE_SMA, PRICE_CLOSE, i); 368 | if(valForceTmp < valForce) { 369 | valForce = valForceTmp; 370 | } 371 | } 372 | return valForce; 373 | } else { 374 | return -1.0; 375 | } 376 | } 377 | 378 | bool GetSignalStoch(eSignalType signalType) 379 | { 380 | double valStoch = iStochastic(NULL, 0, OscStochK, OscStochD, OscStochSlow, MODE_SMA, 0, MODE_MAIN, 1); 381 | 382 | if(signalType == SignalEntry) { 383 | if(Direction == DirectionBuy) { 384 | double periodLow = GetStochPeriodMax(OscPeriodMax, PeriodLow); 385 | if(periodLow == -1.0) { 386 | return false; 387 | } 388 | if(valStoch < 30.0 && 389 | valStoch > periodLow) { 390 | return true; 391 | } 392 | } else if(Direction == DirectionSell) { 393 | double periodHigh = GetForcePeriodMax(OscPeriodMax, PeriodHigh); 394 | if(periodHigh == -1.0) { 395 | return false; 396 | } 397 | if(valStoch > 70.0 && 398 | valStoch < periodHigh) { 399 | return true; 400 | } 401 | } else { 402 | return false; 403 | } 404 | } else { 405 | if(Direction == DirectionBuy) { 406 | if(valStoch > 30.0) { 407 | return true; 408 | } 409 | } else if(Direction == DirectionSell) { 410 | if(valStoch < 70.0) { 411 | return true; 412 | } 413 | } 414 | } 415 | 416 | return false; 417 | } 418 | 419 | double GetStochPeriodMax(int periodLen, ePeriodMaxType type) 420 | { 421 | if(type == PeriodHigh) { 422 | double valStoch = iStochastic(NULL, 0, OscStochK, OscStochD, OscStochSlow, MODE_SMA, 0, MODE_MAIN, 2); 423 | for(int i=3; i<=periodLen; i++) { 424 | double valStochTmp = iStochastic(NULL, 0, OscStochK, OscStochD, OscStochSlow, MODE_SMA, 0, MODE_MAIN, i); 425 | if(valStochTmp > valStoch) { 426 | valStoch = valStochTmp; 427 | } 428 | } 429 | return valStoch; 430 | } else if(type == PeriodLow) { 431 | double valStoch = iStochastic(NULL, 0, OscStochK, OscStochD, OscStochSlow, MODE_SMA, 0, MODE_MAIN, 2); 432 | for(int i=3; i<=periodLen; i++) { 433 | double valStochTmp = iStochastic(NULL, 0, OscStochK, OscStochD, OscStochSlow, MODE_SMA, 0, MODE_MAIN, i); 434 | if(valStochTmp < valStoch) { 435 | valStoch = valStochTmp; 436 | } 437 | } 438 | return valStoch; 439 | } else { 440 | return -1.0; 441 | } 442 | } 443 | 444 | void CheckEntry() { 445 | if(Direction == DirectionBuy) { 446 | double prevHigh = High[1]; 447 | if(Bid >= prevHigh + Point) { 448 | OpenPosition(); 449 | } 450 | } else if(Direction == DirectionSell) { 451 | double prevLow = Low[1]; 452 | if(Bid <= prevLow - Point) { 453 | OpenPosition(); 454 | } 455 | } 456 | } 457 | 458 | void ClosePosition() 459 | { 460 | if(CurrentTicket == 0) { 461 | Print("Fatal: cannot close; no current order"); 462 | } 463 | 464 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET) != true) { 465 | Print("Fatal: cannot select current order"); 466 | } 467 | 468 | bool closeRet = false; 469 | if(Direction == DirectionBuy) { 470 | closeRet = OrderClose(CurrentTicket, OrderLots(), Bid, DefaultSlippage, clrRed); 471 | } else if(Direction == DirectionSell) { 472 | closeRet = OrderClose(CurrentTicket, OrderLots(), Ask, DefaultSlippage, clrBlue); 473 | } 474 | 475 | if(closeRet != true) { 476 | Print("Fatal: cannot close current order"); 477 | return; 478 | } 479 | 480 | PositionOpen = 0; 481 | CurrentTicket = 0; 482 | Direction = DirectionInit; 483 | doEnter = false; 484 | StopSize = 0; 485 | return; 486 | } 487 | 488 | void OpenPosition() 489 | { 490 | double stopLevel = -1; 491 | if(Direction == DirectionBuy) { 492 | stopLevel = getStopLevel(Ask); 493 | } else if(Direction == DirectionSell) { 494 | stopLevel = getStopLevel(Bid); 495 | } 496 | 497 | if(stopLevel < 0) { 498 | if(LogLevel >= LevelError) { 499 | PrintFormat("OpenPosition: Error stopLevel."); 500 | } 501 | return; 502 | } 503 | 504 | double stopSize; 505 | if(Direction == DirectionBuy) { 506 | stopSize = (Ask - stopLevel) / Point; 507 | if(Point == 0.001 || Point == 0.00001) stopSize /= 10; 508 | } else if(Direction == DirectionSell) { 509 | stopSize = (stopLevel - Bid) / Point; 510 | if(Point == 0.001 || Point == 0.00001) stopSize /= 10; 511 | } else { 512 | if(LogLevel >= LevelError) { 513 | PrintFormat("OpenPosition: Error Direction."); 514 | } 515 | return; 516 | } 517 | double lotSize = getLotSize(stopSize); 518 | if(lotSize < 0) { 519 | if(LogLevel >= LevelError) { 520 | PrintFormat("OpenPosition: Error lotSize."); 521 | } 522 | return; 523 | } 524 | 525 | int ticketNr = -1; 526 | if(Direction == DirectionBuy) { 527 | ticketNr = OrderSend(Symbol(), OP_BUY, lotSize, Ask, DefaultSlippage, stopLevel, 0, "ttn3Screen Long", MagicNumber, 0, clrBlue); 528 | } else if(Direction == DirectionSell) { 529 | ticketNr = OrderSend(Symbol(), OP_SELL, lotSize, Bid, DefaultSlippage, stopLevel, 0, "ttn3Screen Short", MagicNumber, 0, clrRed); 530 | } else { 531 | return; 532 | } 533 | 534 | if(ticketNr != -1) { 535 | PositionOpen = 1; 536 | CurrentTicket = ticketNr; 537 | StopSize = stopSize * Point; 538 | if(LogLevel >= LevelDebug) { 539 | PrintFormat("TRADE: dir=%d stop=%f lotsize=%f ticket=%d", Direction, stopLevel, lotSize, ticketNr); 540 | } 541 | } 542 | 543 | CheckEvents(MagicNumber); 544 | if(eventBuyClosed_SL > 0 || 545 | eventSellClosed_SL > 0 || 546 | eventBuyClosed_TP > 0 || 547 | eventSellClosed_TP > 0) { 548 | PositionOpen = 0; 549 | CurrentTicket = 0; 550 | Direction = DirectionInit; 551 | doEnter = false; 552 | StopSize = 0; 553 | } 554 | } 555 | 556 | double getStopLevel(double currentPrice) 557 | { 558 | double level = 1.0; 559 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 560 | 561 | 562 | if(StopSizeMode == StopSizePrice) { 563 | if(Direction == DirectionBuy) { 564 | double curLow = Low[0]; 565 | double prevLow = Low[1]; 566 | if(curLow < prevLow) { 567 | level = curLow - Point; 568 | } else { 569 | level = prevLow - Point; 570 | } 571 | if(level > Ask - marketStopLevel) { 572 | PrintFormat("SL(%f) > Ask(%f) - minSL(%f)", level, Ask, marketStopLevel); 573 | return -1.0; 574 | } 575 | } else if (Direction == DirectionSell) { 576 | double curHigh = High[0]; 577 | double prevHigh = High[1]; 578 | if(curHigh > prevHigh) { 579 | level = curHigh + Point; 580 | } else { 581 | level = prevHigh + Point; 582 | } 583 | if(level < Bid + marketStopLevel) { 584 | PrintFormat("SL(%f) < Bid(%f) + minSL(%f)", level, Bid, marketStopLevel); 585 | return -1.0; 586 | } 587 | } else { 588 | return -1.0; 589 | } 590 | } else if(StopSizeMode == StopSizeATR) { 591 | double curATR = iATR(NULL, 0, ATRPeriod, 1); 592 | if(curATR == 0.0) { 593 | if(LogLevel >= LevelError) { 594 | PrintFormat("Error: ATR=%f", curATR); 595 | } 596 | return -1.0; 597 | } 598 | 599 | if(Direction == DirectionBuy) { 600 | level = Bid - (StopATR * curATR); 601 | if(level > Ask - marketStopLevel) { 602 | PrintFormat("SL(%f) > Ask(%f) - minSL(%f)", level, Ask, marketStopLevel); 603 | return -1.0; 604 | } 605 | return level; 606 | } else if(Direction == DirectionSell) { 607 | level = Ask + (StopATR * curATR); 608 | if(level < Bid + marketStopLevel) { 609 | PrintFormat("SL(%f) < Bid(%f) + minSL(%f)", level, Bid, marketStopLevel); 610 | return -1.0; 611 | } 612 | return level; 613 | } else { 614 | if(LogLevel >= LevelError) { 615 | PrintFormat("Error: GetStopSize: Direction=%d", Direction); 616 | } 617 | return -1.0; 618 | } 619 | } else if(StopSizeMode == StopSizeBoth) { 620 | double curATR = iATR(NULL, 0, ATRPeriod, 1); 621 | if(curATR == 0.0) { 622 | if(LogLevel >= LevelError) { 623 | PrintFormat("Error: ATR=%f", curATR); 624 | } 625 | return -1.0; 626 | } 627 | 628 | if(Direction == DirectionBuy) { 629 | double curLow = Low[0]; 630 | double prevLow = Low[1]; 631 | double levelPrice; 632 | double levelATR; 633 | if(curLow < prevLow) { 634 | levelPrice = curLow - Point; 635 | } else { 636 | levelPrice = prevLow - Point; 637 | } 638 | levelATR = Bid - (StopATR * curATR); 639 | if(levelATR < levelPrice) { 640 | level = levelATR; 641 | } else { 642 | level = levelPrice; 643 | } 644 | if(level > Ask - marketStopLevel) { 645 | PrintFormat("SL(%f) > Ask(%f) - minSL(%f)", level, Ask, marketStopLevel); 646 | return -1.0; 647 | } 648 | } else if (Direction == DirectionSell) { 649 | double curHigh = High[0]; 650 | double prevHigh = High[1]; 651 | double levelPrice; 652 | double levelATR; 653 | if(curHigh > prevHigh) { 654 | levelPrice = curHigh + Point; 655 | } else { 656 | levelPrice = prevHigh + Point; 657 | } 658 | levelATR = Ask + (StopATR * curATR); 659 | if(levelATR > levelPrice) { 660 | level = levelATR; 661 | } else { 662 | level = levelPrice; 663 | } 664 | if(level < Bid + marketStopLevel) { 665 | PrintFormat("SL(%f) < Bid(%f) + minSL(%f)", level, Bid, marketStopLevel); 666 | return -1.0; 667 | } 668 | } else { 669 | return -1.0; 670 | } 671 | } 672 | 673 | return level; 674 | } 675 | 676 | double getLotSize(double stopSize) 677 | { 678 | double riskAmount = AccountEquity() * (EquityPerTrade / 100); 679 | 680 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 681 | if(Point == 0.001 || Point == 0.00001) tickValue *= 10; 682 | 683 | double lotSize = (riskAmount / stopSize) / tickValue; 684 | 685 | if(lotSize < MarketInfo(Symbol(),MODE_MINLOT)) 686 | { 687 | return -1.0; 688 | } else if(lotSize > MarketInfo(Symbol(),MODE_MAXLOT)) 689 | { 690 | lotSize = MarketInfo(Symbol(),MODE_MAXLOT); 691 | } 692 | 693 | if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) 694 | { 695 | lotSize = NormalizeDouble(lotSize,1); 696 | } 697 | else { 698 | lotSize = NormalizeDouble(lotSize,2); 699 | } 700 | 701 | return lotSize; 702 | } 703 | 704 | void TrailSL() 705 | { 706 | if(TrailSLMode == TrailNoTrail) { 707 | return; 708 | } else if(TrailSLMode == TrailToBreakEven) { 709 | double trailPrice; 710 | if(TrailSLOnTick) { 711 | trailPrice = Close[0]; 712 | } else { 713 | trailPrice = Close[1]; 714 | } 715 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET)) { 716 | double orderOpenPrice = OrderOpenPrice(); 717 | double orderStopLoss = OrderStopLoss(); 718 | double currentStopDistance; 719 | 720 | if(orderStopLoss >= orderOpenPrice) { 721 | return; 722 | } 723 | 724 | if(Direction == DirectionBuy) { 725 | currentStopDistance = trailPrice - orderStopLoss; 726 | } else if(Direction == DirectionSell) { 727 | currentStopDistance = orderStopLoss - trailPrice; 728 | } else { 729 | return; 730 | } 731 | 732 | if(StopSize > 0 && 733 | currentStopDistance > StopSize) { 734 | 735 | double newStopLoss; 736 | if(Direction == DirectionBuy) { 737 | newStopLoss = trailPrice - StopSize; 738 | if(newStopLoss > (orderOpenPrice + (Ask - Bid))) 739 | newStopLoss = orderOpenPrice + (Ask - Bid); 740 | } else if(Direction == DirectionSell) { 741 | newStopLoss = trailPrice + StopSize; 742 | if(newStopLoss < (orderOpenPrice - (Bid - Ask))) 743 | newStopLoss = orderOpenPrice - (Bid - Ask); 744 | } else { 745 | return; 746 | } 747 | 748 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 749 | 750 | if(Direction == DirectionBuy) { 751 | if(newStopLoss > Bid - marketStopLevel) { 752 | if(Bid - marketStopLevel > orderStopLoss) { 753 | PrintFormat("%f > Bid(%f) - minSL(%f); setting new Sl to %f", 754 | newStopLoss, Bid, marketStopLevel, Bid - marketStopLevel); 755 | newStopLoss = Bid - marketStopLevel; 756 | } 757 | } 758 | if(newStopLoss < orderStopLoss) { 759 | Print("new SL would be < current SL; aborting"); 760 | return; 761 | } 762 | } else if(Direction == DirectionSell) { 763 | if(newStopLoss < Ask + marketStopLevel) { 764 | if(Ask + marketStopLevel < orderStopLoss) { 765 | PrintFormat("%f < Ask(%f) + minSL(%f); setting new Sl to %f", 766 | newStopLoss, Ask, marketStopLevel, Ask + marketStopLevel); 767 | newStopLoss = Ask + marketStopLevel; 768 | } 769 | } 770 | if(newStopLoss > orderStopLoss) { 771 | Print("new SL would be > current SL; aborting"); 772 | return; 773 | } 774 | } 775 | 776 | if(!OrderModify(CurrentTicket, OrderOpenPrice(), NormalizeDouble(newStopLoss, Digits), OrderTakeProfit(), 0, clrYellow)) { 777 | Print("Warning: error trailing SL ", ErrorDescription(GetLastError())); 778 | return; 779 | } 780 | } 781 | } else { 782 | if(LogLevel >= LevelError) { 783 | Print("TrailSL Error: Cannot select order: ",GetLastError()); 784 | return; 785 | } 786 | } 787 | } else if(TrailSLMode == Trail50Pct) { 788 | double trailPrice; 789 | if(TrailSLOnTick) { 790 | trailPrice = Close[0]; 791 | } else { 792 | trailPrice = Close[1]; 793 | } 794 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET)) { 795 | double orderOpenPrice = OrderOpenPrice(); 796 | double orderStopLoss = OrderStopLoss(); 797 | double currentStopDistance; 798 | 799 | if(orderStopLoss >= orderOpenPrice) { 800 | return; 801 | } 802 | 803 | if(Direction == DirectionBuy) { 804 | currentStopDistance = trailPrice - orderStopLoss; 805 | } else if(Direction == DirectionSell) { 806 | currentStopDistance = orderStopLoss - trailPrice; 807 | } else { 808 | return; 809 | } 810 | 811 | if(StopSize > 0 && 812 | currentStopDistance > StopSize) { 813 | 814 | double newStopLoss; 815 | if(Direction == DirectionBuy) { 816 | newStopLoss = trailPrice - StopSize; 817 | if(newStopLoss > (orderOpenPrice + (Ask - Bid))) 818 | newStopLoss = trailPrice - ((trailPrice - orderOpenPrice) / 2.0); 819 | } else if(Direction == DirectionSell) { 820 | newStopLoss = trailPrice + StopSize; 821 | if(newStopLoss < (orderOpenPrice - (Bid - Ask))) 822 | newStopLoss = trailPrice + ((orderOpenPrice - trailPrice) / 2.0); 823 | } else { 824 | return; 825 | } 826 | 827 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 828 | 829 | if(Direction == DirectionBuy) { 830 | if(newStopLoss > Bid - marketStopLevel) { 831 | if(Bid - marketStopLevel > orderStopLoss) { 832 | PrintFormat("%f > Bid(%f) - minSL(%f); setting new Sl to %f", 833 | newStopLoss, Bid, marketStopLevel, Bid - marketStopLevel); 834 | newStopLoss = Bid - marketStopLevel; 835 | } 836 | } 837 | if(newStopLoss < orderStopLoss) { 838 | Print("new SL would be < current SL; aborting"); 839 | return; 840 | } 841 | } else if(Direction == DirectionSell) { 842 | if(newStopLoss < Ask + marketStopLevel) { 843 | if(Ask + marketStopLevel < orderStopLoss) { 844 | PrintFormat("%f < Ask(%f) + minSL(%f); setting new Sl to %f", 845 | newStopLoss, Ask, marketStopLevel, Ask + marketStopLevel); 846 | newStopLoss = Ask + marketStopLevel; 847 | } 848 | } 849 | if(newStopLoss > orderStopLoss) { 850 | Print("new SL would be > current SL; aborting"); 851 | return; 852 | } 853 | } 854 | 855 | if(!OrderModify(CurrentTicket, OrderOpenPrice(), NormalizeDouble(newStopLoss, Digits), OrderTakeProfit(), 0, clrYellow)) { 856 | Print("Warning: error trailing SL ", ErrorDescription(GetLastError())); 857 | return; 858 | } 859 | } 860 | } else { 861 | if(LogLevel >= LevelError) { 862 | Print("TrailSL Error: Cannot select order: ",GetLastError()); 863 | return; 864 | } 865 | } 866 | } 867 | 868 | return; 869 | } 870 | -------------------------------------------------------------------------------- /Experts/ttnATRbreakout.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnATRbreakout.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #property copyright "tickelton@gmail.com" 36 | #property link "https://github.com/tickelton" 37 | #property version "1.00" 38 | #property strict 39 | 40 | extern int MagicNumber = 5128; 41 | 42 | extern double EquityPerTrade = 1.0; 43 | extern int NperTrade = 1; 44 | extern int MAPeriod = 350; 45 | extern ENUM_MA_METHOD MAmethod = MODE_EMA; 46 | extern int ATRPeriod = 20; 47 | extern double BullATR = 7.0; 48 | extern double BearATR = 3.0; 49 | extern bool ForceMinLot = false; 50 | 51 | enum eLogLevel { 52 | LevelCritical = 0, 53 | LevelError = 1, 54 | LevelWarning = 2, 55 | LevelInfo = 3, 56 | LevelDebug = 4, 57 | }; 58 | extern eLogLevel LogLevel = LevelError; 59 | 60 | int PositionOpen = 0; 61 | int CurrentTicket = 0; 62 | datetime CurrentTimestamp; 63 | enum TradeDirection { 64 | DirectionInit, 65 | DirectionBuy, 66 | DirectionSell, 67 | DirectionNoTrade, 68 | }; 69 | TradeDirection Direction = DirectionInit; 70 | const int DefaultSlippage = 3; 71 | const string eaName = "ttnATRbreakout"; 72 | 73 | int OnInit() 74 | { 75 | 76 | return(INIT_SUCCEEDED); 77 | } 78 | 79 | void OnDeinit(const int reason) 80 | { 81 | 82 | 83 | } 84 | 85 | void OnTick() 86 | { 87 | bool NewBar = false; 88 | 89 | CheckEvents(MagicNumber); 90 | 91 | if(eventBuyClosed_SL > 0 || 92 | eventSellClosed_SL > 0 || 93 | eventBuyClosed_TP > 0 || 94 | eventSellClosed_TP > 0) { 95 | PositionOpen = 0; 96 | } 97 | 98 | if(CurrentTimestamp != Time[0]) { 99 | CurrentTimestamp = Time[0]; 100 | NewBar = true; 101 | } 102 | 103 | if(NewBar) { 104 | if(PositionOpen) { 105 | CheckClose(); 106 | } 107 | if(!PositionOpen) { 108 | CheckOpen(); 109 | } 110 | } 111 | } 112 | 113 | void CheckOpen() 114 | { 115 | if(Bars <= MathMax(ATRPeriod, MAPeriod)) 116 | return; 117 | 118 | double atr = iATR(NULL, 0, ATRPeriod, 1); 119 | if(atr == 0.0) { 120 | if(LogLevel >= LevelError) { 121 | PrintFormat("Error: ATR=%f", atr); 122 | return; 123 | } 124 | } 125 | if(atr < 0.0001) { 126 | if(LogLevel >= LevelCritical) { 127 | PrintFormat("Warning: ATR=%f", atr); 128 | return; 129 | } 130 | } 131 | double ma = iMA(NULL, 0, MAPeriod, 0, MAmethod, PRICE_CLOSE, 1); 132 | double bullLimit = ma + (BullATR * atr); 133 | double bearLimit = ma - (BearATR * atr); 134 | 135 | if(Close[1] > bullLimit) { 136 | Direction = DirectionBuy; 137 | if(!OpenPosition()) { 138 | if(LogLevel >= LevelError) { 139 | Print("CheckOpen: Bull open error."); 140 | } 141 | } 142 | } else if(Close[1] < bearLimit) { 143 | Direction = DirectionSell; 144 | if(!OpenPosition()) { 145 | if(LogLevel >= LevelError) { 146 | Print("CheckOpen: Bear open error."); 147 | } 148 | } 149 | } 150 | } 151 | 152 | void CheckClose() 153 | { 154 | double ma = iMA(NULL, 0, MAPeriod, 0, MAmethod, PRICE_CLOSE, 1); 155 | 156 | if(Direction == DirectionBuy) { 157 | if(Close[1] < ma) { 158 | ClosePosition(); 159 | } 160 | } else if (Direction == DirectionSell) { 161 | if(Close[1] > ma) { 162 | ClosePosition(); 163 | } 164 | } 165 | } 166 | 167 | bool OpenPosition() 168 | { 169 | int ticketNr = -1; 170 | 171 | double lotSize = GetLotSize(); 172 | if(lotSize == -1.0) { 173 | if(LogLevel >= LevelError) { 174 | Print("PlaceOrder: error lotSize"); 175 | } 176 | return false; 177 | } 178 | if(Direction == DirectionBuy) { 179 | ticketNr = OrderSend(Symbol(), OP_BUY, lotSize, Ask, DefaultSlippage, 0, 0, eaName, MagicNumber, 0, clrBlue); 180 | } else if(Direction == DirectionSell) { 181 | ticketNr = OrderSend(Symbol(), OP_SELL, lotSize, Bid, DefaultSlippage, 0, 0, eaName, MagicNumber, 0, clrRed); 182 | } else { 183 | if(LogLevel >= LevelDebug) { 184 | Print("PlaceOrder: error Direction"); 185 | } 186 | return false; 187 | } 188 | 189 | if(ticketNr != -1) { 190 | PositionOpen = 1; 191 | CurrentTicket = ticketNr; 192 | if(LogLevel >= LevelDebug) { 193 | PrintFormat("TRADE: dir=%d ticket=%d", Direction, ticketNr); 194 | } 195 | } 196 | 197 | CheckEvents(MagicNumber); 198 | if(eventBuyClosed_SL > 0 || 199 | eventSellClosed_SL > 0 || 200 | eventBuyClosed_TP > 0 || 201 | eventSellClosed_TP > 0) { 202 | PositionOpen = 0; 203 | } 204 | 205 | return true; 206 | } 207 | 208 | double GetLotSize() 209 | { 210 | double curATR = iATR(NULL, 0, ATRPeriod, 1) / Point; 211 | if(curATR == 0.0) { 212 | if(LogLevel >= LevelError) { 213 | PrintFormat("Error: ATR=%f", curATR); 214 | } 215 | return -1.0; 216 | } 217 | double riskAmount = AccountEquity() * (EquityPerTrade / 100); 218 | 219 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 220 | if(Point == 0.001 || Point == 0.00001) tickValue *= 10; 221 | 222 | double lotSize = NperTrade * (riskAmount / (curATR * tickValue)); 223 | 224 | if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) 225 | { 226 | lotSize = NormalizeDouble(lotSize,1); 227 | } 228 | else { 229 | lotSize = NormalizeDouble(lotSize,2); 230 | } 231 | 232 | if(lotSize < MarketInfo(Symbol(),MODE_MINLOT)) 233 | { 234 | if(LogLevel >= LevelError) { 235 | PrintFormat("GetLotSize: lotSize=%f < MINLOT", lotSize); 236 | } 237 | if (ForceMinLot) { 238 | return MarketInfo(Symbol(),MODE_MINLOT); 239 | } else { 240 | return -1.0; 241 | } 242 | } else if(lotSize > MarketInfo(Symbol(),MODE_MAXLOT)) 243 | { 244 | lotSize = MarketInfo(Symbol(),MODE_MAXLOT); 245 | } 246 | 247 | return lotSize; 248 | 249 | } 250 | 251 | void ClosePosition() 252 | { 253 | if(CurrentTicket == 0) { 254 | Print("Fatal: cannot close; no current order"); 255 | } 256 | 257 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET) != true) { 258 | Print("Fatal: cannot select current order"); 259 | } 260 | 261 | bool closeRet = false; 262 | if(Direction == DirectionBuy) { 263 | closeRet = OrderClose(CurrentTicket, OrderLots(), Bid, DefaultSlippage, clrRed); 264 | } else if(Direction == DirectionSell) { 265 | closeRet = OrderClose(CurrentTicket, OrderLots(), Ask, DefaultSlippage, clrBlue); 266 | } 267 | 268 | if(closeRet != true) { 269 | Print("Fatal: cannot close current order"); 270 | return; 271 | } 272 | 273 | PositionOpen = 0; 274 | CurrentTicket = 0; 275 | Direction = DirectionInit; 276 | } 277 | -------------------------------------------------------------------------------- /Experts/ttnBollingerBreakout.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnBollingerBreakout.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #property copyright "tickelton@gmail.com" 36 | #property link "https://github.com/tickelton" 37 | #property version "1.00" 38 | #property strict 39 | 40 | extern int MagicNumber = 5129; 41 | 42 | extern double EquityPerTrade = 1.0; 43 | extern int NperTrade = 1; 44 | extern int MAPeriod = 350; 45 | extern double Deviations = 2.5; 46 | extern int ATRPeriod = 20; 47 | extern bool ForceMinLot = false; 48 | 49 | enum eLogLevel { 50 | LevelCritical = 0, 51 | LevelError = 1, 52 | LevelWarning = 2, 53 | LevelInfo = 3, 54 | LevelDebug = 4, 55 | }; 56 | extern eLogLevel LogLevel = LevelError; 57 | 58 | int PositionOpen = 0; 59 | int CurrentTicket = 0; 60 | datetime CurrentTimestamp; 61 | enum TradeDirection { 62 | DirectionInit, 63 | DirectionBuy, 64 | DirectionSell, 65 | DirectionNoTrade, 66 | }; 67 | TradeDirection Direction = DirectionInit; 68 | const int DefaultSlippage = 3; 69 | const string eaName = "ttnBollingerBreakout"; 70 | 71 | int OnInit() 72 | { 73 | 74 | return(INIT_SUCCEEDED); 75 | } 76 | 77 | void OnDeinit(const int reason) 78 | { 79 | 80 | 81 | } 82 | 83 | void OnTick() 84 | { 85 | bool NewBar = false; 86 | 87 | CheckEvents(MagicNumber); 88 | 89 | if(eventBuyClosed_SL > 0 || 90 | eventSellClosed_SL > 0 || 91 | eventBuyClosed_TP > 0 || 92 | eventSellClosed_TP > 0) { 93 | PositionOpen = 0; 94 | } 95 | 96 | if(CurrentTimestamp != Time[0]) { 97 | CurrentTimestamp = Time[0]; 98 | NewBar = true; 99 | } 100 | 101 | if(NewBar) { 102 | if(PositionOpen) { 103 | CheckClose(); 104 | } 105 | if(!PositionOpen) { 106 | CheckOpen(); 107 | } 108 | } 109 | } 110 | 111 | void CheckOpen() 112 | { 113 | if(Bars <= MAPeriod) 114 | return; 115 | 116 | double bollingerLow = iBands(NULL, 0, MAPeriod, Deviations, 0, PRICE_CLOSE, MODE_LOWER, 1); 117 | double bollingerHigh = iBands(NULL, 0, MAPeriod, Deviations, 0, PRICE_CLOSE, MODE_UPPER, 1); 118 | 119 | if(Close[1] > bollingerHigh) { 120 | Direction = DirectionBuy; 121 | if(!OpenPosition()) { 122 | if(LogLevel >= LevelError) { 123 | Print("CheckOpen: Bull open error."); 124 | } 125 | } 126 | } else if(Close[1] < bollingerLow) { 127 | Direction = DirectionSell; 128 | if(!OpenPosition()) { 129 | if(LogLevel >= LevelError) { 130 | Print("CheckOpen: Bear open error."); 131 | } 132 | } 133 | } 134 | } 135 | 136 | void CheckClose() 137 | { 138 | double ma = iMA(NULL, 0, MAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1); 139 | 140 | if(Direction == DirectionBuy) { 141 | if(Close[1] < ma) { 142 | ClosePosition(); 143 | } 144 | } else if (Direction == DirectionSell) { 145 | if(Close[1] > ma) { 146 | ClosePosition(); 147 | } 148 | } 149 | } 150 | 151 | bool OpenPosition() 152 | { 153 | int ticketNr = -1; 154 | 155 | double lotSize = GetLotSize(); 156 | if(lotSize == -1.0) { 157 | if(LogLevel >= LevelError) { 158 | Print("PlaceOrder: error lotSize"); 159 | } 160 | return false; 161 | } 162 | if(Direction == DirectionBuy) { 163 | ticketNr = OrderSend(Symbol(), OP_BUY, lotSize, Ask, DefaultSlippage, 0, 0, eaName, MagicNumber, 0, clrBlue); 164 | } else if(Direction == DirectionSell) { 165 | ticketNr = OrderSend(Symbol(), OP_SELL, lotSize, Bid, DefaultSlippage, 0, 0, eaName, MagicNumber, 0, clrRed); 166 | } else { 167 | if(LogLevel >= LevelDebug) { 168 | Print("PlaceOrder: error Direction"); 169 | } 170 | return false; 171 | } 172 | 173 | if(ticketNr != -1) { 174 | PositionOpen = 1; 175 | CurrentTicket = ticketNr; 176 | if(LogLevel >= LevelDebug) { 177 | PrintFormat("TRADE: dir=%d ticket=%d", Direction, ticketNr); 178 | } 179 | } 180 | 181 | CheckEvents(MagicNumber); 182 | if(eventBuyClosed_SL > 0 || 183 | eventSellClosed_SL > 0 || 184 | eventBuyClosed_TP > 0 || 185 | eventSellClosed_TP > 0) { 186 | PositionOpen = 0; 187 | } 188 | 189 | return true; 190 | } 191 | 192 | double GetLotSize() 193 | { 194 | double curATR = iATR(NULL, 0, ATRPeriod, 1) / Point; 195 | if(curATR == 0.0) { 196 | if(LogLevel >= LevelError) { 197 | PrintFormat("Error: ATR=%f", curATR); 198 | } 199 | return -1.0; 200 | } 201 | double riskAmount = AccountEquity() * (EquityPerTrade / 100); 202 | 203 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 204 | if(Point == 0.001 || Point == 0.00001) tickValue *= 10; 205 | 206 | double lotSize = NperTrade * (riskAmount / (curATR * tickValue)); 207 | 208 | if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) 209 | { 210 | lotSize = NormalizeDouble(lotSize,1); 211 | } 212 | else { 213 | lotSize = NormalizeDouble(lotSize,2); 214 | } 215 | 216 | if(lotSize < MarketInfo(Symbol(),MODE_MINLOT)) 217 | { 218 | if(LogLevel >= LevelError) { 219 | PrintFormat("GetLotSize: lotSize=%f < MINLOT", lotSize); 220 | } 221 | if (ForceMinLot) { 222 | return MarketInfo(Symbol(),MODE_MINLOT); 223 | } else { 224 | return -1.0; 225 | } 226 | } else if(lotSize > MarketInfo(Symbol(),MODE_MAXLOT)) 227 | { 228 | lotSize = MarketInfo(Symbol(),MODE_MAXLOT); 229 | } 230 | 231 | return lotSize; 232 | 233 | } 234 | 235 | void ClosePosition() 236 | { 237 | if(CurrentTicket == 0) { 238 | Print("Fatal: cannot close; no current order"); 239 | } 240 | 241 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET) != true) { 242 | Print("Fatal: cannot select current order"); 243 | } 244 | 245 | bool closeRet = false; 246 | if(Direction == DirectionBuy) { 247 | closeRet = OrderClose(CurrentTicket, OrderLots(), Bid, DefaultSlippage, clrRed); 248 | } else if(Direction == DirectionSell) { 249 | closeRet = OrderClose(CurrentTicket, OrderLots(), Ask, DefaultSlippage, clrBlue); 250 | } 251 | 252 | if(closeRet != true) { 253 | Print("Fatal: cannot close current order"); 254 | return; 255 | } 256 | 257 | PositionOpen = 0; 258 | CurrentTicket = 0; 259 | Direction = DirectionInit; 260 | } 261 | -------------------------------------------------------------------------------- /Experts/ttnDonchianBreakout.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnDonchianBreakout.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #property copyright "tickelton@gmail.com" 36 | #property link "https://github.com/tickelton" 37 | #property version "1.00" 38 | #property strict 39 | 40 | extern int MagicNumber = 5130; 41 | 42 | extern double EquityPerTrade = 1.0; 43 | extern int NperTrade = 1; 44 | extern int ATRPeriod = 20; 45 | extern int SlowMAPeriod = 370; 46 | extern int FastMAPeriod = 12; 47 | extern ENUM_MA_METHOD MAmethod = MODE_EMA; 48 | extern int EntryPerid = 20; 49 | extern int ExitPeriod = 10; 50 | extern double StopATR = 2.0; 51 | extern bool ForceMinLot = false; 52 | 53 | enum eLogLevel { 54 | LevelCritical = 0, 55 | LevelError = 1, 56 | LevelWarning = 2, 57 | LevelInfo = 3, 58 | LevelDebug = 4, 59 | }; 60 | extern eLogLevel LogLevel = LevelError; 61 | 62 | int PositionOpen = 0; 63 | int CurrentTicket = 0; 64 | datetime CurrentTimestamp; 65 | enum TradeDirection { 66 | DirectionInit, 67 | DirectionBuy, 68 | DirectionSell, 69 | DirectionNoTrade, 70 | }; 71 | TradeDirection Direction = DirectionInit; 72 | const int DefaultSlippage = 3; 73 | const string eaName = "ttnDonchianBreakout"; 74 | 75 | int OnInit() 76 | { 77 | 78 | return(INIT_SUCCEEDED); 79 | } 80 | 81 | void OnDeinit(const int reason) 82 | { 83 | 84 | 85 | } 86 | 87 | void OnTick() 88 | { 89 | bool NewBar = false; 90 | 91 | CheckEvents(MagicNumber); 92 | 93 | if(eventBuyClosed_SL > 0 || 94 | eventSellClosed_SL > 0 || 95 | eventBuyClosed_TP > 0 || 96 | eventSellClosed_TP > 0) { 97 | PositionOpen = 0; 98 | } 99 | 100 | if(CurrentTimestamp != Time[0]) { 101 | CurrentTimestamp = Time[0]; 102 | NewBar = true; 103 | } 104 | 105 | if(NewBar) { 106 | if(PositionOpen) { 107 | CheckClose(); 108 | } 109 | if(!PositionOpen) { 110 | CheckOpen(); 111 | } 112 | } 113 | } 114 | 115 | void CheckOpen() 116 | { 117 | if(Bars <= SlowMAPeriod) 118 | return; 119 | 120 | TradeDirection direction = getDirection(); 121 | 122 | if(direction == DirectionBuy) { 123 | double highVal = iHigh(Symbol(),Period(),iHighest(Symbol(),Period(),MODE_HIGH,EntryPerid,2)); 124 | if(Close[1] > highVal) { 125 | Direction = DirectionBuy; 126 | if(!OpenPosition()) { 127 | if(LogLevel >= LevelError) { 128 | Print("CheckOpen: Bull open error."); 129 | } 130 | } 131 | } 132 | } else if(direction == DirectionSell) { 133 | double lowVal = iLow(Symbol(),Period(),iLowest(Symbol(),Period(),MODE_LOW,EntryPerid,2)); 134 | if(Close[1] < lowVal) { 135 | Direction = DirectionSell; 136 | if(!OpenPosition()) { 137 | if(LogLevel >= LevelError) { 138 | Print("CheckOpen: Bear open error."); 139 | } 140 | } 141 | } 142 | } 143 | } 144 | 145 | TradeDirection getDirection() 146 | { 147 | double slowMA = iMA(NULL, 0, SlowMAPeriod, 0, MAmethod, PRICE_CLOSE, 1); 148 | double fastMA = iMA(NULL, 0, FastMAPeriod, 0, MAmethod, PRICE_CLOSE, 1); 149 | 150 | if(fastMA > slowMA) { 151 | return DirectionBuy; 152 | } else if(fastMA < slowMA) { 153 | return DirectionSell; 154 | } else { 155 | return DirectionNoTrade; 156 | } 157 | 158 | return DirectionInit; 159 | } 160 | 161 | void CheckClose() 162 | { 163 | if(Direction == DirectionBuy) { 164 | double lowVal = iLow(Symbol(),Period(),iLowest(Symbol(),Period(),MODE_LOW,ExitPeriod,2)); 165 | if(Close[1] < lowVal) { 166 | ClosePosition(); 167 | } 168 | } else if(Direction == DirectionSell) { 169 | double highVal = iHigh(Symbol(),Period(),iHighest(Symbol(),Period(),MODE_HIGH,ExitPeriod,2)); 170 | if(Close[1] > highVal) { 171 | ClosePosition(); 172 | } 173 | } 174 | } 175 | 176 | bool OpenPosition() 177 | { 178 | int ticketNr = -1; 179 | 180 | double lotSize = GetLotSize(); 181 | if(lotSize == -1.0) { 182 | if(LogLevel >= LevelError) { 183 | Print("PlaceOrder: error lotSize"); 184 | } 185 | return false; 186 | } 187 | 188 | double stopSize = GetStopSize(); 189 | if(stopSize == -1.0 || 190 | stopSize < 0) { 191 | if(LogLevel >= LevelError) { 192 | Print("PlaceOrder: error stopSize"); 193 | } 194 | return false; 195 | } 196 | 197 | if(Direction == DirectionBuy) { 198 | ticketNr = OrderSend(Symbol(), OP_BUY, lotSize, Ask, DefaultSlippage, stopSize, 0, eaName, MagicNumber, 0, clrBlue); 199 | } else if(Direction == DirectionSell) { 200 | ticketNr = OrderSend(Symbol(), OP_SELL, lotSize, Bid, DefaultSlippage, stopSize, 0, eaName, MagicNumber, 0, clrRed); 201 | } else { 202 | if(LogLevel >= LevelDebug) { 203 | Print("PlaceOrder: error Direction"); 204 | } 205 | return false; 206 | } 207 | 208 | if(ticketNr != -1) { 209 | PositionOpen = 1; 210 | CurrentTicket = ticketNr; 211 | if(LogLevel >= LevelDebug) { 212 | PrintFormat("TRADE: dir=%d ticket=%d", Direction, ticketNr); 213 | } 214 | } 215 | 216 | CheckEvents(MagicNumber); 217 | if(eventBuyClosed_SL > 0 || 218 | eventSellClosed_SL > 0 || 219 | eventBuyClosed_TP > 0 || 220 | eventSellClosed_TP > 0) { 221 | PositionOpen = 0; 222 | } 223 | 224 | return true; 225 | } 226 | 227 | double GetStopSize() 228 | { 229 | double curATR = iATR(NULL, 0, ATRPeriod, 1); 230 | if(curATR == 0.0) { 231 | if(LogLevel >= LevelError) { 232 | PrintFormat("Error: ATR=%f", curATR); 233 | } 234 | return -1.0; 235 | } 236 | 237 | double stopSize; 238 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 239 | 240 | if(Direction == DirectionBuy) { 241 | stopSize = Bid - (StopATR * curATR); 242 | if(stopSize > Ask - marketStopLevel) { 243 | PrintFormat("SL(%f) > Ask(%f) - minSL(%f)", stopSize, Ask, marketStopLevel); 244 | return -1.0; 245 | } 246 | return stopSize; 247 | } else if(Direction == DirectionSell) { 248 | stopSize = Ask + (StopATR * curATR); 249 | if(stopSize < Bid + marketStopLevel) { 250 | PrintFormat("SL(%f) < Bid(%f) + minSL(%f)", stopSize, Bid, marketStopLevel); 251 | return -1.0; 252 | } 253 | return stopSize; 254 | } else { 255 | if(LogLevel >= LevelError) { 256 | PrintFormat("Error: GetStopSize: Direction=%d", Direction); 257 | } 258 | return -1.0; 259 | } 260 | 261 | return -1.0; 262 | } 263 | 264 | double GetLotSize() 265 | { 266 | double curATR = iATR(NULL, 0, ATRPeriod, 1) / Point; 267 | if(curATR == 0.0) { 268 | if(LogLevel >= LevelError) { 269 | PrintFormat("Error: ATR=%f", curATR); 270 | } 271 | return -1.0; 272 | } 273 | double riskAmount = AccountEquity() * (EquityPerTrade / 100); 274 | 275 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 276 | if(Point == 0.001 || Point == 0.00001) tickValue *= 10; 277 | 278 | double lotSize = NperTrade * (riskAmount / (curATR * tickValue)); 279 | 280 | if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) 281 | { 282 | lotSize = NormalizeDouble(lotSize,1); 283 | } 284 | else { 285 | lotSize = NormalizeDouble(lotSize,2); 286 | } 287 | 288 | if(lotSize < MarketInfo(Symbol(),MODE_MINLOT)) 289 | { 290 | if(LogLevel >= LevelError) { 291 | PrintFormat("GetLotSize: lotSize=%f < MINLOT", lotSize); 292 | } 293 | if (ForceMinLot) { 294 | return MarketInfo(Symbol(),MODE_MINLOT); 295 | } else { 296 | return -1.0; 297 | } 298 | } else if(lotSize > MarketInfo(Symbol(),MODE_MAXLOT)) 299 | { 300 | lotSize = MarketInfo(Symbol(),MODE_MAXLOT); 301 | } 302 | 303 | return lotSize; 304 | 305 | } 306 | 307 | void ClosePosition() 308 | { 309 | if(CurrentTicket == 0) { 310 | Print("Fatal: cannot close; no current order"); 311 | } 312 | 313 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET) != true) { 314 | Print("Fatal: cannot select current order"); 315 | } 316 | 317 | bool closeRet = false; 318 | if(Direction == DirectionBuy) { 319 | closeRet = OrderClose(CurrentTicket, OrderLots(), Bid, DefaultSlippage, clrRed); 320 | } else if(Direction == DirectionSell) { 321 | closeRet = OrderClose(CurrentTicket, OrderLots(), Ask, DefaultSlippage, clrBlue); 322 | } 323 | 324 | if(closeRet != true) { 325 | Print("Fatal: cannot close current order"); 326 | return; 327 | } 328 | 329 | PositionOpen = 0; 330 | CurrentTicket = 0; 331 | Direction = DirectionInit; 332 | } 333 | -------------------------------------------------------------------------------- /Experts/ttnFullTurtle.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnFullTurtle.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #property copyright "tickelton@gmail.com" 36 | #property link "https://github.com/tickelton" 37 | #property version "1.00" 38 | #property strict 39 | 40 | enum eStrategyType { 41 | StrategyTypeS1, 42 | StrategyTypeS2, 43 | }; 44 | 45 | enum eLosingS1Trade { 46 | WasS1LoserYes, 47 | WasS1LoserNo, 48 | WasS1LoserNext, 49 | }; 50 | 51 | enum eMAFilterDirection { 52 | DirMABuy, 53 | DirMASell, 54 | DirMABoth, 55 | }; 56 | 57 | struct sTradeStatus { 58 | int openOffset; 59 | datetime lastTradeTime; 60 | eTradeDirection direction; 61 | }; 62 | 63 | extern int MagicNumber_System1 = ttnMagicNumberTurtleSystem1; 64 | extern int MagicNumber_System2 = ttnMagicNumberTurtleSystem2; 65 | extern int PeriodS1Entry = 20; 66 | extern int PeriodS1Exit = 10; 67 | extern int PeriodS2Entry = 55; 68 | extern int PeriodS2Exit = 20; 69 | extern int PeriodATR = 14; 70 | extern int SlowMAPeriod = 350; 71 | extern int FastMAPeriod = 25; 72 | extern bool SkipS1AfterWinningTrade = true; 73 | extern bool UseMAFilter = true; 74 | extern double EquityPctPerN = 1.0; 75 | extern int UnitsPerTrade = 1; 76 | extern int StopSizeInN = 2; 77 | extern double PyramidingThresholdN = 0.5; 78 | extern int MaxPyramidingTimeframe = 5; 79 | extern int MaxUnitsPerCurrencyPair = 4; 80 | extern int MaxUnitStronglyCorrelated = 5; 81 | extern int MaxUnitMildlyCorrelated = 7; 82 | extern int MaxUnitsPerDirection = 10; 83 | extern eLogLevel LogLevel = LevelError; 84 | 85 | const string eaName = "ttnFullTurtle"; 86 | datetime CurrentTimestamp; 87 | 88 | int OnInit() 89 | { 90 | 91 | return(INIT_SUCCEEDED); 92 | } 93 | 94 | void OnDeinit(const int reason) 95 | { 96 | 97 | 98 | } 99 | 100 | void OnTick() 101 | { 102 | CheckExit(StrategyTypeS2); 103 | CheckExit(StrategyTypeS1); 104 | CheckEntry(StrategyTypeS2); 105 | CheckEntry(StrategyTypeS1); 106 | } 107 | 108 | void CheckExit(eStrategyType strategyType) 109 | { 110 | bool gotBullOrders = false; 111 | bool gotBearOrders = false; 112 | int magic; 113 | 114 | if(strategyType == StrategyTypeS1) { 115 | magic = MagicNumber_System1; 116 | } else if(strategyType == StrategyTypeS2) { 117 | magic = MagicNumber_System2; 118 | } else { 119 | if(LogLevel >= LevelError) { 120 | PrintFormat("Error: CheckExit: Invalid strategy type: %d", strategyType); 121 | } 122 | return; 123 | } 124 | 125 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 126 | { 127 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 128 | 129 | if(OrderMagicNumber() == magic && 130 | OrderSymbol() == Symbol()) 131 | { 132 | int type = OrderType(); 133 | if(type == OP_BUY || 134 | type == OP_BUYLIMIT || 135 | type == OP_BUYSTOP) 136 | { 137 | gotBullOrders = true; 138 | } else if (type == OP_SELL || 139 | type == OP_SELLLIMIT || 140 | type == OP_SELLSTOP) 141 | { 142 | gotBearOrders = true; 143 | } 144 | } 145 | } 146 | 147 | if(gotBullOrders) { 148 | CheckBullExit(strategyType); 149 | } 150 | if(gotBearOrders) { 151 | CheckBearExit(strategyType); 152 | } 153 | } 154 | 155 | void CheckBullExit(eStrategyType strategyType) 156 | { 157 | int period; 158 | int magic; 159 | if(strategyType == StrategyTypeS1) { 160 | period = PeriodS1Exit; 161 | magic = MagicNumber_System1; 162 | } else if(strategyType == StrategyTypeS2) { 163 | period = PeriodS2Exit; 164 | magic = MagicNumber_System2; 165 | } else { 166 | if(LogLevel >= LevelError) { 167 | PrintFormat("Error: CheckBullExit: Invalid strategy type: %d", strategyType); 168 | } 169 | return; 170 | } 171 | 172 | double lowVal = iLow(Symbol(),Period(),iLowest(Symbol(),Period(),MODE_LOW,period,1)); 173 | if(Bid < lowVal) { 174 | CloseBullPositions(magic); 175 | } 176 | } 177 | 178 | void CheckBearExit(eStrategyType strategyType) 179 | { 180 | int period; 181 | int magic; 182 | if(strategyType == StrategyTypeS1) { 183 | period = PeriodS1Exit; 184 | magic = MagicNumber_System1; 185 | } else if(strategyType == StrategyTypeS2) { 186 | period = PeriodS2Exit; 187 | magic = MagicNumber_System2; 188 | } else { 189 | if(LogLevel >= LevelError) { 190 | PrintFormat("Error: CheckBearExit: Invalid strategy type: %d", strategyType); 191 | } 192 | return; 193 | } 194 | 195 | double highVal = iHigh(Symbol(),Period(),iHighest(Symbol(),Period(),MODE_HIGH,period,1)); 196 | if(Bid > highVal) { 197 | CloseBearPositions(magic); 198 | } 199 | } 200 | 201 | void CloseBullPositions(int magic) 202 | { 203 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 204 | { 205 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 206 | 207 | if(OrderMagicNumber() == magic && 208 | OrderSymbol() == Symbol()) 209 | { 210 | int type = OrderType(); 211 | if(type == OP_BUY) 212 | { 213 | if(!OrderClose(OrderTicket(), OrderLots(), Bid, ttnDefaultSlippage, ttnClrBuyClose)) 214 | { 215 | if(LogLevel >= LevelError) 216 | { 217 | Print("Error: Cannot close order: " + ErrorDescription(GetLastError())); 218 | } 219 | } 220 | } 221 | else if(type == OP_BUYLIMIT || 222 | type == OP_BUYSTOP) 223 | { 224 | if(!OrderDelete(OrderTicket(), ttnClrDelete)) 225 | { 226 | if(LogLevel >= LevelError) 227 | { 228 | Print("Error: Cannot delete order: " + ErrorDescription(GetLastError())); 229 | } 230 | } 231 | } 232 | } 233 | } 234 | } 235 | 236 | void CloseBearPositions(int magic) 237 | { 238 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 239 | { 240 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 241 | 242 | if(OrderMagicNumber() == magic && 243 | OrderSymbol() == Symbol()) 244 | { 245 | int type = OrderType(); 246 | if (type == OP_SELL) 247 | { 248 | if(!OrderClose(OrderTicket(), OrderLots(), Ask, ttnDefaultSlippage, ttnClrSellClose)) 249 | { 250 | if(LogLevel >= LevelError) 251 | { 252 | Print("Error: Cannot close order: " + ErrorDescription(GetLastError())); 253 | } 254 | } 255 | } 256 | else if(type == OP_SELLLIMIT || 257 | type == OP_SELLSTOP) 258 | { 259 | if(!OrderDelete(OrderTicket(), ttnClrDelete)) 260 | { 261 | if(LogLevel >= LevelError) 262 | { 263 | Print("Error: Cannot delete order: " + ErrorDescription(GetLastError())); 264 | } 265 | } 266 | } 267 | } 268 | } 269 | } 270 | 271 | void CheckEntry(eStrategyType strategyType) 272 | { 273 | if(strategyType != StrategyTypeS1 && strategyType != StrategyTypeS2) { 274 | if(LogLevel >= LevelError) { 275 | PrintFormat("Error: CheckEntry: Invalid strategy type: %d", strategyType); 276 | } 277 | return; 278 | } 279 | 280 | sTradeStatus tradeStatus = CurrentlyTrading(strategyType); 281 | if(tradeStatus.openOffset > -1) { 282 | if(GotFreeVolume(tradeStatus)) { 283 | CheckPyramiding(strategyType, tradeStatus); 284 | } 285 | } else if(tradeStatus.openOffset == -1) { 286 | eTradeDirection tmpDir = EntrySignalGiven(strategyType); 287 | if(tmpDir != DIR_NONE) { 288 | tradeStatus.direction = tmpDir; 289 | if(GotFreeVolume(tradeStatus)) { 290 | if(EnterPosition(strategyType, tradeStatus) == -1) { 291 | if(LogLevel >= LevelError) { 292 | Print("CheckEntry: Error opening position."); 293 | } 294 | } 295 | } 296 | } 297 | } else { 298 | if(LogLevel >= LevelError) { 299 | Print("CheckEntry: Unable to determine trading status."); 300 | } 301 | } 302 | } 303 | 304 | sTradeStatus CurrentlyTrading(eStrategyType strategyType) 305 | { 306 | sTradeStatus tradeStatus; 307 | tradeStatus.openOffset = -2; 308 | tradeStatus.direction = DIR_NONE; 309 | int magic; 310 | 311 | if(strategyType == StrategyTypeS1) { 312 | magic = MagicNumber_System1; 313 | } else if(strategyType == StrategyTypeS2) { 314 | magic = MagicNumber_System2; 315 | } else { 316 | if(LogLevel >= LevelError) { 317 | PrintFormat("Error: CurrentlyTrading: Invalid strategy type: %d", strategyType); 318 | } 319 | return tradeStatus; 320 | } 321 | 322 | 323 | bool gotBullOrders = false; 324 | bool gotBearOrders = false; 325 | 326 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 327 | { 328 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 329 | 330 | if(OrderMagicNumber() == magic && 331 | OrderSymbol() == Symbol()) 332 | { 333 | int type = OrderType(); 334 | if(type == OP_BUY || 335 | type == OP_BUYLIMIT || 336 | type == OP_BUYSTOP) 337 | { 338 | gotBullOrders = true; 339 | } else if (type == OP_SELL || 340 | type == OP_SELLLIMIT || 341 | type == OP_SELLSTOP) 342 | { 343 | gotBearOrders = true; 344 | } 345 | } 346 | } 347 | 348 | if(!gotBearOrders && !gotBullOrders) { 349 | tradeStatus.openOffset = -1; 350 | } else if(gotBearOrders && gotBullOrders) { 351 | if(LogLevel >= LevelCritical) { 352 | PrintFormat( 353 | "Critical: CurrentlyTrading(%d): Open orders in both directions!", 354 | strategyType 355 | ); 356 | } 357 | tradeStatus.openOffset = -2; 358 | } else if(gotBearOrders && !gotBullOrders) { 359 | tradeStatus.direction = DIR_SELL; 360 | GetOrderStatus(strategyType, tradeStatus); 361 | } else if(!gotBearOrders && gotBullOrders) { 362 | tradeStatus.direction = DIR_BUY; 363 | GetOrderStatus(strategyType, tradeStatus); 364 | } else { 365 | if(LogLevel >= LevelCritical) { 366 | PrintFormat( 367 | "Critical: CurrentlyTrading(%d): This should not happen!", 368 | strategyType 369 | ); 370 | } 371 | tradeStatus.openOffset = -2; 372 | } 373 | 374 | return tradeStatus; 375 | } 376 | 377 | void GetOrderStatus(eStrategyType strategyType, sTradeStatus &status) 378 | { 379 | int magic; 380 | if(strategyType == StrategyTypeS1) { 381 | magic = MagicNumber_System1; 382 | } else if(strategyType == StrategyTypeS2) { 383 | magic = MagicNumber_System2; 384 | } else { 385 | if(LogLevel >= LevelError) { 386 | PrintFormat("Error: GetOpenOffset: Invalid strategy type: %d", strategyType); 387 | } 388 | status.openOffset = -2; 389 | return; 390 | } 391 | 392 | if(status.direction != DIR_BUY && status.direction != DIR_SELL) { 393 | if(LogLevel >= LevelError) { 394 | PrintFormat("Error: GetOpenOffset: Invalid direction: %d", status.direction); 395 | } 396 | status.openOffset = -2; 397 | return; 398 | } 399 | 400 | datetime openTime = D'01.01.1970'; 401 | datetime lastTradeTime = D'01.01.1970'; 402 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 403 | { 404 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 405 | 406 | if(OrderMagicNumber() == magic && 407 | OrderSymbol() == Symbol()) 408 | { 409 | int type = OrderType(); 410 | if(type == OP_BUY || 411 | type == OP_BUYLIMIT || 412 | type == OP_BUYSTOP) 413 | { 414 | if(status.direction == DIR_SELL) { 415 | if(LogLevel >= LevelCritical) { 416 | PrintFormat( 417 | "Critical: GetOpenOffset(%d): Looking for Sell oders but found Buy order!" 418 | ); 419 | } 420 | status.openOffset = -2; 421 | return; 422 | } 423 | if(openTime == D'01.01.1970') { 424 | openTime = OrderOpenTime(); 425 | } else { 426 | datetime tmpTime = OrderOpenTime(); 427 | if(tmpTime < openTime) { 428 | openTime = tmpTime; 429 | } 430 | } 431 | if(lastTradeTime == D'01.01.1970') { 432 | lastTradeTime = OrderOpenTime(); 433 | } else { 434 | datetime tmpTime = OrderOpenTime(); 435 | if(tmpTime > lastTradeTime) { 436 | lastTradeTime = tmpTime; 437 | } 438 | } 439 | } else if (type == OP_SELL || 440 | type == OP_SELLLIMIT || 441 | type == OP_SELLSTOP) 442 | { 443 | if(status.direction == DIR_BUY) { 444 | if(LogLevel >= LevelCritical) { 445 | PrintFormat( 446 | "Critical: GetOpenOffset(%d): Looking for Buy oders but found Sell order!" 447 | ); 448 | } 449 | status.openOffset = -2; 450 | return; 451 | } 452 | if(openTime == D'01.01.1970') { 453 | openTime = OrderOpenTime(); 454 | } else { 455 | datetime tmpTime = OrderOpenTime(); 456 | if(tmpTime < openTime) { 457 | openTime = tmpTime; 458 | } 459 | } 460 | if(lastTradeTime == D'01.01.1970') { 461 | lastTradeTime = OrderOpenTime(); 462 | } else { 463 | datetime tmpTime = OrderOpenTime(); 464 | if(tmpTime > lastTradeTime) { 465 | lastTradeTime = tmpTime; 466 | } 467 | } 468 | } 469 | } 470 | } 471 | 472 | if(openTime != D'01.01.1970' && 473 | lastTradeTime != D'01.01.1970') { 474 | status.lastTradeTime = lastTradeTime; 475 | status.openOffset = GetPeriodOffset(openTime); 476 | } else { 477 | status.openOffset = -2; 478 | } 479 | 480 | return; 481 | } 482 | 483 | int GetPeriodOffset(datetime openTime) 484 | { 485 | datetime curTime = TimeCurrent(); 486 | int timeDiff = (int)(curTime - openTime); 487 | 488 | if(timeDiff < 0) { 489 | if(LogLevel >= LevelError) { 490 | PrintFormat("Error: GetPeriodOffset: Negative timeDiff: %d", timeDiff); 491 | } 492 | return -2; 493 | } 494 | 495 | int tfCurrent = Period(); 496 | if(tfCurrent == PERIOD_M1) { 497 | return timeDiff / 60; 498 | } else if(tfCurrent == PERIOD_M5) { 499 | return timeDiff / 300; 500 | } else if(tfCurrent == PERIOD_M15) { 501 | return timeDiff / 900; 502 | } else if(tfCurrent == PERIOD_M30) { 503 | return timeDiff / 1800; 504 | } else if(tfCurrent == PERIOD_H1) { 505 | return timeDiff / 3600; 506 | } else if(tfCurrent == PERIOD_H4) { 507 | return timeDiff / 14400; 508 | } else if(tfCurrent == PERIOD_D1) { 509 | return timeDiff / 86400; 510 | } else { 511 | PrintFormat("Timeframe %d not supported", tfCurrent); 512 | return -2; 513 | } 514 | 515 | return -2; 516 | } 517 | 518 | bool GotFreeVolume(sTradeStatus &tradeStatus) 519 | { 520 | double equityPerN = AccountBalance() *(EquityPctPerN / 100.0); 521 | 522 | double equityPerTrade = equityPerN * UnitsPerTrade; 523 | 524 | double equityPair = 0; 525 | double equityMildCorrelation = 0; 526 | double equityStrongCorrelation = 0; 527 | double equityDirection = 0; 528 | int unitsPair = 0; 529 | int unitsMildCorrelation = 0; 530 | int unitsStrongCorrelation = 0; 531 | int unitsDirection = 0; 532 | 533 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 534 | { 535 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 536 | 537 | double tmpRisk = 0; 538 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 539 | int orderMagic = OrderMagicNumber(); 540 | 541 | int type = OrderType(); 542 | if(type == OP_BUY) 543 | { 544 | tmpRisk = ((OrderOpenPrice() - OrderStopLoss()) * OrderLots() * tickValue) / Point; 545 | if(tradeStatus.direction == DIR_BUY) { 546 | equityDirection += tmpRisk; 547 | if(orderMagic == MagicNumber_System1 || 548 | orderMagic == MagicNumber_System2) { 549 | unitsDirection++; 550 | } 551 | } 552 | } else if (type == OP_SELL) 553 | { 554 | tmpRisk = ((OrderStopLoss() - OrderOpenPrice()) * OrderLots() * tickValue) / Point; 555 | if(tradeStatus.direction == DIR_SELL) { 556 | equityDirection += tmpRisk; 557 | if(orderMagic == MagicNumber_System1 || 558 | orderMagic == MagicNumber_System2) { 559 | unitsDirection++; 560 | } 561 | } 562 | } 563 | 564 | if(!StringCompare(Symbol(), OrderSymbol())) { 565 | equityPair += tmpRisk; 566 | if(orderMagic == MagicNumber_System1 || 567 | orderMagic == MagicNumber_System2) { 568 | unitsPair++; 569 | } 570 | } 571 | if(IsCorrelated(Symbol(), OrderSymbol(), CORRELATION_MILD)) { 572 | equityMildCorrelation += tmpRisk; 573 | if(orderMagic == MagicNumber_System1 || 574 | orderMagic == MagicNumber_System2) { 575 | unitsMildCorrelation++; 576 | } 577 | } 578 | if(IsCorrelated(Symbol(), OrderSymbol(), CORRELATION_STRONG)) { 579 | equityStrongCorrelation += tmpRisk; 580 | if(orderMagic == MagicNumber_System1 || 581 | orderMagic == MagicNumber_System2) { 582 | unitsStrongCorrelation++; 583 | } 584 | } 585 | 586 | } 587 | 588 | if(unitsPair >= MaxUnitsPerCurrencyPair) { 589 | return false; 590 | } 591 | if(unitsMildCorrelation >= MaxUnitMildlyCorrelated) { 592 | return false; 593 | } 594 | if(unitsStrongCorrelation >= MaxUnitStronglyCorrelated) { 595 | return false; 596 | } 597 | if(unitsDirection >= MaxUnitsPerDirection) { 598 | return false; 599 | } 600 | if(equityPair + equityPerTrade > MaxUnitsPerCurrencyPair * equityPerTrade) { 601 | return false; 602 | } 603 | if(equityMildCorrelation + equityPerTrade > MaxUnitMildlyCorrelated * equityPerTrade) { 604 | return false; 605 | } 606 | if(equityStrongCorrelation + equityPerTrade > MaxUnitStronglyCorrelated * equityPerTrade) { 607 | return false; 608 | } 609 | if(equityDirection + equityPerTrade > MaxUnitsPerDirection * equityPerTrade) { 610 | return false; 611 | } 612 | 613 | return true; 614 | } 615 | 616 | bool IsCorrelated(string curSymbol, string orderSymbol, eCorrelationType type) 617 | { 618 | for(int i=0; i= LevelError) { 648 | PrintFormat("Error: EntrySignalGiven: Invalid strategy type: %d", strategyType); 649 | } 650 | return DIR_NONE; 651 | } 652 | 653 | int maDirection = DirMABoth; 654 | if(UseMAFilter) { 655 | if(Bars < SlowMAPeriod) { 656 | return DIR_NONE; 657 | } 658 | double slowMA = iMA(NULL, 0, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1); 659 | double fastMA = iMA(NULL, 0, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE, 1); 660 | if(slowMA > fastMA) { 661 | maDirection = DirMASell; 662 | } else if(slowMA < fastMA) { 663 | maDirection = DirMABuy; 664 | } else { 665 | return DIR_NONE; 666 | } 667 | } 668 | 669 | if(maDirection == DirMABuy || maDirection == DirMABoth) { 670 | double maxVal = iHigh(Symbol(),Period(),iHighest(Symbol(),Period(),MODE_HIGH,period,1)); 671 | if(Close[0] > maxVal) { 672 | if(strategyType == StrategyTypeS1 && SkipS1AfterWinningTrade) { 673 | return PreviousS1TradeWasLosing(DIR_BUY, 0); 674 | } else { 675 | return DIR_BUY; 676 | } 677 | } 678 | } 679 | 680 | if(maDirection == DirMASell || maDirection == DirMABoth) { 681 | double maxVal = iLow(Symbol(),Period(),iLowest(Symbol(),Period(),MODE_LOW,period,1)); 682 | if(Close[0] < maxVal) { 683 | if(strategyType == StrategyTypeS1 && SkipS1AfterWinningTrade) { 684 | return PreviousS1TradeWasLosing(DIR_SELL, 0); 685 | } else { 686 | return DIR_SELL; 687 | } 688 | } 689 | } 690 | 691 | return DIR_NONE; 692 | } 693 | 694 | eTradeDirection PreviousS1TradeWasLosing(eTradeDirection direction, int offset) 695 | { 696 | for(int i=offset; i iHigh(Symbol(),Period(),iHighest(Symbol(),Period(),MODE_HIGH,PeriodS1Entry,i+1))) { 699 | for(int j=i+1; j= iLow(Symbol(),Period(),iLowest(Symbol(),Period(),MODE_LOW,PeriodS1Entry,j+1))) { 718 | eLosingS1Trade tradeState = CheckS1Loser(direction, j-1); 719 | if(tradeState == WasS1LoserYes) { 720 | return direction; 721 | } else if(tradeState == WasS1LoserNo) { 722 | return DIR_NONE; 723 | } else if(tradeState == WasS1LoserNext) { 724 | return PreviousS1TradeWasLosing(direction, j); 725 | } else { 726 | return DIR_NONE; 727 | } 728 | } 729 | } 730 | } 731 | } else { 732 | if(LogLevel >= LevelError) { 733 | PrintFormat( 734 | "PreviousS1TradeWasLosing error: Invalid direction: %d", 735 | direction 736 | ); 737 | } 738 | return DIR_NONE; 739 | } 740 | } 741 | 742 | return DIR_NONE; 743 | } 744 | 745 | eLosingS1Trade CheckS1Loser(eTradeDirection direction, int offset) 746 | { 747 | int lastPeriod = 1; 748 | bool foundExit = false; 749 | if(direction == DIR_BUY) { 750 | double openPrice = iHigh(Symbol(),Period(),iHighest(Symbol(),Period(),MODE_HIGH,PeriodS1Entry,offset)); 751 | 752 | double curATR = iATR(NULL, 0, PeriodATR, offset+1); 753 | if(curATR == 0.0) { 754 | if(LogLevel >= LevelError) { 755 | PrintFormat("CheckS1Loser error: ATR=%f", curATR); 756 | } 757 | return WasS1LoserNo; 758 | } 759 | double stopLoss = openPrice - StopSizeInN * curATR; 760 | 761 | for(int i=offset-1; i>0; i++) { 762 | if(Low[i] < iLow(Symbol(),Period(),iLowest(Symbol(),Period(),MODE_LOW,PeriodS1Exit,i+1))) { 763 | if(Low[i] < openPrice) { 764 | return WasS1LoserYes; 765 | } else { 766 | foundExit = true; 767 | lastPeriod = i; 768 | break; 769 | } 770 | } 771 | } 772 | for(int i=offset; i>=lastPeriod; i++) { 773 | if(Low[i] < stopLoss) { 774 | if(i == offset) { 775 | if(Close[i] > Open[i]) { 776 | continue; 777 | } else { 778 | return WasS1LoserYes; 779 | } 780 | } else { 781 | return WasS1LoserYes; 782 | } 783 | } 784 | } 785 | if(!foundExit) { 786 | return WasS1LoserNext; 787 | } else { 788 | return WasS1LoserNo; 789 | } 790 | } else if(direction == DIR_SELL) { 791 | double openPrice = iLow(Symbol(),Period(),iLowest(Symbol(),Period(),MODE_LOW,PeriodS1Entry,offset)); 792 | 793 | double curATR = iATR(NULL, 0, PeriodATR, offset+1); 794 | if(curATR == 0.0) { 795 | if(LogLevel >= LevelError) { 796 | PrintFormat("CheckS1Loser error: ATR=%f", curATR); 797 | } 798 | return WasS1LoserNo; 799 | } 800 | double stopLoss = openPrice + StopSizeInN * curATR; 801 | 802 | for(int i=offset-1; i>0; i++) { 803 | if(High[i] > iHigh(Symbol(),Period(),iHighest(Symbol(),Period(),MODE_HIGH,PeriodS1Exit,i+1))) { 804 | if(High[i] > openPrice) { 805 | return WasS1LoserYes; 806 | } else { 807 | foundExit = true; 808 | lastPeriod = i; 809 | break; 810 | } 811 | } 812 | } 813 | for(int i=offset; i>=lastPeriod; i++) { 814 | if(High[i] > stopLoss) { 815 | if(i == offset) { 816 | if(Close[i] < Open[i]) { 817 | continue; 818 | } else { 819 | return WasS1LoserYes; 820 | } 821 | } else { 822 | return WasS1LoserYes; 823 | } 824 | } 825 | } 826 | if(!foundExit) { 827 | return WasS1LoserNext; 828 | } else { 829 | return WasS1LoserNo; 830 | } 831 | } else { 832 | if(LogLevel >= LevelError) { 833 | PrintFormat( 834 | "CheckS1Loser error: Invalid direction: %d", 835 | direction 836 | ); 837 | } 838 | return WasS1LoserNo; 839 | } 840 | 841 | return WasS1LoserNo; 842 | } 843 | 844 | void CheckPyramiding(eStrategyType strategyType, sTradeStatus &status) 845 | { 846 | if(PyramidingSignalGiven(strategyType, status)) { 847 | int ticketNr = EnterPosition(strategyType, status); 848 | if(ticketNr > -1) { 849 | MoveStops(strategyType, status, ticketNr); 850 | } 851 | } 852 | } 853 | 854 | void MoveStops(eStrategyType strategyType, sTradeStatus &status, int ticketNr) 855 | { 856 | int magic; 857 | if(strategyType == StrategyTypeS1) { 858 | magic = MagicNumber_System1; 859 | } else if(strategyType == StrategyTypeS2) { 860 | magic = MagicNumber_System2; 861 | } else { 862 | if(LogLevel >= LevelError) { 863 | PrintFormat("MoveStops error: Invalid strategy type: %d", strategyType); 864 | } 865 | return; 866 | } 867 | 868 | if(status.direction != DIR_BUY && status.direction != DIR_SELL) { 869 | if(LogLevel >= LevelError) { 870 | PrintFormat("MoveStops error: Invalid direction: %d", status.direction); 871 | } 872 | return; 873 | } 874 | 875 | int periodOffset; 876 | if(status.openOffset == -2 ) { 877 | if(LogLevel >= LevelError) { 878 | Print("MoveStops error: openOffset=-2"); 879 | } 880 | return; 881 | } else if(status.openOffset < 1) { 882 | periodOffset = 1; 883 | } else { 884 | periodOffset = status.openOffset; 885 | } 886 | 887 | double orderATR = iATR(NULL, 0, PeriodATR, periodOffset); 888 | if(orderATR == 0.0) { 889 | if(LogLevel >= LevelError) { 890 | PrintFormat("MoveStops error: ATR=%f", orderATR); 891 | } 892 | return; 893 | } 894 | 895 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 896 | { 897 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 898 | 899 | if(OrderMagicNumber() == magic && 900 | OrderSymbol() == Symbol() && 901 | OrderTicket() != ticketNr) 902 | { 903 | double oldStopLevel = OrderStopLoss(); 904 | if(oldStopLevel == 0) { 905 | if(LogLevel >= LevelCritical) { 906 | PrintFormat( 907 | "MoveStops error: Order does not have SL!" 908 | ); 909 | } 910 | continue; 911 | } 912 | 913 | int type = OrderType(); 914 | if(type == OP_BUY || 915 | type == OP_BUYLIMIT || 916 | type == OP_BUYSTOP) 917 | { 918 | if(status.direction == DIR_SELL) { 919 | if(LogLevel >= LevelCritical) { 920 | PrintFormat( 921 | "Critical: MoveStops: Looking for Sell oders but found Buy order!" 922 | ); 923 | } 924 | return; 925 | } 926 | 927 | double newStopLevel = oldStopLevel + PyramidingThresholdN * orderATR; 928 | if(!OrderModify( 929 | OrderTicket(), 930 | OrderOpenPrice(), 931 | NormalizeDouble(newStopLevel, Digits), 932 | OrderTakeProfit(), 933 | 0, 934 | clrYellow) 935 | ) { 936 | if(LogLevel >= LevelError) { 937 | Print("MoveStops warning: error trailing SL ", ErrorDescription(GetLastError())); 938 | continue; 939 | } 940 | } 941 | } else if (type == OP_SELL || 942 | type == OP_SELLLIMIT || 943 | type == OP_SELLSTOP) 944 | { 945 | if(status.direction == DIR_BUY) { 946 | if(LogLevel >= LevelCritical) { 947 | PrintFormat( 948 | "Critical: MoveStops: Looking for Buy oders but found Sell order!" 949 | ); 950 | } 951 | return; 952 | } 953 | 954 | double newStopLevel = oldStopLevel - PyramidingThresholdN * orderATR; 955 | if(!OrderModify( 956 | OrderTicket(), 957 | OrderOpenPrice(), 958 | NormalizeDouble(newStopLevel, Digits), 959 | OrderTakeProfit(), 960 | 0, 961 | clrYellow) 962 | ) { 963 | if(LogLevel >= LevelError) { 964 | Print("MoveStops warning: error trailing SL ", ErrorDescription(GetLastError())); 965 | continue; 966 | } 967 | } 968 | } 969 | } 970 | } 971 | } 972 | 973 | bool PyramidingSignalGiven(eStrategyType strategyType, sTradeStatus &status) 974 | { 975 | double lastPrice = GetLastPrice(strategyType, status); 976 | if(lastPrice < 0) { 977 | if(LogLevel >= LevelError) { 978 | Print("PyramidingSignalGiven error: lastPrice=%f", 979 | lastPrice); 980 | } 981 | return false; 982 | } 983 | 984 | int periodOffset; 985 | if(status.openOffset == -2 ) { 986 | if(LogLevel >= LevelError) { 987 | Print("PyramidingSignalGiven error: openOffset=-2"); 988 | } 989 | return false; 990 | } else if(status.openOffset < 1) { 991 | periodOffset = 1; 992 | } else { 993 | periodOffset = status.openOffset; 994 | } 995 | 996 | if(periodOffset > MaxPyramidingTimeframe) { 997 | return false; 998 | } 999 | 1000 | double curATR = iATR(NULL, 0, PeriodATR, periodOffset); 1001 | if(curATR == 0.0) { 1002 | if(LogLevel >= LevelError) { 1003 | PrintFormat("PyramidingSignalGiven error: ATR=%f", curATR); 1004 | } 1005 | return false; 1006 | } 1007 | 1008 | if(status.direction == DIR_BUY) { 1009 | if(Ask >= lastPrice + (curATR * PyramidingThresholdN)) { 1010 | return true; 1011 | } 1012 | } else if(status.direction == DIR_SELL) { 1013 | if(Bid <= lastPrice - (curATR * PyramidingThresholdN)) { 1014 | return true; 1015 | } 1016 | } else { 1017 | if(LogLevel >= LevelError) { 1018 | PrintFormat("PyramidingSignalGiven error: direction=%d", 1019 | status.direction); 1020 | } 1021 | return false; 1022 | } 1023 | 1024 | return false; 1025 | } 1026 | 1027 | double GetLastPrice(eStrategyType strategyType, sTradeStatus &status) 1028 | { 1029 | int magic; 1030 | if(strategyType == StrategyTypeS1) { 1031 | magic = MagicNumber_System1; 1032 | } else if(strategyType == StrategyTypeS2) { 1033 | magic = MagicNumber_System2; 1034 | } else { 1035 | if(LogLevel >= LevelError) { 1036 | PrintFormat("GetLastPrice error: Invalid strategy type: %d", strategyType); 1037 | } 1038 | return -1.0; 1039 | } 1040 | 1041 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 1042 | { 1043 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 1044 | 1045 | if(OrderMagicNumber() == magic && 1046 | OrderSymbol() == Symbol()) 1047 | { 1048 | int type = OrderType(); 1049 | if(type == OP_BUY || 1050 | type == OP_BUYLIMIT || 1051 | type == OP_BUYSTOP) 1052 | { 1053 | if(status.direction == DIR_SELL) { 1054 | if(LogLevel >= LevelCritical) { 1055 | PrintFormat( 1056 | "Critical: GetLastPrice(%d): Looking for Sell oders but found Buy order!" 1057 | ); 1058 | } 1059 | return -1.0; 1060 | } 1061 | 1062 | datetime tmpOpenTime = OrderOpenTime(); 1063 | if(tmpOpenTime == status.lastTradeTime) { 1064 | return OrderOpenPrice(); 1065 | } 1066 | } else if (type == OP_SELL || 1067 | type == OP_SELLLIMIT || 1068 | type == OP_SELLSTOP) 1069 | { 1070 | if(status.direction == DIR_BUY) { 1071 | if(LogLevel >= LevelCritical) { 1072 | PrintFormat( 1073 | "Critical: GetLastPrice(%d): Looking for Buy oders but found Sell order!" 1074 | ); 1075 | } 1076 | return -1.0; 1077 | } 1078 | 1079 | datetime tmpOpenTime = OrderOpenTime(); 1080 | if(tmpOpenTime == status.lastTradeTime) { 1081 | return OrderOpenPrice(); 1082 | } 1083 | } 1084 | } 1085 | } 1086 | 1087 | return -1.0; 1088 | } 1089 | 1090 | int EnterPosition(eStrategyType strategyType, sTradeStatus &status) 1091 | { 1092 | int magic; 1093 | if(strategyType == StrategyTypeS1) { 1094 | magic = MagicNumber_System1; 1095 | } else if(strategyType == StrategyTypeS2) { 1096 | magic = MagicNumber_System2; 1097 | } else { 1098 | if(LogLevel >= LevelError) { 1099 | PrintFormat("Error: CurrentlyTrading: Invalid strategy type: %d", 1100 | strategyType); 1101 | } 1102 | return -1; 1103 | } 1104 | 1105 | double lotSize = GetLotSize(status); 1106 | if(lotSize == -1.0) { 1107 | if(LogLevel >= LevelError) { 1108 | Print("EnterPosition: error lotSize"); 1109 | } 1110 | return -1; 1111 | } 1112 | 1113 | double stopSize = GetStopSize(status); 1114 | if(stopSize == -1.0 || stopSize < 0) { 1115 | if(LogLevel >= LevelError) { 1116 | Print("EnterPosition: error stopSize"); 1117 | } 1118 | return -1; 1119 | } 1120 | 1121 | int ticketNr = -1; 1122 | if(status.direction == DIR_BUY) { 1123 | ticketNr = OrderSend( 1124 | Symbol(), 1125 | OP_BUY, 1126 | lotSize, 1127 | Ask, 1128 | ttnDefaultSlippage, 1129 | stopSize, 1130 | 0, 1131 | eaName, 1132 | magic, 1133 | 0, 1134 | clrBlue 1135 | ); 1136 | } else if(status.direction == DIR_SELL) { 1137 | ticketNr = OrderSend( 1138 | Symbol(), 1139 | OP_SELL, 1140 | lotSize, 1141 | Bid, 1142 | ttnDefaultSlippage, 1143 | stopSize, 1144 | 0, 1145 | eaName, 1146 | magic, 1147 | 0, 1148 | clrRed 1149 | ); 1150 | } else { 1151 | if(LogLevel >= LevelError) { 1152 | Print("EnterPosition: error direction=%d", status.direction); 1153 | } 1154 | return -1; 1155 | } 1156 | 1157 | if(ticketNr == -1) { 1158 | if(LogLevel >= LevelError) { 1159 | PrintFormat("EnterPosition: Error opening order: %s", 1160 | ErrorDescription(GetLastError())); 1161 | } 1162 | return -1; 1163 | } 1164 | 1165 | return ticketNr; 1166 | } 1167 | 1168 | double GetLotSize(sTradeStatus &status) 1169 | { 1170 | int periodOffset; 1171 | if(status.openOffset == -2 ) { 1172 | if(LogLevel >= LevelError) { 1173 | Print("GetLotSize error: openOffset=-2"); 1174 | } 1175 | return -1.0; 1176 | } else if(status.openOffset < 1) { 1177 | periodOffset = 1; 1178 | } else { 1179 | periodOffset = status.openOffset; 1180 | } 1181 | 1182 | double curATR = iATR(NULL, 0, PeriodATR, periodOffset) / Point; 1183 | if(curATR == 0.0) { 1184 | if(LogLevel >= LevelError) { 1185 | PrintFormat("GetLotSize error: ATR=%f", curATR); 1186 | } 1187 | return -1.0; 1188 | } 1189 | 1190 | double equityPerN = AccountBalance() *(EquityPctPerN / 100.0); 1191 | double equityPerTrade = equityPerN * UnitsPerTrade; 1192 | 1193 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 1194 | 1195 | double lotSize = equityPerTrade / (StopSizeInN * curATR * tickValue); 1196 | 1197 | if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) 1198 | { 1199 | lotSize = NormalizeDouble(lotSize,1); 1200 | } 1201 | else { 1202 | lotSize = NormalizeDouble(lotSize,2); 1203 | } 1204 | 1205 | if(lotSize < MarketInfo(Symbol(),MODE_MINLOT)) 1206 | { 1207 | if(LogLevel >= LevelError) { 1208 | PrintFormat("GetLotSize: lotSize=%f < MINLOT", lotSize); 1209 | } 1210 | return -1.0; 1211 | } else if(lotSize > MarketInfo(Symbol(),MODE_MAXLOT)) 1212 | { 1213 | lotSize = MarketInfo(Symbol(),MODE_MAXLOT); 1214 | if(LogLevel >= LevelWarning) { 1215 | PrintFormat("GetLotSize: lotSize=%f > MAXLOT=%f", 1216 | lotSize, MarketInfo(Symbol(),MODE_MAXLOT)); 1217 | } 1218 | } 1219 | 1220 | return lotSize; 1221 | } 1222 | 1223 | double GetStopSize(sTradeStatus &status) 1224 | { 1225 | int periodOffset; 1226 | if(status.openOffset == -2 ) { 1227 | if(LogLevel >= LevelError) { 1228 | Print("GetStopSize error: openOffset=-2"); 1229 | } 1230 | return -1.0; 1231 | } else if(status.openOffset < 1) { 1232 | periodOffset = 1; 1233 | } else { 1234 | periodOffset = status.openOffset; 1235 | } 1236 | 1237 | double curATR = iATR(NULL, 0, PeriodATR, periodOffset); 1238 | if(curATR == 0.0) { 1239 | if(LogLevel >= LevelError) { 1240 | PrintFormat("GetStopSize error: ATR=%f", curATR); 1241 | } 1242 | return -1.0; 1243 | } 1244 | 1245 | double stopSize; 1246 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 1247 | 1248 | if(status.direction == DIR_BUY) { 1249 | stopSize = Bid - (StopSizeInN * curATR); 1250 | if(stopSize > Ask - marketStopLevel) { 1251 | PrintFormat("SL(%f) > Ask(%f) - minSL(%f)", 1252 | stopSize, Ask, marketStopLevel); 1253 | return -1.0; 1254 | } 1255 | return stopSize; 1256 | } else if(status.direction == DIR_SELL) { 1257 | stopSize = Ask + (StopSizeInN * curATR); 1258 | if(stopSize < Bid + marketStopLevel) { 1259 | PrintFormat("SL(%f) < Bid(%f) + minSL(%f)", 1260 | stopSize, Bid, marketStopLevel); 1261 | return -1.0; 1262 | } 1263 | return stopSize; 1264 | } else { 1265 | if(LogLevel >= LevelError) { 1266 | PrintFormat("GetStopSize error: direction=%d", status.direction); 1267 | } 1268 | return -1.0; 1269 | } 1270 | 1271 | return -1.0; 1272 | } 1273 | -------------------------------------------------------------------------------- /Experts/ttnTrendRider.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnTrendRider.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | #property copyright "tickelton@gmail.com" 35 | #property link "https://github.com/tickelton" 36 | #property version "1.00" 37 | #property strict 38 | 39 | extern int MagicNumber = 5124; 40 | extern int LongTermMAPeriod = 288; 41 | extern int SlowMAPeriod = 14; 42 | extern int IntermediateMAPeriod = 6; 43 | extern int FastMAPeriod = 4; 44 | extern int MAlevelFlipPeriod = 4; 45 | extern int ADXperiod = 14; 46 | extern double MinADX = 25; 47 | extern double FactorReversalCandle = 1.5; 48 | extern double EquityPerTrade = 1.0; 49 | /* 50 | Log Levels: 51 | 0 = Critical 52 | 1 = Error 53 | 2 = Warning 54 | 3 = Info 55 | 4 = Debug 56 | */ 57 | extern int LogLevel = 0; 58 | 59 | enum TradeDirection { 60 | DirectionInit, 61 | DirectionBuy, 62 | DirectionSell, 63 | DirectionNoTrade, 64 | }; 65 | 66 | enum CandleType { 67 | CandleBearish, 68 | CandleBullish, 69 | }; 70 | 71 | int PositionOpen = 0; 72 | int CurrentTicket = 0; 73 | datetime CurrentTimestamp; 74 | TradeDirection direction = DirectionInit; 75 | int DefaultSlippage = 3; 76 | 77 | int OnInit() 78 | { 79 | CurrentTimestamp = Time[0]; 80 | 81 | return(INIT_SUCCEEDED); 82 | } 83 | 84 | void OnDeinit(const int reason) 85 | { 86 | 87 | 88 | } 89 | 90 | void OnTick() 91 | { 92 | bool NewBar = false; 93 | 94 | CheckEvents(MagicNumber); 95 | 96 | if(eventBuyClosed_SL > 0 || 97 | eventSellClosed_SL > 0 || 98 | eventBuyClosed_TP > 0 || 99 | eventSellClosed_TP > 0) { 100 | PositionOpen = 0; 101 | } 102 | 103 | if(CurrentTimestamp != Time[0]) { 104 | CurrentTimestamp = Time[0]; 105 | NewBar = true; 106 | } 107 | 108 | if(NewBar) { 109 | if(PositionOpen) { 110 | // In a position. 111 | if(direction == DirectionBuy) { 112 | double sellBelow = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 0); 113 | if(Close[1] < sellBelow) { 114 | bool closeRet = CloseCurrentOrder(); 115 | } else { 116 | // If we still are in a Position, check if we need to trail the SL. 117 | if(!TrailStopLossMAlow()) { 118 | if(LogLevel < 3) { 119 | Print("Warning: error trailing bull SL"); 120 | } 121 | } 122 | } 123 | } else if(direction == DirectionSell) { 124 | double sellAbove = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 0); 125 | if(Close[1] > sellAbove) { 126 | bool closeRet = CloseCurrentOrder(); 127 | } else { 128 | // If we still are in a Position, check if we need to trail the SL. 129 | if(!TrailStopLossMAlow()) { 130 | if(LogLevel < 3) { 131 | Print("Warning: error trailing bear SL"); 132 | } 133 | } 134 | } 135 | } 136 | } else { 137 | // Not in a position. 138 | if(checkMAlevels()) { 139 | if(direction == DirectionBuy || 140 | direction == DirectionSell) { 141 | if(checkSignalCandle()) { 142 | if(checkMACD()){ 143 | if(checkADX()) { 144 | if(PlaceOrder() != true) { 145 | if(LogLevel < 2) { 146 | Print("Error placing sell order"); 147 | } 148 | } 149 | } 150 | } 151 | } 152 | } 153 | } 154 | } 155 | } 156 | } 157 | 158 | bool checkMAlevels() 159 | { 160 | TradeDirection tmpDirection = DirectionNoTrade; 161 | double fastMAlevel = iMA(NULL, 0, FastMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 0); 162 | double intermediateMAlevel = iMA(NULL, 0, IntermediateMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 0); 163 | double slowMAlevel = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 0); 164 | 165 | if(fastMAlevel > intermediateMAlevel && 166 | intermediateMAlevel > slowMAlevel && 167 | fastMAlevel > slowMAlevel) { 168 | for(int i=1; i<=MAlevelFlipPeriod; i++) { 169 | double prevFastMAlevel = iMA(NULL, 0, FastMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, i); 170 | double prevIntermediateMAlevel = iMA(NULL, 0, IntermediateMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, i); 171 | double prevSlowMAlevel = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, i); 172 | if(prevFastMAlevel > prevIntermediateMAlevel && 173 | prevIntermediateMAlevel > prevSlowMAlevel && 174 | prevFastMAlevel > prevSlowMAlevel) { 175 | tmpDirection = DirectionNoTrade; 176 | break; 177 | } 178 | if(prevFastMAlevel < prevIntermediateMAlevel && 179 | prevIntermediateMAlevel < prevSlowMAlevel && 180 | prevFastMAlevel < prevSlowMAlevel) { 181 | tmpDirection = DirectionBuy; 182 | break; 183 | } 184 | } 185 | 186 | } else if(fastMAlevel < intermediateMAlevel && 187 | intermediateMAlevel < slowMAlevel && 188 | fastMAlevel < slowMAlevel) { 189 | for(int i=1; i<=MAlevelFlipPeriod; i++) { 190 | double prevFastMAlevel = iMA(NULL, 0, FastMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, i); 191 | double prevIntermediateMAlevel = iMA(NULL, 0, IntermediateMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, i); 192 | double prevSlowMAlevel = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, i); 193 | if(prevFastMAlevel < prevIntermediateMAlevel && 194 | prevIntermediateMAlevel < prevSlowMAlevel && 195 | prevFastMAlevel < prevSlowMAlevel) { 196 | tmpDirection = DirectionNoTrade; 197 | break; 198 | } 199 | if(prevFastMAlevel > prevIntermediateMAlevel && 200 | prevIntermediateMAlevel > prevSlowMAlevel && 201 | prevFastMAlevel > prevSlowMAlevel) { 202 | tmpDirection = DirectionSell; 203 | break; 204 | } 205 | } 206 | } 207 | 208 | if(tmpDirection == DirectionBuy || 209 | tmpDirection == DirectionSell) { 210 | direction = tmpDirection; 211 | return true; 212 | } 213 | 214 | direction = tmpDirection; 215 | return false; 216 | } 217 | 218 | bool checkSignalCandle() 219 | { 220 | double prevLongTermMAlevel = iMA(NULL, 0, LongTermMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 1); 221 | double curLongTermMAlevel = iMA(NULL, 0, LongTermMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 0); 222 | double slowMAlevel = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_TYPICAL, 0); 223 | double priceOpen = Open[1]; 224 | 225 | if(direction == DirectionBuy) { 226 | if(CandleIsDecisive(CandleBullish, 1)){ 227 | if(priceOpen > slowMAlevel && 228 | priceOpen > curLongTermMAlevel) { 229 | return true; 230 | } 231 | } 232 | } else if(direction == DirectionSell) { 233 | if(CandleIsDecisive(CandleBearish, 1)) { 234 | if(priceOpen < slowMAlevel && 235 | priceOpen < curLongTermMAlevel) { 236 | return true; 237 | } 238 | } 239 | } 240 | 241 | return false; 242 | } 243 | 244 | bool CandleIsDecisive(CandleType type, int idx) 245 | { 246 | if(type == CandleBearish) { 247 | if(Close[idx] > Open[idx]) 248 | return false; 249 | 250 | double candleHeight = Open[idx] - Close[idx]; 251 | 252 | if(Point == 0.0001 || Point == 0.00001){ 253 | if (candleHeight < 0.0001) { 254 | return false; 255 | } 256 | } else if(Point == 0.01 || Point == 0.001) { 257 | if (candleHeight < 0.01) { 258 | return false; 259 | } 260 | } 261 | double wickTop = High[idx] - Open[idx]; 262 | double wickBottom = Close[idx] - Low[idx]; 263 | 264 | if(wickTop > (candleHeight * FactorReversalCandle)) 265 | return false; 266 | if(wickBottom > (candleHeight * FactorReversalCandle)) 267 | return false; 268 | 269 | return true; 270 | } else if (type == CandleBullish) { 271 | if(Open[idx] > Close[idx]) 272 | return false; 273 | 274 | double candleHeight = Close[idx] - Open[idx]; 275 | if(Point == 0.0001 || Point == 0.00001){ 276 | if (candleHeight < 0.0001) { 277 | return false; 278 | } 279 | } else if(Point == 0.01 || Point == 0.001) { 280 | if (candleHeight < 0.01) { 281 | return false; 282 | } 283 | } 284 | 285 | double wickTop = High[idx] - Close[idx]; 286 | double wickBottom = Open[idx] - Low[idx]; 287 | 288 | if(wickTop > (candleHeight * FactorReversalCandle)) 289 | return false; 290 | if(wickBottom > (candleHeight * FactorReversalCandle)) 291 | return false; 292 | 293 | return true; 294 | } 295 | 296 | return false; 297 | } 298 | 299 | bool checkMACD() 300 | { 301 | double prevMACD = iMACD(NULL,0,IntermediateMAPeriod,SlowMAPeriod,FastMAPeriod,PRICE_CLOSE,MODE_MAIN,2); 302 | double nowMACD = iMACD(NULL,0,IntermediateMAPeriod,SlowMAPeriod,FastMAPeriod,PRICE_CLOSE,MODE_MAIN,1); 303 | double prevMACDsignal = iMACD(NULL,0,IntermediateMAPeriod,SlowMAPeriod,FastMAPeriod,PRICE_CLOSE,MODE_SIGNAL,2); 304 | double nowMACDsignal = iMACD(NULL,0,IntermediateMAPeriod,SlowMAPeriod,FastMAPeriod,PRICE_CLOSE,MODE_SIGNAL,1); 305 | 306 | if(direction == DirectionBuy) { 307 | if((prevMACD < 0 && nowMACD >= 0) || 308 | (prevMACD <= 0 && nowMACD > 0)) { 309 | return true; 310 | } 311 | } else if(direction == DirectionSell) { 312 | if((prevMACD > 0 && nowMACD <= 0) || 313 | (prevMACD >= 0 && nowMACD < 0)) { 314 | return true; 315 | } 316 | } 317 | 318 | return false; 319 | } 320 | 321 | bool checkADX() 322 | { 323 | double prevADX = iADX(NULL, 0, ADXperiod, PRICE_TYPICAL, MODE_MAIN, 2); 324 | double nowADX = iADX(NULL, 0, ADXperiod, PRICE_TYPICAL, MODE_MAIN, 1); 325 | 326 | if(nowADX >= MinADX) { 327 | return true; 328 | } 329 | 330 | return false; 331 | } 332 | 333 | bool TrailStopLossMAlow() 334 | { 335 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET)) { 336 | double orderStopLoss = OrderStopLoss(); 337 | double orderOpenPrice = OrderOpenPrice(); 338 | 339 | double newStopLoss = 0; 340 | if(direction == DirectionBuy) { 341 | double maLevel = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_LOW, 0); 342 | if(maLevel > orderStopLoss) 343 | newStopLoss = maLevel; 344 | } else if(direction == DirectionSell) { 345 | double maLevel = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_HIGH, 0); 346 | if(maLevel < orderStopLoss) 347 | newStopLoss = maLevel; 348 | } else { 349 | return false; 350 | } 351 | 352 | if(newStopLoss != 0) { 353 | if(direction == DirectionBuy) { 354 | if(newStopLoss > (orderOpenPrice + (Ask - Bid))) 355 | newStopLoss = orderOpenPrice + (Ask - Bid); 356 | } else if(direction == DirectionSell) { 357 | if(newStopLoss < (orderOpenPrice - (Bid - Ask))) 358 | newStopLoss = orderOpenPrice - (Bid - Ask); 359 | } else { 360 | return false; 361 | } 362 | 363 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 364 | 365 | if(direction == DirectionBuy) { 366 | if(newStopLoss > Bid - marketStopLevel) { 367 | if(Bid - marketStopLevel > orderStopLoss) { 368 | if(LogLevel < 4) { 369 | PrintFormat("%f > Bid(%f) - minSL(%f); setting new Sl to %f", 370 | newStopLoss, Bid, marketStopLevel, Bid - marketStopLevel); 371 | } 372 | newStopLoss = Bid - marketStopLevel; 373 | } 374 | } 375 | if(newStopLoss < orderStopLoss) { 376 | if(LogLevel < 4) { 377 | Print("new SL would be < current SL; aborting"); 378 | } 379 | return false; 380 | } 381 | } else if(direction == DirectionSell) { 382 | if(newStopLoss < Ask + marketStopLevel) { 383 | if(Ask + marketStopLevel < orderStopLoss) { 384 | if(LogLevel < 4) { 385 | PrintFormat("%f < Ask(%f) + minSL(%f); setting new Sl to %f", 386 | newStopLoss, Ask, marketStopLevel, Ask + marketStopLevel); 387 | } 388 | newStopLoss = Ask + marketStopLevel; 389 | } 390 | } 391 | if(newStopLoss > orderStopLoss) { 392 | if(LogLevel < 4) { 393 | Print("new SL would be > current SL; aborting"); 394 | } 395 | return false; 396 | } 397 | } 398 | 399 | if(!OrderModify(CurrentTicket, OrderOpenPrice(), NormalizeDouble(newStopLoss, Digits), OrderTakeProfit(), 0, clrYellow)) { 400 | if(LogLevel < 3) { 401 | Print("Warning: error trailing SL ", GetLastError()); 402 | } 403 | return false; 404 | } 405 | } 406 | } else { 407 | if(LogLevel < 3) { 408 | Print("Warning: Cannot select order: ",GetLastError()); 409 | } 410 | return false; 411 | } 412 | 413 | return true; 414 | } 415 | 416 | bool CloseCurrentOrder() 417 | { 418 | if(CurrentTicket == 0) { 419 | if(LogLevel <= 0) { 420 | Print("Fatal: cannot close; no current order"); 421 | } 422 | } 423 | 424 | if(OrderSelect(CurrentTicket, SELECT_BY_TICKET) != true) { 425 | if(LogLevel <= 0) { 426 | Print("Fatal: cannot select current order"); 427 | } 428 | } 429 | 430 | bool closeRet = false; 431 | if(direction == DirectionBuy) { 432 | closeRet = OrderClose(CurrentTicket, OrderLots(), Bid, DefaultSlippage, clrRed); 433 | } else if(direction == DirectionSell) { 434 | closeRet = OrderClose(CurrentTicket, OrderLots(), Ask, DefaultSlippage, clrBlue); 435 | } 436 | 437 | if(closeRet != true) { 438 | if(LogLevel <= 0) { 439 | Print("Fatal: cannot close current order"); 440 | } 441 | return false; 442 | } 443 | 444 | PositionOpen = 0; 445 | CurrentTicket = 0; 446 | 447 | return true; 448 | } 449 | 450 | bool PlaceOrder() 451 | { 452 | double stopLevel = -1; 453 | if(direction == DirectionBuy) { 454 | stopLevel = getStopLevel(Ask); 455 | } else if(direction == DirectionSell) { 456 | stopLevel = getStopLevel(Bid); 457 | } 458 | 459 | if(stopLevel < 0) 460 | return false; 461 | 462 | double stopSize; 463 | if(direction == DirectionBuy) { 464 | stopSize = (Ask - stopLevel) / Point; 465 | if(Point == 0.001 || Point == 0.00001) stopSize /= 10; 466 | } else if(direction == DirectionSell) { 467 | stopSize = (stopLevel - Bid) / Point; 468 | if(Point == 0.001 || Point == 0.00001) stopSize /= 10; 469 | } else { 470 | return false; 471 | } 472 | double lotSize = getLotSize(stopSize); 473 | if(lotSize < 0) 474 | return false; 475 | 476 | int ticketNr = -1; 477 | if(direction == DirectionBuy) { 478 | ticketNr = OrderSend(Symbol(), OP_BUY, lotSize, Ask, DefaultSlippage, stopLevel, 0, "ttnTrendRider Long", MagicNumber, 0, clrBlue); 479 | } else if(direction == DirectionSell) { 480 | ticketNr = OrderSend(Symbol(), OP_SELL, lotSize, Bid, DefaultSlippage, stopLevel, 0, "ttnTrendRider Short", MagicNumber, 0, clrRed); 481 | } else { 482 | return false; 483 | } 484 | 485 | if(ticketNr != -1) { 486 | PositionOpen = 1; 487 | CurrentTicket = ticketNr; 488 | if(LogLevel < 5) { 489 | PrintFormat("TRADE: dir=%d stop=%f lotsize=%f ticket=%d", direction, stopLevel, lotSize, ticketNr); 490 | } 491 | } 492 | 493 | CheckEvents(MagicNumber); 494 | if(eventBuyClosed_SL > 0 || 495 | eventSellClosed_SL > 0 || 496 | eventBuyClosed_TP > 0 || 497 | eventSellClosed_TP > 0) { 498 | PositionOpen = 0; 499 | } 500 | 501 | return true; 502 | } 503 | 504 | double getStopLevel(double currentPrice) 505 | { 506 | double level; 507 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 508 | 509 | if(direction == DirectionBuy) { 510 | level = iMA(NULL, 0, SlowMAPeriod, 1, MODE_EMA, PRICE_LOW, 0); 511 | if(level > Bid - marketStopLevel) { 512 | if(LogLevel < 4) { 513 | PrintFormat("SL(%f) > Bid(%f) - minSL(%f)", level, Bid, marketStopLevel); 514 | } 515 | return Bid - marketStopLevel; 516 | } 517 | } else if (direction == DirectionSell) { 518 | level = iMA(NULL, 0, FastMAPeriod, 1, MODE_EMA, PRICE_HIGH, 0); 519 | if(level < Ask + marketStopLevel) { 520 | if(LogLevel < 4) { 521 | PrintFormat("SL(%f) < Ask(%f) + minSL(%f)", level, Ask, marketStopLevel); 522 | } 523 | return Ask + marketStopLevel; 524 | } 525 | } else { 526 | return -1.0; 527 | } 528 | 529 | return level; 530 | } 531 | 532 | double getLotSize(double stopSize) 533 | { 534 | double riskAmount = AccountEquity() * (EquityPerTrade / 100); 535 | 536 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 537 | if(Point == 0.001 || Point == 0.00001) tickValue *= 10; 538 | 539 | double lotSize = (riskAmount / stopSize) / tickValue; 540 | 541 | if(lotSize < MarketInfo(Symbol(),MODE_MINLOT)) 542 | { 543 | return -1.0; 544 | } else if(lotSize > MarketInfo(Symbol(),MODE_MAXLOT)) 545 | { 546 | lotSize = MarketInfo(Symbol(),MODE_MAXLOT); 547 | } 548 | 549 | if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) 550 | { 551 | lotSize = NormalizeDouble(lotSize,1); 552 | } 553 | else { 554 | lotSize = NormalizeDouble(lotSize,2); 555 | } 556 | 557 | return lotSize; 558 | } 559 | -------------------------------------------------------------------------------- /Experts/ttnTrueSymphonie.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnTrueSymphonie.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #property copyright "tickelton@gmail.com" 36 | #property link "https://github.com/tickelton" 37 | #property version "1.00" 38 | #property strict 39 | 40 | extern int MagicNumber = ttnMagicNumberTrueSymphonie; 41 | extern int MAPeriodSlow = 72; 42 | extern int MAPeriodFast = 12; 43 | extern ENUM_MA_METHOD MAMethodSlow = MODE_SMA; 44 | extern ENUM_MA_METHOD MAMethodFast = MODE_EMA; 45 | extern double SlowATRThreshold = 1.0; 46 | extern double FastATRThreshold = 2.0; 47 | extern int ATRPeriod = 14; 48 | extern int ValidityTimeout = 4; 49 | extern double EquityPctPerN = 1.0; 50 | extern int UnitsPerTrade = 1; 51 | extern int MaxUnitsPerCurrencyPair = 4; 52 | extern int MaxUnitStronglyCorrelated = 5; 53 | extern int MaxUnitMildlyCorrelated = 7; 54 | extern int MaxUnitsPerDirection = 10; 55 | extern eLogLevel LogLevel = LevelError; 56 | 57 | struct sOrderStatus { 58 | int ticketNr; 59 | double risk; 60 | double openPrice; 61 | eTradeDirection direction; 62 | }; 63 | 64 | struct sTradeStatus { 65 | int openOffset; 66 | datetime lastTradeTime; 67 | eTradeDirection direction; 68 | }; 69 | 70 | const string eaName = "ttnTrueSymphonie"; 71 | datetime CurrentTimestamp; 72 | sOrderStatus OrderStatus; 73 | 74 | 75 | int OnInit() 76 | { 77 | CurrentTimestamp = Time[0]; 78 | OrderStatus.ticketNr = 0; 79 | OrderStatus.openPrice = 0; 80 | OrderStatus.risk = 0; 81 | OrderStatus.direction = DIR_NONE; 82 | 83 | return(INIT_SUCCEEDED); 84 | } 85 | 86 | void OnDeinit(const int reason) 87 | { 88 | 89 | 90 | } 91 | 92 | void OnTick() 93 | { 94 | bool NewBar = false; 95 | 96 | if(CurrentTimestamp != Time[0]) { 97 | CurrentTimestamp = Time[0]; 98 | NewBar = true; 99 | } 100 | 101 | if(NewBar) { 102 | CheckEntry(); 103 | } 104 | 105 | CheckStopLevel(); 106 | CheckExit(); 107 | } 108 | 109 | void CheckEntry() 110 | { 111 | sTradeStatus tradeStatus = CurrentlyTrading(); 112 | if(tradeStatus.openOffset > -1) { 113 | /* 114 | if(GotFreeVolume(tradeStatus)) { 115 | CheckPyramiding(strategyType, tradeStatus); 116 | } 117 | */ 118 | } else if(tradeStatus.openOffset == -1) { 119 | eTradeDirection tmpDir = EntrySignalGiven(); 120 | if(tmpDir != DIR_NONE) { 121 | tradeStatus.direction = tmpDir; 122 | if(GotFreeVolume(tradeStatus)) { 123 | if(EnterPosition(tradeStatus) == -1) { 124 | if(LogLevel >= LevelError) { 125 | Print("CheckEntry: Error opening position."); 126 | } 127 | } 128 | } 129 | } 130 | } else { 131 | if(LogLevel >= LevelError) { 132 | Print("CheckEntry: Unable to determine trading status."); 133 | } 134 | } 135 | } 136 | 137 | sTradeStatus CurrentlyTrading() 138 | { 139 | sTradeStatus tradeStatus; 140 | tradeStatus.openOffset = -2; 141 | tradeStatus.direction = DIR_NONE; 142 | 143 | bool gotBullOrders = false; 144 | bool gotBearOrders = false; 145 | 146 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 147 | { 148 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 149 | 150 | if(OrderMagicNumber() == MagicNumber && 151 | OrderSymbol() == Symbol()) 152 | { 153 | int type = OrderType(); 154 | if(type == OP_BUY || 155 | type == OP_BUYLIMIT || 156 | type == OP_BUYSTOP) 157 | { 158 | gotBullOrders = true; 159 | } else if (type == OP_SELL || 160 | type == OP_SELLLIMIT || 161 | type == OP_SELLSTOP) 162 | { 163 | gotBearOrders = true; 164 | } 165 | } 166 | } 167 | 168 | if(!gotBearOrders && !gotBullOrders) { 169 | tradeStatus.openOffset = -1; 170 | } else if(gotBearOrders && gotBullOrders) { 171 | if(LogLevel >= LevelCritical) { 172 | Print( 173 | "Critical: CurrentlyTrading(): Open orders in both directions!" 174 | ); 175 | } 176 | tradeStatus.openOffset = -2; 177 | } else if(gotBearOrders && !gotBullOrders) { 178 | tradeStatus.direction = DIR_SELL; 179 | GetOrderStatus(tradeStatus); 180 | } else if(!gotBearOrders && gotBullOrders) { 181 | tradeStatus.direction = DIR_BUY; 182 | GetOrderStatus(tradeStatus); 183 | } else { 184 | if(LogLevel >= LevelCritical) { 185 | Print( 186 | "Critical: CurrentlyTrading(%d): This should not happen!" 187 | ); 188 | } 189 | tradeStatus.openOffset = -2; 190 | } 191 | 192 | return tradeStatus; 193 | } 194 | 195 | void GetOrderStatus(sTradeStatus &status) 196 | { 197 | if(status.direction != DIR_BUY && status.direction != DIR_SELL) { 198 | if(LogLevel >= LevelError) { 199 | PrintFormat("Error: GetOpenOffset: Invalid direction: %d", status.direction); 200 | } 201 | status.openOffset = -2; 202 | return; 203 | } 204 | 205 | datetime openTime = D'01.01.1970'; 206 | datetime lastTradeTime = D'01.01.1970'; 207 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 208 | { 209 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 210 | 211 | if(OrderMagicNumber() == MagicNumber && 212 | OrderSymbol() == Symbol()) 213 | { 214 | int type = OrderType(); 215 | if(type == OP_BUY || 216 | type == OP_BUYLIMIT || 217 | type == OP_BUYSTOP) 218 | { 219 | if(status.direction == DIR_SELL) { 220 | if(LogLevel >= LevelCritical) { 221 | Print( 222 | "Critical: GetOpenOffset(): Looking for Sell oders but found Buy order!" 223 | ); 224 | } 225 | status.openOffset = -2; 226 | return; 227 | } 228 | if(openTime == D'01.01.1970') { 229 | openTime = OrderOpenTime(); 230 | } else { 231 | datetime tmpTime = OrderOpenTime(); 232 | if(tmpTime < openTime) { 233 | openTime = tmpTime; 234 | } 235 | } 236 | if(lastTradeTime == D'01.01.1970') { 237 | lastTradeTime = OrderOpenTime(); 238 | } else { 239 | datetime tmpTime = OrderOpenTime(); 240 | if(tmpTime > lastTradeTime) { 241 | lastTradeTime = tmpTime; 242 | } 243 | } 244 | } else if (type == OP_SELL || 245 | type == OP_SELLLIMIT || 246 | type == OP_SELLSTOP) 247 | { 248 | if(status.direction == DIR_BUY) { 249 | if(LogLevel >= LevelCritical) { 250 | Print( 251 | "Critical: GetOpenOffset(): Looking for Buy oders but found Sell order!" 252 | ); 253 | } 254 | status.openOffset = -2; 255 | return; 256 | } 257 | if(openTime == D'01.01.1970') { 258 | openTime = OrderOpenTime(); 259 | } else { 260 | datetime tmpTime = OrderOpenTime(); 261 | if(tmpTime < openTime) { 262 | openTime = tmpTime; 263 | } 264 | } 265 | if(lastTradeTime == D'01.01.1970') { 266 | lastTradeTime = OrderOpenTime(); 267 | } else { 268 | datetime tmpTime = OrderOpenTime(); 269 | if(tmpTime > lastTradeTime) { 270 | lastTradeTime = tmpTime; 271 | } 272 | } 273 | } 274 | } 275 | } 276 | 277 | if(openTime != D'01.01.1970' && 278 | lastTradeTime != D'01.01.1970') { 279 | status.lastTradeTime = lastTradeTime; 280 | status.openOffset = GetPeriodOffset(openTime); 281 | } else { 282 | status.openOffset = -2; 283 | } 284 | 285 | return; 286 | } 287 | 288 | int GetPeriodOffset(datetime openTime) 289 | { 290 | datetime curTime = TimeCurrent(); 291 | int timeDiff = (int)(curTime - openTime); 292 | 293 | if(timeDiff < 0) { 294 | if(LogLevel >= LevelError) { 295 | PrintFormat("Error: GetPeriodOffset: Negative timeDiff: %d", timeDiff); 296 | } 297 | return -2; 298 | } 299 | 300 | int tfCurrent = Period(); 301 | if(tfCurrent == PERIOD_M1) { 302 | return timeDiff / 60; 303 | } else if(tfCurrent == PERIOD_M5) { 304 | return timeDiff / 300; 305 | } else if(tfCurrent == PERIOD_M15) { 306 | return timeDiff / 900; 307 | } else if(tfCurrent == PERIOD_M30) { 308 | return timeDiff / 1800; 309 | } else if(tfCurrent == PERIOD_H1) { 310 | return timeDiff / 3600; 311 | } else if(tfCurrent == PERIOD_H4) { 312 | return timeDiff / 14400; 313 | } else if(tfCurrent == PERIOD_D1) { 314 | return timeDiff / 86400; 315 | } else { 316 | PrintFormat("Timeframe %d not supported", tfCurrent); 317 | return -2; 318 | } 319 | 320 | return -2; 321 | } 322 | 323 | eTradeDirection EntrySignalGiven() 324 | { 325 | double tmpATR = iATR(NULL, 0, ATRPeriod, 1); 326 | double tmpSlowMA = iMA(NULL, 0, MAPeriodSlow, 0, MAMethodSlow, PRICE_TYPICAL, 1); 327 | double lowSlow = tmpSlowMA - (tmpATR * FastATRThreshold); 328 | double lowFast = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_LOW, 1); 329 | double highSlow = tmpSlowMA + (tmpATR * FastATRThreshold); 330 | double highFast = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_HIGH, 1); 331 | 332 | if(Open[1] > highSlow && Open[1] > highFast && 333 | Close[1] > Open[1]) { 334 | double prevATR = iATR(NULL, 0, ATRPeriod, 2); 335 | double prevSlowMA = iMA(NULL, 0, MAPeriodSlow, 0, MAMethodSlow, PRICE_TYPICAL, 2); 336 | double prevHighSlow = prevSlowMA + (prevATR * FastATRThreshold); 337 | double prevHighFast = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_HIGH, 2); 338 | if(Close[2] > prevHighFast && Close[2] > prevHighSlow) { 339 | return DIR_BUY; 340 | } 341 | } else if (Open[1] < lowSlow && Open[1] < lowFast && 342 | Close[1] < Open[1]) { 343 | double prevATR = iATR(NULL, 0, ATRPeriod, 2); 344 | double prevSlowMA = iMA(NULL, 0, MAPeriodSlow, 0, MAMethodSlow, PRICE_TYPICAL, 2); 345 | double prevLowSlow = prevSlowMA - (prevATR * FastATRThreshold); 346 | double prevLowFast = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_LOW, 2); 347 | if(Close[2] < prevLowFast && Close[2] < prevLowSlow) { 348 | return DIR_SELL; 349 | } 350 | } 351 | 352 | return DIR_NONE; 353 | } 354 | 355 | bool GotFreeVolume(sTradeStatus &tradeStatus) 356 | { 357 | double equityPerN = AccountBalance() *(EquityPctPerN / 100.0); 358 | 359 | double equityPerTrade = equityPerN * UnitsPerTrade; 360 | 361 | double equityPair = 0; 362 | double equityMildCorrelation = 0; 363 | double equityStrongCorrelation = 0; 364 | double equityDirection = 0; 365 | int unitsPair = 0; 366 | int unitsMildCorrelation = 0; 367 | int unitsStrongCorrelation = 0; 368 | int unitsDirection = 0; 369 | 370 | for(int pos=OrdersTotal()-1; pos>=0; pos--) 371 | { 372 | if(OrderSelect(pos,SELECT_BY_POS)==false) continue; 373 | 374 | double tmpRisk = 0; 375 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 376 | int orderMagic = OrderMagicNumber(); 377 | 378 | int type = OrderType(); 379 | if(type == OP_BUY) 380 | { 381 | tmpRisk = ((OrderOpenPrice() - OrderStopLoss()) * OrderLots() * tickValue) / Point; 382 | if(tradeStatus.direction == DIR_BUY) { 383 | equityDirection += tmpRisk; 384 | if(orderMagic == MagicNumber) { 385 | unitsDirection++; 386 | } 387 | } 388 | } else if (type == OP_SELL) 389 | { 390 | tmpRisk = ((OrderStopLoss() - OrderOpenPrice()) * OrderLots() * tickValue) / Point; 391 | if(tradeStatus.direction == DIR_SELL) { 392 | equityDirection += tmpRisk; 393 | if(orderMagic == MagicNumber) { 394 | unitsDirection++; 395 | } 396 | } 397 | } 398 | 399 | if(!StringCompare(Symbol(), OrderSymbol())) { 400 | equityPair += tmpRisk; 401 | if(orderMagic == MagicNumber) { 402 | unitsPair++; 403 | } 404 | } 405 | if(IsCorrelated(Symbol(), OrderSymbol(), CORRELATION_MILD)) { 406 | equityMildCorrelation += tmpRisk; 407 | if(orderMagic == MagicNumber) { 408 | unitsMildCorrelation++; 409 | } 410 | } 411 | if(IsCorrelated(Symbol(), OrderSymbol(), CORRELATION_STRONG)) { 412 | equityStrongCorrelation += tmpRisk; 413 | if(orderMagic == MagicNumber) { 414 | unitsStrongCorrelation++; 415 | } 416 | } 417 | 418 | } 419 | 420 | if(unitsPair >= MaxUnitsPerCurrencyPair) { 421 | return false; 422 | } 423 | if(unitsMildCorrelation >= MaxUnitMildlyCorrelated) { 424 | return false; 425 | } 426 | if(unitsStrongCorrelation >= MaxUnitStronglyCorrelated) { 427 | return false; 428 | } 429 | if(unitsDirection >= MaxUnitsPerDirection) { 430 | return false; 431 | } 432 | if(equityPair + equityPerTrade > MaxUnitsPerCurrencyPair * equityPerTrade) { 433 | return false; 434 | } 435 | if(equityMildCorrelation + equityPerTrade > MaxUnitMildlyCorrelated * equityPerTrade) { 436 | return false; 437 | } 438 | if(equityStrongCorrelation + equityPerTrade > MaxUnitStronglyCorrelated * equityPerTrade) { 439 | return false; 440 | } 441 | if(equityDirection + equityPerTrade > MaxUnitsPerDirection * equityPerTrade) { 442 | return false; 443 | } 444 | 445 | return true; 446 | } 447 | 448 | bool IsCorrelated(string curSymbol, string orderSymbol, eCorrelationType type) 449 | { 450 | for(int i=0; i= LevelError) { 476 | Print("EnterPosition: error stopLevel"); 477 | } 478 | return -1; 479 | } 480 | 481 | 482 | int ticketNr = -1; 483 | double stopSize; 484 | if(status.direction == DIR_BUY) { 485 | stopSize = (Ask - stopLevel) / Point; 486 | double lotSize = GetLotSize(stopSize, status); 487 | if(lotSize == -1.0) { 488 | if(LogLevel >= LevelError) { 489 | Print("EnterPosition: error lotSize"); 490 | } 491 | return -1; 492 | } 493 | 494 | ticketNr = OrderSend( 495 | Symbol(), 496 | OP_BUY, 497 | lotSize, 498 | Ask, 499 | ttnDefaultSlippage, 500 | stopLevel, 501 | 0, 502 | eaName, 503 | MagicNumber, 504 | 0, 505 | clrBlue 506 | ); 507 | } else if(status.direction == DIR_SELL) { 508 | stopLevel += (Ask - Bid); 509 | stopSize = (stopLevel - Bid) / Point; 510 | double lotSize = GetLotSize(stopSize, status); 511 | if(lotSize == -1.0) { 512 | if(LogLevel >= LevelError) { 513 | Print("EnterPosition: error lotSize"); 514 | } 515 | return -1; 516 | } 517 | 518 | ticketNr = OrderSend( 519 | Symbol(), 520 | OP_SELL, 521 | lotSize, 522 | Bid, 523 | ttnDefaultSlippage, 524 | stopLevel, 525 | 0, 526 | eaName, 527 | MagicNumber, 528 | 0, 529 | clrRed 530 | ); 531 | } else { 532 | if(LogLevel >= LevelError) { 533 | Print("EnterPosition: error direction=%d", status.direction); 534 | } 535 | return -1; 536 | } 537 | 538 | if(ticketNr == -1) { 539 | if(LogLevel >= LevelError) { 540 | PrintFormat("EnterPosition: Error opening order: %s", 541 | ErrorDescription(GetLastError())); 542 | } 543 | return -1; 544 | } 545 | 546 | if(OrderSelect(ticketNr, SELECT_BY_TICKET) == false) { 547 | OrderStatus.ticketNr = 0; 548 | OrderStatus.openPrice = 0; 549 | OrderStatus.risk = 0; 550 | OrderStatus.direction = DIR_NONE; 551 | } else { 552 | OrderStatus.ticketNr = ticketNr; 553 | OrderStatus.openPrice = OrderOpenPrice(); 554 | if(status.direction == DIR_BUY) { 555 | OrderStatus.risk = OrderStatus.openPrice - OrderStopLoss(); 556 | OrderStatus.direction = DIR_BUY; 557 | } else if(status.direction == DIR_SELL) { 558 | OrderStatus.risk = OrderStopLoss() - OrderStatus.openPrice; 559 | OrderStatus.direction = DIR_SELL; 560 | } 561 | } 562 | 563 | return ticketNr; 564 | } 565 | 566 | double GetStopLevel(sTradeStatus &status) 567 | { 568 | int periodOffset; 569 | if(status.openOffset == -2 ) { 570 | if(LogLevel >= LevelError) { 571 | Print("GetStopSize error: openOffset=-2"); 572 | } 573 | return -1.0; 574 | } else if(status.openOffset < 1) { 575 | periodOffset = 1; 576 | } else { 577 | periodOffset = status.openOffset; 578 | } 579 | 580 | double stopSize; 581 | double stopLevel; 582 | double marketStopLevel = MarketInfo(Symbol(),MODE_STOPLEVEL) * Point; 583 | 584 | if(status.direction == DIR_BUY) { 585 | stopLevel = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_HIGH, periodOffset); 586 | stopSize = (Bid - stopLevel); 587 | if(stopSize < marketStopLevel) { 588 | PrintFormat("SL(%f) < minSL(%f)", 589 | stopSize, marketStopLevel); 590 | return -1.0; 591 | } 592 | return stopLevel; 593 | } else if(status.direction == DIR_SELL) { 594 | stopLevel = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_LOW, periodOffset); 595 | stopSize = (stopLevel - Ask); 596 | if(stopSize < marketStopLevel) { 597 | PrintFormat("SL(%f) < minSL(%f)", 598 | stopSize, marketStopLevel); 599 | return -1.0; 600 | } 601 | return stopLevel; 602 | } else { 603 | if(LogLevel >= LevelError) { 604 | PrintFormat("GetStopSize error: direction=%d", status.direction); 605 | } 606 | return -1.0; 607 | } 608 | 609 | return -1.0; 610 | } 611 | 612 | double GetLotSize(double stopSize, sTradeStatus &status) 613 | { 614 | double equityPerN = AccountBalance() *(EquityPctPerN / 100.0); 615 | double equityPerTrade = equityPerN * UnitsPerTrade; 616 | 617 | double tickValue = MarketInfo(Symbol(),MODE_TICKVALUE); 618 | 619 | double lotSize = equityPerTrade / (stopSize * tickValue); 620 | 621 | if(MarketInfo(Symbol(),MODE_LOTSTEP) == 0.1) 622 | { 623 | lotSize = NormalizeDouble(lotSize,1); 624 | } 625 | else { 626 | lotSize = NormalizeDouble(lotSize,2); 627 | } 628 | 629 | if(lotSize < MarketInfo(Symbol(),MODE_MINLOT)) 630 | { 631 | if(LogLevel >= LevelError) { 632 | PrintFormat("GetLotSize: lotSize=%f < MINLOT", lotSize); 633 | } 634 | return -1.0; 635 | } else if(lotSize > MarketInfo(Symbol(),MODE_MAXLOT)) 636 | { 637 | lotSize = MarketInfo(Symbol(),MODE_MAXLOT); 638 | if(LogLevel >= LevelWarning) { 639 | PrintFormat("GetLotSize: lotSize=%f > MAXLOT=%f", 640 | lotSize, MarketInfo(Symbol(),MODE_MAXLOT)); 641 | } 642 | } 643 | 644 | return lotSize; 645 | } 646 | 647 | void CheckStopLevel() 648 | { 649 | sTradeStatus tradeStatus = CurrentlyTrading(); 650 | if(tradeStatus.openOffset <= -1) { 651 | return; 652 | } 653 | 654 | if(OrderStatus.ticketNr == 0) { 655 | return; 656 | } 657 | 658 | double newSL = 0.0; 659 | if(OrderStatus.direction == DIR_BUY) { 660 | if(Bid >= OrderStatus.openPrice + (OrderStatus.risk * 2.5)) { 661 | newSL = OrderStatus.openPrice + ((Bid - OrderStatus.openPrice) * 0.66); 662 | } else if(Bid >= OrderStatus.openPrice + (OrderStatus.risk * 1.5)) { 663 | newSL = OrderStatus.openPrice + ((Bid - OrderStatus.openPrice) * 0.4); 664 | } else if(Bid >= OrderStatus.openPrice + (OrderStatus.risk * 0.5)) { 665 | newSL = OrderStatus.openPrice; 666 | } 667 | } else if(OrderStatus.direction == DIR_SELL) { 668 | if(Bid <= OrderStatus.openPrice - (OrderStatus.risk * 1.5)) { 669 | newSL = OrderStatus.openPrice - ((OrderStatus.openPrice - Bid) * 0.66); 670 | } else if(Bid <= OrderStatus.openPrice - (OrderStatus.risk * 1.5)) { 671 | newSL = OrderStatus.openPrice - ((OrderStatus.openPrice - Bid) * 0.4); 672 | } else if(Bid <= OrderStatus.openPrice - (OrderStatus.risk * 0.5)) { 673 | newSL = OrderStatus.openPrice; 674 | } 675 | } 676 | 677 | newSL = NormalizeDouble(newSL, Digits); 678 | if(newSL > 0.0) { 679 | if(OrderSelect(OrderStatus.ticketNr, SELECT_BY_TICKET) == false) { 680 | Print("CheckStopLevel Error: error selecting order ", ErrorDescription(GetLastError())); 681 | return; 682 | } 683 | if(OrderStatus.direction == DIR_BUY && 684 | newSL > OrderStopLoss()) { 685 | if(!OrderModify(OrderStatus.ticketNr, OrderOpenPrice(), NormalizeDouble(newSL, Digits), OrderTakeProfit(), 0, clrYellow)) { 686 | Print("CheckStopLevel Error: error trailing SL ", ErrorDescription(GetLastError())); 687 | return; 688 | } 689 | } else if(OrderStatus.direction == DIR_SELL && 690 | newSL < OrderStopLoss()) { 691 | if(!OrderModify(OrderStatus.ticketNr, OrderOpenPrice(), NormalizeDouble(newSL, Digits), OrderTakeProfit(), 0, clrYellow)) { 692 | Print("CheckStopLevel Error: error trailing SL ", ErrorDescription(GetLastError())); 693 | return; 694 | } 695 | } 696 | } 697 | 698 | return; 699 | } 700 | 701 | void InitOrderStatus() 702 | { 703 | 704 | } 705 | 706 | void CheckExit() 707 | { 708 | sTradeStatus tradeStatus = CurrentlyTrading(); 709 | if(tradeStatus.openOffset <= -1) { 710 | return; 711 | } 712 | 713 | if(OrderStatus.direction == DIR_BUY) { 714 | double tmpATR = iATR(NULL, 0, ATRPeriod, 1); 715 | double tmpMA = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_TYPICAL, 1); 716 | double stopVal = tmpMA - (tmpATR * SlowATRThreshold); 717 | if(Bid < stopVal) { 718 | if(OrderSelect(OrderStatus.ticketNr, SELECT_BY_TICKET) == false) { 719 | Print("CheckExit Error: error selecting order ", ErrorDescription(GetLastError())); 720 | return; 721 | } 722 | if(!OrderClose(OrderTicket(), OrderLots(), Bid, ttnDefaultSlippage, ttnClrBuyClose)) { 723 | if(LogLevel >= LevelError) { 724 | Print("Error: Cannot close order: " + ErrorDescription(GetLastError())); 725 | } 726 | } 727 | } 728 | } else if(OrderStatus.direction == DIR_SELL) { 729 | double tmpATR = iATR(NULL, 0, ATRPeriod, 1); 730 | double tmpMA = iMA(NULL, 0, MAPeriodFast, 0, MAMethodFast, PRICE_TYPICAL, 1); 731 | double stopVal = tmpMA + (tmpATR * SlowATRThreshold); 732 | if(Bid > stopVal) { 733 | if(OrderSelect(OrderStatus.ticketNr, SELECT_BY_TICKET) == false) { 734 | Print("CheckExit Error: error selecting order ", ErrorDescription(GetLastError())); 735 | return; 736 | } 737 | if(!OrderClose(OrderTicket(), OrderLots(), Ask, ttnDefaultSlippage, ttnClrBuyClose)) { 738 | if(LogLevel >= LevelError) { 739 | Print("Error: Cannot close order: " + ErrorDescription(GetLastError())); 740 | } 741 | } 742 | } 743 | } 744 | 745 | return; 746 | } 747 | -------------------------------------------------------------------------------- /Include/ttnCommon.mqh: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttnCommon.mqh | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | #property copyright "tickelton@gmail.com" 7 | #property link "https://github.com/tickelton" 8 | #property strict 9 | 10 | /* MIT License 11 | 12 | Copyright (c) 2017 tickelton@gmail.com 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a copy 15 | of this software and associated documentation files (the "Software"), to deal 16 | in the Software without restriction, including without limitation the rights 17 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | copies of the Software, and to permit persons to whom the Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | SOFTWARE. 31 | */ 32 | 33 | #define ttnMagicNumberTurtleSystem1 5132 34 | #define ttnMagicNumberTurtleSystem2 5133 35 | #define ttnMagicNumberTrueSymphonie 5134 36 | #define ttnMagicNumberHFOne 5135 37 | #define ttnMagicNumber201703breakoutreversal 5136 38 | #define ttnMagicNumber201704plateaudrawer 5137 39 | #define ttnMagicNumber201705plateauReversal 5138 40 | #define ttnMagicNumber201705CCIbreakoutReversal 5139 41 | #define ttnMagicNumber201712MarketWeatherDualMA 5140 42 | #define ttnMagicNumber201712MarketWeatherTripleMA 5141 43 | #define ttnMagicNumber201712MarketWeatherRSI 5142 44 | #define ttnMagicNumber201712MarketWeatherWpR 5143 45 | #define ttnMagicNumber201712MarketWeatherStoch 5144 46 | 47 | #define OP_NONE 9999 48 | 49 | enum eLogLevel { 50 | LevelCritical = 0, 51 | LevelError = 1, 52 | LevelWarning = 2, 53 | LevelInfo = 3, 54 | LevelDebug = 4, 55 | LevelTrace = 5, 56 | }; 57 | 58 | enum eTradeDirection { 59 | DIR_NONE, 60 | DIR_BUY, 61 | DIR_SELL, 62 | }; 63 | 64 | enum eCorrelationType { 65 | CORRELATION_MILD, 66 | CORRELATION_STRONG, 67 | }; 68 | 69 | int ttnDefaultSlippage = 3; 70 | int ttnClrBuyOpen = clrBlue; 71 | int ttnClrBuyClose = clrRed; 72 | int ttnClrSellOpen = clrRed; 73 | int ttnClrSellClose = clrBlue; 74 | int ttnClrDelete = clrYellow; 75 | 76 | #define N_CURRENCY_PAIRS 14 77 | #define N_CORRELATION_PAIRS 13 78 | struct sCorrelationTable { 79 | string name; 80 | string moderateCorrelation[N_CORRELATION_PAIRS]; 81 | string strongCorrelation[N_CORRELATION_PAIRS]; 82 | }; 83 | 84 | sCorrelationTable CorrelationTable[N_CURRENCY_PAIRS] = { 85 | { 86 | "AUDJPY", 87 | {"NZDUSD", "", "", "", "", "", "", "", "", "", "", "", ""}, 88 | {"EURGBP", "EURJPY", "GBPCHF", "GBPJPY", "GBPUSD", "USDJPY", "", "", "", "", "", "", ""} 89 | }, 90 | { 91 | "AUDUSD", 92 | {"EURGBP", "EURJPY", "GBPCHF", "GBPJPY", "GBPUSD", "USDAD", "USDJPY", "", "", "", "", "", ""}, 93 | {"EURAUD", "NZDUSD", "", "", "", "", "", "", "", "", "", "", ""} 94 | }, 95 | { 96 | "EURAUD", 97 | {"EURJPY", "GBPJPY", "GBPUSD", "NZDUSD", "", "", "", "", "", "", "", "", ""}, 98 | {"AUDUSD", "", "", "", "", "", "", "", "", "", "", "", ""} 99 | }, 100 | { 101 | "EURCHF", 102 | {"", "", "", "", "", "", "", "", "", "", "", "", ""}, 103 | {"", "", "", "", "", "", "", "", "", "", "", "", ""}}, 104 | { 105 | "EURGBP", 106 | {"AUDUSD", "", "", "", "", "", "", "", "", "", "", "", ""}, 107 | {"AUDJPY", "EURJPY", "GBPCHF", "GBPJPY", "GBPUSD", "NZDUSD", "USDJPY", "", "", "", "", "", ""} 108 | }, 109 | { 110 | "EURJPY", 111 | {"AUDUSD", "EURAUD", "", "", "", "", "", "", "", "", "", "", ""}, 112 | {"AUDJPY", "EURGBP", "GBPCHF", "GBPJPY", "GBPUSD", "NZDUSD", "USDJPY", "", "", "", "", "", ""} 113 | }, 114 | { 115 | "EURUSD", 116 | {"USDCAD", "", "", "", "", "", "", "", "", "", "", "", ""}, 117 | {"USDCHF", "", "", "", "", "", "", "", "", "", "", "", ""} 118 | }, 119 | { 120 | "GBPCHF", 121 | {"AUDUSD", "", "", "", "", "", "", "", "", "", "", "", ""}, 122 | {"AUDJPY", "EURGBP", "EURJPY", "GBPJPY", "GBPUSD", "NZDUSD", "USDJPY", "", "", "", "", "", ""} 123 | }, 124 | { 125 | "GBPJPY", 126 | {"AUDUSD", "EURAUD", "", "", "", "", "", "", "", "", "", "", ""}, 127 | {"AUDJPY", "EURGBP", "EURJPY", "GBPCHF", "GBPUSD", "NZDUSD", "USDJPY", "", "", "", "", "", ""} 128 | }, 129 | { 130 | "GBPUSD", 131 | {"AUDUSD", "EURAUD", "NZDUSD", "", "", "", "", "", "", "", "", "", ""}, 132 | {"AUDJPY", "EURGBP", "EURJPY", "GBPCHF", "GBPJPY", "USDJPY", "", "", "", "", "", "", ""} 133 | }, 134 | { 135 | "NZDUSD", 136 | {"AUDJPY", "EURAUD", "GBPUSD", "USDCAD", "", "", "", "", "", "", "", "", ""}, 137 | {"AUDUSD", "EURGBP", "EURJPY", "GBPCHF", "GBPJPY", "USDJPY", "", "", "", "", "", "", ""} 138 | }, 139 | { 140 | "USDCAD", 141 | {"AUDUSD", "EURUSD", "NZDUSD", "USDCHF", "", "", "", "", "", "", "", "", ""}, 142 | {"", "", "", "", "", "", "", "", "", "", "", "", ""} 143 | }, 144 | { 145 | "USDCHF", 146 | {"USDCAD", "", "", "", "", "", "", "", "", "", "", "", ""}, 147 | {"EURUSD", "", "", "", "", "", "", "", "", "", "", "", ""} 148 | }, 149 | { 150 | "USDJPY", 151 | {"AUDUSD", "", "", "", "", "", "", "", "", "", "", "", ""}, 152 | {"AUDJPY", "EURGBP", "EURJPY", "GBPCHF", "GBPJPY", "GBPUSD", "NZDUSD", "", "", "", "", "", ""} 153 | }, 154 | }; 155 | 156 | struct s_linereg { 157 | double m; 158 | double b; 159 | double r; 160 | }; 161 | -------------------------------------------------------------------------------- /Include/ttnCommonFuncs.mqh: -------------------------------------------------------------------------------- 1 | /* MIT License 2 | 3 | Copyright (c) 2017 tickelton@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | #include 25 | 26 | #import "ttnCommonFuncs.ex4" 27 | s_linereg linereg(int n, int m); 28 | int getTimeDiff(datetime time1, datetime time2); 29 | void LogTerminal(eLogLevel level, eLogLevel gLevel, string s); 30 | #import 31 | -------------------------------------------------------------------------------- /Indicators/ttn-201712-market_weather_dual_ma_cross.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttn-201712-market_weather_dual_ma_cross.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #property copyright "tickelton@gmail.com" 31 | #property link "https://github.com/tickelton" 32 | #property version "1.00" 33 | #property strict 34 | 35 | #property indicator_separate_window 36 | #property indicator_buffers 1 37 | #property indicator_color1 Gold 38 | 39 | input int SlowMAperiod = 60; 40 | input int FastMAperiod = 20; 41 | input int CountPeriods = 480; 42 | 43 | double ExtCrossBuffer[]; 44 | double ExtSlowMABuffer[]; 45 | double ExtFastMABuffer[]; 46 | 47 | int OnInit(void) 48 | { 49 | string short_name; 50 | 51 | IndicatorBuffers(3); 52 | IndicatorDigits(Digits); 53 | SetIndexStyle(0,DRAW_LINE,STYLE_SOLID, 1); 54 | SetIndexBuffer(0, ExtCrossBuffer); 55 | SetIndexBuffer(1, ExtSlowMABuffer); 56 | SetIndexBuffer(2, ExtFastMABuffer); 57 | 58 | short_name="MWPMC("+ 59 | IntegerToString(SlowMAperiod)+"/"+ 60 | IntegerToString(FastMAperiod)+"-"+ 61 | IntegerToString(CountPeriods)+")"; 62 | IndicatorShortName(short_name); 63 | SetIndexLabel(0,short_name); 64 | 65 | SetIndexDrawBegin(0,0); 66 | 67 | return(INIT_SUCCEEDED); 68 | } 69 | 70 | int getCrossValue(int idx) 71 | { 72 | static bool once = true; 73 | int crossCount = 0; 74 | 75 | for(int i=idx; i= ExtFastMABuffer[i+1] && 77 | ExtSlowMABuffer[i] < ExtFastMABuffer[i]) { 78 | crossCount++; 79 | } else if(ExtSlowMABuffer[i+1] <= ExtFastMABuffer[i+1] && 80 | ExtSlowMABuffer[i] > ExtFastMABuffer[i]) { 81 | crossCount++; 82 | } 83 | } 84 | 85 | return crossCount; 86 | } 87 | 88 | int OnCalculate(const int rates_total, 89 | const int prev_calculated, 90 | const datetime &time[], 91 | const double &open[], 92 | const double &high[], 93 | const double &low[], 94 | const double &close[], 95 | const long &tick_volume[], 96 | const long &volume[], 97 | const int &spread[]) 98 | { 99 | int i, pos; 100 | 101 | ArraySetAsSeries(ExtCrossBuffer, true); 102 | ArraySetAsSeries(ExtSlowMABuffer, true); 103 | ArraySetAsSeries(ExtFastMABuffer, true); 104 | 105 | pos=rates_total - CountPeriods - prev_calculated - 1; 106 | for(i=pos+CountPeriods; i>=0; i--) { 107 | ExtSlowMABuffer[i] = iMA(NULL, 0, SlowMAperiod, 0, MODE_SMA, PRICE_CLOSE, i); 108 | } 109 | for(i=pos+CountPeriods; i>=0; i--) { 110 | ExtFastMABuffer[i] = iMA(NULL, 0, FastMAperiod, 0, MODE_SMA, PRICE_CLOSE, i); 111 | } 112 | for(i=pos; i>=0; i--) { 113 | ExtCrossBuffer[i] = getCrossValue(i); 114 | } 115 | 116 | return(rates_total); 117 | } 118 | -------------------------------------------------------------------------------- /Indicators/ttn-201712-market_weather_price_ma_cross.mq4: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| ttn-201712-market_weather_price_ma_cross.mq4 | 3 | //| tickelton@gmail.com | 4 | //| https://github.com/tickelton | 5 | //+------------------------------------------------------------------+ 6 | 7 | /* MIT License 8 | 9 | Copyright (c) 2017 tickelton@gmail.com 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | */ 29 | 30 | #property copyright "tickelton@gmail.com" 31 | #property link "https://github.com/tickelton" 32 | #property version "1.00" 33 | #property strict 34 | 35 | #property indicator_separate_window 36 | #property indicator_buffers 1 37 | #property indicator_color1 Gold 38 | 39 | input int MAperiod = 60; 40 | input int CountPeriods = 180; 41 | 42 | double ExtCrossBuffer[]; 43 | double ExtMABuffer[]; 44 | 45 | int OnInit(void) 46 | { 47 | string short_name; 48 | 49 | IndicatorBuffers(2); 50 | IndicatorDigits(Digits); 51 | SetIndexStyle(0,DRAW_LINE,STYLE_SOLID, 1); 52 | SetIndexBuffer(0, ExtCrossBuffer); 53 | SetIndexBuffer(1, ExtMABuffer); 54 | 55 | short_name="MWPMC("+ 56 | IntegerToString(MAperiod)+"/"+ 57 | IntegerToString(CountPeriods)+")"; 58 | IndicatorShortName(short_name); 59 | SetIndexLabel(0,short_name); 60 | 61 | SetIndexDrawBegin(0,0); 62 | 63 | return(INIT_SUCCEEDED); 64 | } 65 | 66 | int getCrossValue(int idx) 67 | { 68 | static bool once = true; 69 | int crossCount = 0; 70 | 71 | if(once) { 72 | for(int i=idx; i= Close[i+1] && 80 | ExtMABuffer[i] < Close[i]) { 81 | crossCount++; 82 | } else if(ExtMABuffer[i+1] <= Close[i+1] && 83 | ExtMABuffer[i] > Close[i]) { 84 | crossCount++; 85 | } 86 | } 87 | 88 | return crossCount; 89 | } 90 | 91 | int OnCalculate(const int rates_total, 92 | const int prev_calculated, 93 | const datetime &time[], 94 | const double &open[], 95 | const double &high[], 96 | const double &low[], 97 | const double &close[], 98 | const long &tick_volume[], 99 | const long &volume[], 100 | const int &spread[]) 101 | { 102 | int i, pos; 103 | 104 | ArraySetAsSeries(ExtCrossBuffer, true); 105 | ArraySetAsSeries(ExtMABuffer, true); 106 | 107 | pos=rates_total - CountPeriods - prev_calculated - 1; 108 | for(i=pos+CountPeriods; i>=0; i--) { 109 | ExtMABuffer[i] = iMA(NULL, 0, MAperiod, 0, MODE_SMA, PRICE_CLOSE, i); 110 | } 111 | for(i=pos; i>=0; i--) { 112 | ExtCrossBuffer[i] = getCrossValue(i); 113 | } 114 | 115 | return(rates_total); 116 | } 117 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 tickelton@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Libraries/ttnCommonFuncs.mq4: -------------------------------------------------------------------------------- 1 | /* MIT License 2 | 3 | Copyright (c) 2017 tickelton@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | */ 23 | 24 | #property library 25 | 26 | #include 27 | 28 | // fits a line through Close[n] to Close[m] 29 | // using the least squares fit algorithm. 30 | s_linereg linereg(int n, int m) export 31 | { 32 | double count = n - m + 1.0; 33 | s_linereg s_line; 34 | 35 | s_line.m = 0; 36 | s_line.b = 0; 37 | s_line.r = 0; 38 | 39 | double sumx = 0; 40 | double sumx2 = 0; 41 | double sumxy = 0; 42 | double sumy = 0; 43 | double sumy2 = 0; 44 | 45 | double j = 0; 46 | for(int i=n; i>=m; i--) { 47 | sumx += j; 48 | sumx2 += j*j; 49 | sumxy += j * Close[i]; 50 | sumy += Close[i]; 51 | sumy2 += Close[i] * Close[i]; 52 | j++; 53 | } 54 | 55 | double denom = (count * sumx2 - (sumx * sumx)); 56 | if(denom == 0) { 57 | return s_line; 58 | } 59 | s_line.m = (count * sumxy - sumx * sumy) / denom; 60 | s_line.b = (sumy * sumx2 - sumx * sumxy) / denom; 61 | s_line.r = (sumxy - sumx * sumy / count) / 62 | sqrt((sumx2 - (sumx * sumx)/count) * 63 | (sumy2 - (sumy * sumy)/count)); 64 | 65 | return s_line; 66 | } 67 | 68 | // Calculates the difference between two timestamps 69 | // in units of Period() of the current Chart. 70 | int getTimeDiff(datetime time1, datetime time2) export 71 | { 72 | int timeDiff = (int)(time1 - time2); 73 | int tfCurrent = Period(); 74 | if(tfCurrent == PERIOD_M1) { 75 | timeDiff = timeDiff / 60; 76 | } else if(tfCurrent == PERIOD_M5) { 77 | timeDiff = timeDiff / 300; 78 | } else if(tfCurrent == PERIOD_M15) { 79 | timeDiff = timeDiff / 900; 80 | } else if(tfCurrent == PERIOD_M30) { 81 | timeDiff = timeDiff / 1800; 82 | } else if(tfCurrent == PERIOD_H1) { 83 | timeDiff = timeDiff / 3600; 84 | } else if(tfCurrent == PERIOD_H4) { 85 | timeDiff = timeDiff / 14400; 86 | } else if(tfCurrent == PERIOD_D1) { 87 | timeDiff = timeDiff / 86400; 88 | } 89 | 90 | if(timeDiff < 0) 91 | return -timeDiff; 92 | 93 | return timeDiff; 94 | } 95 | 96 | void LogTerminal(eLogLevel level, eLogLevel gLevel, string s) export 97 | { 98 | if(level <= gLevel) 99 | Print(s); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mql4 2 | 3 | This repository contains a random collection of [MQL4](https://www.mql4.com/) 4 | code to be used with [MetaTrader4](https://www.metatrader4.com/). 5 | 6 | The idea is to collect all the mql4 code I use in a central location, archive 7 | some Experts no longer in active use, and refactor several components to reduce 8 | duplicated code. 9 | 10 | Not everything you find here will necessarily be functional as some code might 11 | depend on additional libraries or indicators. 12 | Because a lot of the MQL4 code found on the Internet does not come with 13 | conclusive copyright information, all such code was removed here to be on the 14 | safe side. 15 | If you run into such problems and need help getting something to run, don't 16 | hesitate to contact me, but please don't open any issues against this 17 | repository in such cases (as the problem usually lies not with the code 18 | contained herein, but with e.g. missing dependencies). 19 | 20 | ## License 21 | 22 | All code and data contained in the repository is release under the MIT License. 23 | 24 | Copyright (c) 2017 tickelton@gmail.com 25 | --------------------------------------------------------------------------------