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.

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.


Pine Script Basics Course Pine Script Mastery Course

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


Free Premium Charts!

5 3 votes
Article Rating
Subscribe
Notify of
guest
4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Johnny
Johnny
1 year ago

Hi
Does the Ultimate Pullback Indicator work on Tradingview please? Or which ones do?
Thanks

Haz
Haz
1 year ago

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.