Hammers & Stars Strategy
This script trades basic hammer and shooting star candlestick patterns.
It’s an extremely simple strategy with minimal filters, and according to my personal manual backtesting and automated trading results, performs best on the Daily chart on certain forex pairs.
It is intended to be traded on the forex markets but theoretically should work on all markets (especially if you optimize the settings).
The script also comes with complete AutoView automation for Oanda.
Table of Contents
How It Works
According to the default settings, the script defines a hammer candle as a candle which closes in the upper 33% of the entire bar, and does not close equal to its open (ie. is not a doji).
The same is true for shooting star candles – the candle must close in the lower third of the entire bar including the top and bottom wick in order to be considered valid.
This strategy is based on the definitions provided by Investopedia, and was inspired by a YouTube video created by my trading mentor Steven Hart.
It also includes full inbuilt automation through both AutoView and PineConnector.
If you’re unfamiliar with these services, I have an AutoView YouTube Guide and a PineConnector YouTube Guide which you might find helpful.
These guides are designed for coders, so if you’re a coder you will find them particularly interesting – but even if you’re not a coder, the first lesson in both series teaches you how to set up these services.
Settings

Updated 30th July 2021:

Strategy Settings:
>= ATR Filter:
Minimum size of entry candle compared to ATR
<= ATR Filter:
Maximum size of entry candle compared to ATR
Stop Loss ATR:
Stop loss multiplier (x ATR)
R:R:
Risk:Reward profile
Fib Level:
Used to calculate upper/lower third of candle. (For example, setting it to 0.5 will mean hammers must close >= 50% mark of the total candle size)
Start Date Filter:
Date & time to begin trading from
End Date Filter:
Date & time to stop trading
AutoView Oanda Settings:
Use AutoView:
If turned on then the script will format its alerts to be compatible with AutoView.
Use Oanda Demo:
If turned on then oandapractice broker prefix will be used for AutoView alerts (demo account). If turned off then live account will be used
Use Limit Order:
If turned on then AutoView will use limit orders. If turned off then market orders will be used (recommended to use limit order to mitigate spread issues)
Days To Leave Limit Order:
This is your GTD setting (good til day)
Account Balance:
Your account balance (used for calculating position size)
Account Currency:
Your account balance currency (used for calculating position size)
Risk Per Trade %:
Your risk per trade as a % of your account balance
PineConnector Settings
Use PineConnector:
If turned on then the script will format its alerts to be compatible with PineConnector.
License ID:
This is your PineConnector license ID which you can find by clicking here.
Risk Per Trade:
This is how much to risk per trade. If you’ve set PineConnector up to receive this parameter as a percentage of your account balance, use a whole number (eg. 1 = 1%). If you’ve set it up to be based on volume / fixed lot size then use a lot size (eg. 1 = 1 standard lot, 0.1 = mini lot etc).
MetaTrader Prefix:
This is the prefix for your MetaTrader symbols. Some brokers have a prefix before the symbol name (eg. m-EURUSD). Only input this if your symbols have one – leave blank otherwise.
MetaTrader Suffix:
This is the suffix for your MetaTrader symbols. Some brokers have a suffix after the symbol name (eg. EURUSD-f). Only input this if your symbols have one – leave blank otherwise.
Spread:
This is used to offset your limit orders if you’re using them. (Eg. 0.5 means set your buy limit half a pip below current price).
Use Limit Order:
If turned on the script will use a limit order for trade entry, if turned off the script will use a market order.
Source Code
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © ZenAndTheArtOfTrading / PineScriptMastery // Last Updated: 30th July, 2021 // @version=4 strategy("Hammers & Stars Strategy [Automated]", shorttitle="HSS", overlay=true) // Get Strategy Settings var g_strategy = "Strategy Settings" stopMultiplier = input(title="Stop Loss ATR", type=input.float, defval=1.0, tooltip="Stop loss multiplier (x ATR)", group=g_strategy) rr = input(title="R:R", type=input.float, defval=1.0, tooltip="Risk:Reward profile", group=g_strategy) fibLevel = input(title="Fib Level", type=input.float, defval=0.333, tooltip="Used to calculate upper/lower third of candle. (For example, setting it to 0.5 will mean hammers must close >= 50% mark of the total candle size)", group=g_strategy) // Filter Settings var g_filters = "Filter Settings" // ATR Filter atrMinFilterSize = input(title=">= ATR Filter", type=input.float, defval=0.0, minval=0.0, tooltip="Minimum size of entry candle compared to ATR", group=g_filters) atrMaxFilterSize = input(title="<= ATR Filter", type=input.float, defval=3.0, minval=0.0, tooltip="Maximum size of entry candle compared to ATR", group=g_filters) // EMA Filter emaFilterLength = input(title="EMA Filter Length", type=input.integer, defval=20, tooltip="EMA Length for filtering trades (set to zero to disable)", group=g_filters) emaTouchFilter = input(title="EMA Touch Filter", type=input.bool, defval=false, tooltip="Price must touch EMA", group=g_filters) // Date Filter startTime = input(title="Start Date Filter", defval=timestamp("01 Jan 1000 13:30 +0000"), type=input.time, tooltip="Date & time to begin trading from", group=g_filters) endTime = input(title="End Date Filter", defval=timestamp("1 Jan 2099 19:30 +0000"), type=input.time, tooltip="Date & time to stop trading", group=g_filters) // Time Filter timeSession = input(title="Time Session To Ignore Trades", type=input.session, defval="0600-0915", tooltip="Time session to ignore trades", group=g_filters) useTimeFilter = input(title="Use Time Session Filter", type=input.bool, defval=false, tooltip="Turns on/off time session filter", group=g_filters) // Get AutoView Settings var g_av = "AutoView Oanda Settings" autoview = input(title="Use AutoView?", type=input.bool, defval=false, tooltip="Turn this on to use AutoView alerts", group=g_av) oandaDemo = input(title="Use Oanda Demo?", type=input.bool, defval=false, tooltip="If turned on then oandapractice broker prefix will be used for AutoView alerts (demo account). If turned off then live account will be used", group=g_av) limitOrder = input(title="Use Limit Order?", type=input.bool, defval=true, tooltip="If turned on then AutoView will use limit orders. If turned off then market orders will be used", group=g_av) gtdOrder = input(title="Days To Leave Limit Order", type=input.integer, minval=0, defval=2, tooltip="This is your GTD setting (good til day)", group=g_av) accountBalance = input(title="Account Balance", type=input.float, defval=1000.0, step=100, tooltip="Your account balance (used for calculating position size)", group=g_av) accountCurrency = input(title="Account Currency", type=input.string, defval="USD", options=["AUD", "CAD", "CHF", "EUR", "GBP", "JPY", "NZD", "USD"], tooltip="Your account balance currency (used for calculating position size)", group=g_av) riskPerTrade = input(title="Risk Per Trade %", type=input.float, defval=2.0, step=0.5, tooltip="Your risk per trade as a % of your account balance", group=g_av) // PineConnector Settings var g_pc = "PineConnector Settings" pineconnector = input(title="Use PineConnector?", type=input.bool, defval=false, tooltip="Turn this on to use PineConnector alerts", group=g_pc) pc_id = input(title="License ID", defval="YOUR_ID", type=input.string, group=g_pc, tooltip="This is your PineConnector license ID") pc_risk = input(title="Risk Per Trade", defval=1, step=0.5, type=input.float, group=g_pc, tooltip="This is how much to risk per trade (% of balance or lots)") pc_prefix = input(title="MetaTrader Prefix", defval="", type=input.string, group=g_pc, tooltip="This is your broker's MetaTrader symbol prefix") pc_suffix = input(title="MetaTrader Suffix", defval="", type=input.string, group=g_pc, tooltip="This is your broker's MetaTrader symbol suffix") pc_spread = input(title="Spread", defval=0.5, type=input.float, group=g_pc, tooltip="Enter your average spread for this pair (used for offsetting limit order)") pc_limit = input(title="Use Limit Order?", defval=true, type=input.bool, group=g_pc, tooltip="If true a limit order will be used, if false a market order will be used") // Generate PineConnector alert string var symbol = pc_prefix + syminfo.ticker + pc_suffix var limit = pc_limit ? "limit" : "" pc_entry_alert(direction, sl, tp) => price = pc_limit ? "price=" + tostring(pc_spread) + "," : "" pc_id + "," + direction + limit + "," + symbol + "," + price + "sl=" + tostring(sl) + ",tp=" + tostring(tp) + ",risk=" + tostring(pc_risk) // Get ATR atr = atr(14) // Check ATR Filter atrMinFilter = high - low >= (atrMinFilterSize * atr) or atrMinFilterSize == 0.0 atrMaxFilter = high - low <= (atrMaxFilterSize * atr) or atrMaxFilterSize == 0.0 atrFilter = atrMinFilter and atrMaxFilter // Check EMA Filter ema = emaFilterLength == 0 ? na : ema(close, emaFilterLength) emaLongFilter = emaFilterLength == 0 or (close > ema and not na(ema)) emaShortFilter = emaFilterLength == 0 or (close < ema and not na(ema)) emaTouchFilterLong = (low <= ema and emaTouchFilter and close[1] > ema) or not emaTouchFilter emaTouchFilterShort = (high >= ema and emaTouchFilter and close[1] < ema) or not emaTouchFilter // Check Date Filter dateFilter = time >= startTime and time <= endTime // Check Time Filter isInSession(sess) => na(time(timeframe.period, sess)) == false timeFilter = (useTimeFilter and not isInSession(timeSession)) or not useTimeFilter // Merge Filters longFilters = atrFilter and emaLongFilter and dateFilter and timeFilter and emaTouchFilterLong shortFilters = atrFilter and emaShortFilter and dateFilter and timeFilter and emaTouchFilterShort bgcolor(color=(useTimeFilter and isInSession(timeSession)) or not dateFilter or not atrFilter ? color.new(color.red,70) : na, title="Filter Color") // Calculate the 33.3% fibonacci level for current candle bullFib = (low - high) * fibLevel + high bearFib = (high - low) * fibLevel + low // Determine which price source closes or opens highest/lowest lowestBody = close < open ? close : open highestBody = close > open ? close : open // Determine if we have a valid setup validHammer = lowestBody >= bullFib and close != open and not na(atr) and longFilters validStar = highestBody <= bearFib and close != open and not na(atr) and shortFilters // Check if we have confirmation for our setup validLong = validHammer and strategy.position_size == 0 and barstate.isconfirmed validShort = validStar and strategy.position_size == 0 and barstate.isconfirmed //------------- DETERMINE POSITION SIZE -------------// // Get account inputs var broker = oandaDemo ? "oandapractice" : "oanda" var tradePositionSize = 0.0 var pair = syminfo.basecurrency + "/" + syminfo.currency // Check if our account currency is the same as the base or quote currency (for risk $ conversion purposes) accountSameAsCounterCurrency = accountCurrency == syminfo.currency accountSameAsBaseCurrency = accountCurrency == syminfo.basecurrency // Check if our account currency is neither the base or quote currency (for risk $ conversion purposes) accountNeitherCurrency = not accountSameAsCounterCurrency and not accountSameAsBaseCurrency // Get currency conversion rates if applicable conversionCurrencyPair = accountSameAsCounterCurrency ? syminfo.tickerid : accountNeitherCurrency ? accountCurrency + syminfo.currency : accountCurrency + syminfo.currency conversionCurrencyRate = security(symbol=syminfo.type == "forex" ? conversionCurrencyPair : "AUDUSD", resolution="D", expression=close) // Calculate position size getPositionSize(stopLossSizePoints) => riskAmount = (accountBalance * (riskPerTrade / 100)) * (accountSameAsBaseCurrency or accountNeitherCurrency ? conversionCurrencyRate : 1.0) riskPerPoint = (stopLossSizePoints * syminfo.pointvalue) positionSize = syminfo.type == "forex" ? ((riskAmount / riskPerPoint) / syminfo.mintick) : 0 round(positionSize) // Custom function to convert pips into whole numbers toWhole(number) => return = atr(14) < 1.0 ? (number / syminfo.mintick) / (10 / syminfo.pointvalue) : number return := atr(14) >= 1.0 and atr(14) < 100.0 and syminfo.currency == "JPY" ? return * 100 : return //------------- END POSITION SIZE CODE -------------// // Set up our GTD (good-til-date) order info gtdTime = time + (gtdOrder * 1440 * 60 * 1000) // 86,400,000ms per day gtdYear = year(gtdTime) gtdMonth = month(gtdTime) gtdDay = dayofmonth(gtdTime) gtdString = " dt=" + tostring(gtdYear) + "-" + tostring(gtdMonth) + "-" + tostring(gtdDay) // Calculate our stops & targets stopSize = atr * stopMultiplier longStopPrice = low < low[1] ? low - stopSize : low[1] - stopSize longStopDistance = close - longStopPrice longTargetPrice = close + (longStopDistance * rr) shortStopPrice = high > high[1] ? high + stopSize : high[1] + stopSize shortStopDistance = shortStopPrice - close shortTargetPrice = close - (shortStopDistance * rr) // Save stops & targets for the current trade var tradeStopPrice = 0.0 var tradeTargetPrice = 0.0 // Detect valid long setups & trigger alerts temp_positionSize = getPositionSize(toWhole(longStopDistance) * 10) if validLong tradeStopPrice := longStopPrice tradeTargetPrice := longTargetPrice tradePositionSize := temp_positionSize // Generate PineConnector alert syntax pc_alert = pc_entry_alert("buy", tradeStopPrice, tradeTargetPrice) // Generate AutoView alert syntax av_alert = "e=" + broker + " b=long" + " q=" + tostring(tradePositionSize) + " s=" + pair + " t=" + (limitOrder ? "limit fp=" + tostring(close) : "market") + " fsl=" + tostring(tradeStopPrice) + " ftp=" + tostring(tradeTargetPrice) + (gtdOrder != 0 and limitOrder ? gtdString : "") // Send alert to webhook alert(message=autoview ? av_alert : pineconnector ? pc_alert : "HSS Long Alert", freq=alert.freq_once_per_bar_close) // Detect valid short setups & trigger alerts temp_positionSize := getPositionSize(toWhole(shortStopDistance) * 10) if validShort tradeStopPrice := shortStopPrice tradeTargetPrice := shortTargetPrice tradePositionSize := temp_positionSize // Generate PineConnector alert syntax pc_alert = pc_entry_alert("sell", tradeStopPrice, tradeTargetPrice) // Generate AutoView alert syntax av_alert = "e=" + broker + " b=short" + " q=" + tostring(tradePositionSize) + " s=" + pair + " t=" + (limitOrder ? "limit fp=" + tostring(close) : "market") + " fsl=" + tostring(tradeStopPrice) + " ftp=" + tostring(tradeTargetPrice) + (gtdOrder != 0 and limitOrder ? gtdString : "") // Send alert to webhook alert(message=autoview ? av_alert : pineconnector ? pc_alert : "HSS Short Alert", freq=alert.freq_once_per_bar_close) // Enter trades whenever a valid setup is detected strategy.entry(id="Long", long=strategy.long, when=validLong) strategy.entry(id="Short", long=strategy.short, when=validShort) // Exit trades whenever our stop or target is hit strategy.exit(id="Long Exit", from_entry="Long", limit=tradeTargetPrice, stop=tradeStopPrice, when=strategy.position_size > 0) strategy.exit(id="Short Exit", from_entry="Short", limit=tradeTargetPrice, stop=tradeStopPrice, when=strategy.position_size < 0) // Draw trade data plot(emaFilterLength == 0 ? na : ema, color=close > ema ? color.green : color.red, title="EMA Filter") plot(strategy.position_size != 0 or validLong or validShort ? tradeStopPrice : na, title="Trade Stop Price", color=color.new(color.red,0), style=plot.style_linebr) plot(strategy.position_size != 0 or validLong or validShort ? tradeTargetPrice : na, title="Trade Target Price", color=color.new(color.green,0), style=plot.style_linebr) //plot(strategy.position_size != 0 or validLong or validShort ? tradePositionSize : na, title="Trade Position Size", color=color.purple, style=plot.style_linebr, transp=100) // Draw price action setup arrows plotshape(validLong ? 1 : na, style=shape.triangleup, location=location.belowbar, color=color.green, title="Bullish Setup") plotshape(validShort ? 1 : na, style=shape.triangledown, location=location.abovebar, color=color.red, title="Bearish Setup")
Last Updated: 30th July, 2021
Hi
Does the Ultimate Pullback Indicator work on Tradingview please? Or which ones do?
Thanks
Hey Johnny! All my scripts are designed for TradingView :)
Hi Matthew. What Forex pair and time frame does the Hammer & Stars perform best on, and what are the optimum inputs/settings for that pair and timeframe?
Thank you.
Hey Haz! I typically use this script with the default settings and strictly on the Daily chart on the major FX pairs, but I strongly recommend doing your own backtesting to confirm it works on the broker/timeframe/market you like to trade and that it performs within your risk tolerance and expectations :) it’s not a super profitable strategy due to its simplicity, but it’s a nice easy low-maintenance background system to trade in my experience