// @version=5 strategy('Forex 5 min. Long - EMA Cross Strategy', overlay=true, precision=6, initial_capital=50000, default_qty_type=strategy.fixed, calc_on_order_fills=true, calc_on_every_tick=true, max_labels_count=500) // ====================== // ==== Custom Types ==== // ====================== // This section uses PineScript's new Type syntax to define important data structures used throughout the script. type Settings float maxLossPercentage int leverage float firstLongTargetMultiplier float secondLongTargetMultiplier float thirdLongTargetMultiplier float entryOffsetLong float stopOffsetLong float trailFactor float trailOffsetLong float maxSetupFactorSetupBarTooLarge float maxSetupFactorPeviousBarTooLarge int numBarsCancelTradeEntryLong bool priceExceedsStoploss bool useAutoExit int numBarsAutoExitLong float minSetupBarSize float maxSetupBarSize bool shiftFirstGreenBar int maxNumBarsSetupFollowingCrossover // ================ // ==== Inputs ==== // ================ // Settings Object: General User-Defined Inputs Settings settings = Settings.new( input.float(title='Maximum Loss %', defval=2, minval=.25, maxval=100, step=.25, group="General Settings"), input.int(title="Leverage", defval=30, minval=1, maxval=500, step=1, group="General Settings"), input.float(title="First Target Multiplier", defval=2, group="General Settings", minval=0, maxval=10.0, step=0.1), input.float(title="Second Target Multiplier", defval=3, group="General Settings", minval=0, maxval=10.0, step=0.1), input.float(title="Third Target Multiplier", defval=4, group="General Settings", minval=0, maxval=10.0, step=0.1), input.float(title="Entry Price Offset (pips)", defval=0, group="General Settings", minval=0, maxval=100.0, step=0.1), input.float(title="Stop Price Offset (pips)", defval=0, group="General Settings", minval=0, maxval=100.0, step=0.1), input.float(title="Trailing Stop Factor", defval=2.5, group="General Settings", minval=0, maxval=10.0, step=0.1), input.float(title="Trailing Stop Price Offset (pips)", defval=0.2, group="General Settings", minval=0, maxval=100.0, step=0.1), input.float(title="Setup/Previous Bar Size Factor", defval=3, group="General Settings", minval=0, maxval=100.0, step=0.1), input.float(title="Previous/Setup Bar Size Factor", defval=3, group="General Settings", minval=0, maxval=100.0, step=0.1), input.int(title="Number of Bars to Cancel Trade Entry", defval=5, group="General Settings", minval=0, maxval=20), input.bool(title="Cancel trade if price exceeds stop loss price", defval=true, group="General Settings"), input.bool(title="Automatically Exit Failing Trade", defval=true, group="General Settings", inline="autoExit"), input.int(title="Number of Bars", group="General Settings", defval=4, minval=1, maxval=50, inline="autoExit"), input.float(title="Filter Entry By Minimum Setup Bar Size (pips)", defval=4.5, group="General Settings", minval=0, maxval=1000, step=0.1, tooltip="Value of 0 for no filtering. Input a value to filter."), input.float(title="Filter Entry By Maximum Setup Bar Size (pips)", defval=50, group="General Settings", minval=0, maxval=1000, step=0.1), input.bool(title="Allow setup bar to be at the EMA Curves crossover", defval=false, group="General Settings"), input.int(title="Maximum Number of Bars for Setup Bar Following EMA Curves Crossover", defval=5, group="General Settings", minval=0, maxval=20) ) // // EMA Cross Inputs // EMA1period = input.int(28, "EMA 1 period", group="EMA Settings") EMA2period = input.int(26, "EMA 2 period", group="EMA Settings") timeFrame2 = input.timeframe('30', title="Timeframe for EMA2", options=['5', '10', '15', '20', '25', '30', '35', '40', '45', '60', '75', '90', '105', '120', '135', '150', '165', '180'], group='EMA Settings', tooltip="Typically use a timeframe that is ~5X the chart timeframe.") // // Filter Settings // // EMA Settings useEMAfilter = input.bool(title="Use EMA Filter", defval=true, group="EMA Filters", inline="ema") EMAperiod = input.int(title="Period", defval=50, minval=1, step=1, group="EMA Filters", inline="ema", tooltip="The period length of the EMA.") EMAslopeThreshold = input.float(title="Slope Threshold", defval=-5.5, minval=-500, maxval=500, step=0.1, group="EMA Filters", inline="ema", tooltip="The allowable slope for the steepness of the EMA at the setup bar. Positive values for short trades. Negative values for long trades.") useSMAfilter = input.bool(title="Use SMA Filter", defval=false, group="EMA Filters", inline="sma") SMAperiod = input.int(title="Period", defval=20, minval=1, step=1, group="EMA Filters", inline="sma", tooltip="The period length of the SMA.") // RSI Settings useRSIfilter = input.bool(title="Use RSI Filter", defval=true, group="RSI Filter", tooltip="Relative Strength Index. Measures the speed and change of price movements. RSI values range from 0 to 100. Above 70 is considered overbought. Below 30 is considered oversold.") RSIperiod = input.int(title="Period", defval=18, minval=1, step=1, group="RSI Filter", inline="rsi", tooltip="The period length of the RSI.") RSIthreshold = input.float(title="Threshold", defval=56, minval=1, maxval=100, step=1, group="RSI Filter", inline="rsi", tooltip="The minimum value of the RSI.") RSIpriorBars = input.int(title="Number of bars prior to the setup bar for RSI threshold", defval=3, minval=1, step=1, group="RSI Filter", tooltip="The number of bars prior to the setup bar for determining whether the RSI is above the threshold value.") // CCI Settings useCCIfilter = input.bool(title="Use CCI Filter", defval=false, group="CCI Filter", tooltip="Commodity Channel Index. Measures the current price level relative to its average price over a specific period. The resulting CCI values oscillate around a zero line. Positive values indicate that the price is above the average, while negative values indicate that the price is below the average.") CCIperiod = input.int(title="Period", defval=50, minval=1, step=1, group="CCI Filter", inline="cci", tooltip="The period length of the CCI.") CCIthreshold = input.float(title="Threshold", defval=-5, minval=-500, maxval=500, step=1, group="CCI Filter", inline="cci", tooltip="The threshold value of the CCI. Use a positive number for short trades. Use a negative number for long trades.") CCIpriorBars = input.int(title="Number of bars prior to the setup bar for CCI threshold", defval=2, minval=1, step=1, group="CCI Filter", tooltip="The number of bars prior to the setup bar for determining whether the CCI exceeds the threshold value.") EMA1slopeThreshold = input.float(title="EMA1 Slope Threshold", defval=10.0, minval=-500, maxval=500, step=0.5, group="CCI Filter", tooltip="The slope threshold for the fast EMA curve to override the CCI filter and allow big moves.") // ADX Settings useADXfilter = input.bool(title="Use ADX Filter", defval=true, group="ADX Filter", tooltip="Average Directional Index. Measures the strength and direction of a trend. Consists of a single line that fluctuates between 0 and 100. The higher the ADX value, the stronger the trend, while lower values indicate a weaker or non-existent trend.") ADXlength = input.int(title="Length", defval=12, minval=1, step=1, group="ADX Filter", inline="adx", tooltip="The period length of the ADX.") ADXsmoothing = input.int(title="Smoothing", defval=10, minval=1, step=1, group="ADX Filter", inline="adx", tooltip="The period length of the moving average used to smooth the ADX line.") ADXthreshold = input.float(title="Threshold", defval=13, minval=0, maxval=100, step=1, group="ADX Filter", tooltip="The minimum value of the ADX.") //ADXpriorBars = input.int(title="Number of bars prior to the setup bar for ADX threshold", defval=5, minval=1, step=1, group="ADX Filter", tooltip="The number of bars prior to the setup bar for determining whether the ADX is above the threshold value.") // ATR Settings useATRfilter = input.bool(title="Use ATR Filter", defval=true, group="ATR Filter", tooltip="Average True Range. Provides a measurement of the average price range between the high and low prices over a specified period. A higher ATR value suggests higher volatility, while a lower ATR value indicates lower volatility. Useful in filtering out potentially smaller price moves.") ATRlength = input.int(title="Length", defval=14, minval=1, step=1, group="ATR Filter", inline="atr", tooltip="The period length of the ATR.") ATRthreshold = input.float(title="Threshold (pips)", defval=2.7, minval=1, maxval=500, step=0.1, group="ATR Filter", inline="atr", tooltip="The minimum value of the ATR.") // MACD Settings useMACDfilter = input.bool(title="Use MACD Filter", defval=false, group="MACD Filter", tooltip="Moving Average Convergence Divergence. Allows trade setups where the MACD and signal lines cross within a user-defined number of bars preceeding the setup bar.") MACDfastLength = input.int(title="Fast Length", defval=18, minval=1, step=1, group="MACD Filter", inline="macd1", tooltip="The length of the fast EMA used for the MACD calculation.") MACDslowLength = input.int(title="Slow Length", defval=24, minval=1, step=1, group="MACD Filter", inline="macd1", tooltip="The length of the slow EMA used for the MACD calculation.") MACDsignalLength = input.int(title="Signal Length", defval=9, minval=1, step=1, group="MACD Filter", inline="macd2", tooltip="The length of the EMA of the MACD line for calculating the signal line.") MACDpriorBars = input.int(title="Crossover within prior bars", defval=3, minval=1, step=1, group="MACD Filter", tooltip="The number of bars prior to the setup bar for MACD/signal line crossover.") // // EMA Calculations // src2 = request.security(syminfo.tickerid, timeFrame2, close) float EMA1 = ta.ema(close, EMA1period) float EMA2 = ta.ema(src2, EMA2period) // =========================== // ==== Entries and Exits ==== // =========================== // Active Orders // Check if strategy has open positions bool inLong = strategy.position_size > 0.0001 // Entry Conditions: Booleans for Position Entries int signal = 0 bool isBullishCross = ta.crossover(EMA1, EMA2) bool isBearishCross = ta.crossunder(EMA1, EMA2) signal := isBullishCross ? 1 : isBearishCross ? -1 : nz(signal[1]) isDifferentSignalType = ta.change(signal) isBuySignal = (signal == 1) isNewBuySignal = isBuySignal and isDifferentSignalType // Initialize variables var firstGreenBar = false var ignoreGreenBars = false int barsSinceBullishCross = ta.barssince(isBullishCross) // Reset for new EMA curve crossover if isNewBuySignal == 1 firstGreenBar := 0 ignoreGreenBars := 0 // Finds first green bar after EMA curve crossover that closes above the upper EMA curve if (isBuySignal == 1) if (close > open) and (ignoreGreenBars == 0) and (close > EMA1) // identify the setup bar firstGreenBar := 1 ignoreGreenBars := 1 else if (close <= open ) and (ignoreGreenBars == 0) // account for red bars prior to the setup bar firstGreenBar := 0 ignoreGreenBars := 0 if (barsSinceBullishCross >= settings.maxNumBarsSetupFollowingCrossover) // cancel setup if the firstGreenBar would occur too long after the EMA curve crossover firstGreenBar := 0 ignoreGreenBars := 1 else if (ignoreGreenBars == 1) // account for bars following the setup bar firstGreenBar := 0 ignoreGreenBars := 1 else if (barsSinceBullishCross >= settings.maxNumBarsSetupFollowingCrossover) // cancel setup if the firstGreenBar would occur too long after the EMA curve crossover firstGreenBar := 0 ignoreGreenBars := 1 else // account for all bars outside the long trade crossover window firstGreenBar := 0 ignoreGreenBars := 0 // shift the firstGreenBar away from the crossover point if (settings.shiftFirstGreenBar == 0) and firstGreenBar and isBullishCross firstGreenBar := 0 ignoreGreenBars := 0 // // EMA Filter // float EMAslope = ta.ema(close, EMAperiod) - ta.ema(close[1], EMAperiod) int EMApriorBars = 2 bool EMAdowntrendOK = true if firstGreenBar for i = 0 to EMApriorBars if EMAslope[i] < EMAslopeThreshold*0.00001 EMAdowntrendOK := false isEMAdowntrendOK = useEMAfilter ? EMAdowntrendOK : true // // SMA Filter // isSMAuptrend = useSMAfilter ? (ta.ema(close, SMAperiod) > ta.ema(close[1], SMAperiod)) : true // // RSI Filter // // Calculate RSI RSIfilter = ta.rsi(close, RSIperiod) // Variable to track the condition bool RSIbelowThreshold = false // Loop through the prior bars if firstGreenBar for i = 0 to RSIpriorBars if RSIfilter[i] <= RSIthreshold RSIbelowThreshold := true isRSIbelowThreshold = useRSIfilter ? RSIbelowThreshold : true // // CCI Filter // // Calculate CCI CCIfilter = ta.cci(close, CCIperiod) // Variable to track the condition bool CCIexceedsThreshold = false float EMA1slope = EMA1 - EMA1[1] // Loop through the prior bars if firstGreenBar for i = 0 to CCIpriorBars if CCIfilter[i] <= CCIthreshold CCIexceedsThreshold := true if CCIexceedsThreshold == false and EMA1slope > EMA1slopeThreshold*0.00001 CCIexceedsThreshold := true isCCIexceedsThreshold = useCCIfilter ? CCIexceedsThreshold : true // // ADX Filter // [diPlus, diMinus, ADX] = ta.dmi(ADXlength, ADXsmoothing) isADXaboveThreshold = useADXfilter ? (ADX > ADXthreshold) : true // // ATR Filter // ATR = ta.atr(ATRlength) isATRaboveThreshold = useATRfilter ? (ATR > ATRthreshold*0.0001) : true // // MACD Filter // // Calculate MACD and signal lines [MACDline, signalLine, histLine] = ta.macd(close, MACDfastLength, MACDslowLength, MACDsignalLength) bool MACDfilter = ta.crossover(MACDline, signalLine) bool MACDBullishCross = false // Loop through the prior bars if firstGreenBar for i = 1 to MACDpriorBars if MACDfilter[i] MACDBullishCross := true isMACDBullishCross = useMACDfilter ? MACDBullishCross : true // // Filters based on bar characteristics // //Determine whether setup bar is too large or too small compared to the previous bar float setupBarSize = 0.0 float previousBarSize = 0.0 float setup_to_PreviousBarFactor = 0.0 float previous_to_SetupBarFactor = 0.0 if firstGreenBar setupBarSize := high - low previousBarSize := high[1] - low[1] setup_to_PreviousBarFactor := setupBarSize / previousBarSize previous_to_SetupBarFactor := 1/setup_to_PreviousBarFactor else setup_to_PreviousBarFactor := setup_to_PreviousBarFactor[1] previous_to_SetupBarFactor := previous_to_SetupBarFactor[2] bool isSetup_to_PreviousBar = setup_to_PreviousBarFactor < settings.maxSetupFactorSetupBarTooLarge bool isPrevious_to_SetupBar = previous_to_SetupBarFactor < settings.maxSetupFactorPeviousBarTooLarge bool isMinSetupBarSize = setupBarSize >= settings.minSetupBarSize*0.0001 bool isMaxSetupBarSize = setupBarSize <= settings.maxSetupBarSize*0.0001 // // Start trade // startLongTrade = inLong==0 and firstGreenBar and isEMAdowntrendOK and isSMAuptrend and isRSIbelowThreshold and isCCIexceedsThreshold and isADXaboveThreshold and isATRaboveThreshold and isMACDBullishCross and isSetup_to_PreviousBar and isPrevious_to_SetupBar and isMinSetupBarSize and isMaxSetupBarSize // LONG TRADES // Set profit target levels float targetFactorLong = 0.0 float firstTargetLong = 0.0 float secondTargetLong = 0.0 float thirdTargetLong = 0.0 float trailTarget = 0.0 if startLongTrade==1 targetFactorLong := high - low if inLong == 0 firstTargetLong := high + (targetFactorLong * settings.firstLongTargetMultiplier) secondTargetLong := high + (targetFactorLong * settings.secondLongTargetMultiplier) thirdTargetLong := high + (targetFactorLong * settings.thirdLongTargetMultiplier) trailTarget := high + (targetFactorLong * settings.trailFactor) else firstTargetLong := firstTargetLong[1] secondTargetLong := secondTargetLong[1] thirdTargetLong := thirdTargetLong[1] trailTarget := trailTarget[1] else targetFactorLong := targetFactorLong[1] firstTargetLong := firstTargetLong[1] secondTargetLong := secondTargetLong[1] thirdTargetLong := thirdTargetLong[1] trailTarget := trailTarget[1] // Set stop loss levels float stopLossLong = 0.0 if startLongTrade == 1 if inLong == 0 stopLossLong := low - settings.stopOffsetLong*0.0001 else stopLossLong := stopLossLong[1] else if (EMA2 > trailTarget) // Trailing stop stopLossLong := EMA2 - settings.trailOffsetLong*0.0001 if stopLossLong < stopLossLong[1] stopLossLong := stopLossLong[1] else stopLossLong := stopLossLong[1] // // ORDERS // //LONG positions //Order quantity based on maximum allowable loss and limitations of leverage buying power float longEntryPrice = 0.0 float maxLossAmount = 0.0 float positionSize = 0.0 float orderQuantity = 0.0 float risk = 0.0 float spread = 0.0002 if startLongTrade == 1 and inLong == 0 longEntryPrice := high + settings.entryOffsetLong*0.0001 + spread maxLossAmount := strategy.equity * settings.maxLossPercentage / 100 risk := longEntryPrice - stopLossLong positionSize := maxLossAmount / risk if (positionSize * longEntryPrice) > (1 * strategy.equity * settings.leverage) orderQuantity := math.round(((1 * strategy.equity * settings.leverage)/longEntryPrice), 2) else orderQuantity := math.round(positionSize, 2) else longEntryPrice := longEntryPrice[1] maxLossAmount := maxLossAmount[1] positionSize := positionSize[1] if (inLong == 0) and (startLongTrade == 1) and (longEntryPrice > 0.0) strategy.entry("Long", strategy.long, qty=orderQuantity, limit = longEntryPrice, stop = longEntryPrice) isPriceExceedsStoploss = settings.priceExceedsStoploss ? close < stopLossLong : false int longNumBarsSinceSetup=ta.barssince(startLongTrade==1) bool cancelLongTradeEntry = (longNumBarsSinceSetup > settings.numBarsCancelTradeEntryLong) or isPriceExceedsStoploss if cancelLongTradeEntry == 1 strategy.cancel("Long") // Early exit conditions // Close the trade to minimize the loss after taking profits and the price goes in the wrong direction barsSinceClosed = strategy.closedtrades > 0 ? bar_index - strategy.closedtrades.exit_bar_index(strategy.closedtrades - 1) : na var float setupBarExit = 0.0 if startLongTrade and firstGreenBar setupBarExit := open int longNumBarsSinceInLong=ta.barssince(strategy.position_size < 0.0001) - 1 float midSignalBar = high[longNumBarsSinceSetup] - (high[longNumBarsSinceSetup]-low[longNumBarsSinceSetup])/2 bool longAutoExit = (longNumBarsSinceSetup == settings.numBarsAutoExitLong) and (inLong == 1) and (close < midSignalBar) and (longNumBarsSinceInLong < longNumBarsSinceSetup) if (longAutoExit == 1) and (settings.useAutoExit == 1) strategy.close("Long", qty_percent=100) else if inLong and (longNumBarsSinceSetup > barsSinceClosed) and (close < setupBarExit) strategy.close_all() else strategy.exit("T1/SL", from_entry="Long", qty_percent=25, limit=firstTargetLong, stop=stopLossLong) strategy.exit("T2/SL", from_entry="Long", qty_percent=25, limit=secondTargetLong, stop=stopLossLong) strategy.exit("T3/SL", from_entry="Long", qty_percent=25, limit=thirdTargetLong, stop=stopLossLong) strategy.exit("SL/TRAIL", from_entry="Long", qty_percent=25, stop=stopLossLong) // clean up any remaining fragments of the positions to completely close out a trade if strategy.position_size < 10 strategy.close_all() // // Plot curves // plot(EMA1, "EMA1", color=color.aqua, linewidth=2) plot(EMA2, "EMA2", color=color.rgb(233, 69, 69), linewidth=2) // // Plot stop loss/trailing stop and targets // plot(longEntryPrice, "longEntryPrice", color=color.lime) plot(stopLossLong, "stopLossLong", color=color.purple) plot(firstTargetLong, "firstTargetLong") plot(secondTargetLong, "secondTargetLong") plot(thirdTargetLong, "thirdTargetLong") // ========================= // ==== Plotting Labels ==== // ========================= plotshape(startLongTrade ? low : na, 'Buy', shape.labelup, location.belowbar, color.green, size=size.small, offset=0)