Strategy · Pine Script v5 · On the chart

Ichimoku Kinko Hyo Momentum Strategy

by SynergOps· code credited to Salih Emin (cerebrux)

This long-only trend-following strategy combines Ichimoku Kinko Hyo components with higher timeframe moving average filters.

View on GitHub

GitHub stars7on the repository
Pine versionv5runs as is
Lines of code458
Settings27inputs
LicenceMPL-2.0
Last update1 year ago4 Aug 2025

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new strategy, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart. The trades and results appear in the Strategy Tester tab; adjust the settings with the gear icon.

Source Code

458 lines of Pine Script v5 from Bitcoin Trading/bitcoin Ichimoku momentum strategy.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

bitcoin Ichimoku momentum strategy.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Based on © ZenAndTheArtOfTrading / www.PineScriptMastery.com
// © Salih Emin (cerebrux)
// 
//  Entry Rules:
// - The strategy enters a long position when the current price is above the higher timeframe EMA, and the momentum is strong with Tenkan-Sen crossing above Kijun-Sen
// - The strategy does not enter a long position if the momentum is yellow (caution)
//      -> Caution: True if recent swing minus its current low is greater than 1.5 times the ATR, or if the close price is below the Daily EMA
// - Training stop: Stop is trailed 1 ATR below the highest high or 20% of ATR if momentum is yellow (caution) or if Tenkan-Sen crosses below Kijun-Sen
// - Stop Loss: 10% below the entry price
//@version=5
strategy('Ichimoku Kinko Hyo Momentum Strategy v3.5.0', overlay=true,    initial_capital = 100,    currency=currency.USDT,    default_qty_type = strategy.percent_of_equity,    default_qty_value = 100, commission_type=strategy.commission.percent, commission_value=0.08) // Based on Binance commision on Spot accounts


// Get user input
var const string    G_STRATEGY  = "Strategy Entry Settings"
var const string    G_EXIT      = "Strategy Exit Settings"
var const string    G_FILTER    = "Strategy Filters"
i_HigherTimeframe   = input.timeframe("W", "Higher Timeframe", group=G_STRATEGY, tooltip="Higher timeframe MA reference")
i_EmaLength         = input.int(20, "EMA Length", group=G_STRATEGY, tooltip="Moving average period length")
i_AtrLength         = input.int(5, "ATR Length", group=G_STRATEGY, tooltip="ATR period length")
i_TrailStopSource   = input.source(low, "Trail Profit Source", group=G_EXIT, tooltip="Lowest price source for Trail Profit")
i_TrailStopLookback = input.int(7, "Trail Profit Lookback", group=G_EXIT, tooltip="How many bars to look back for trailing price source")
i_TrailStopMulti    = input.float(0.2, "Trail Profit Ratchet Multiplier", group=G_EXIT, tooltip="When momentum is yellow (caution), shrink ATR distance for TS by this much")
i_ExitOnLastBar     = input.bool(false, "Exit On Last Historical Bar", group=G_EXIT, tooltip="Purely for backtesting and to include the current open position in the stats")
i_StartTime         = input.time(timestamp("09 Jan 2009 13:30 +0000"), "Start Filter", group=G_FILTER, tooltip="BTC release Date: Start date & time to begin searching for setups")
i_EndTime           = input.time(timestamp("1 Jan 2099 19:30 +0000"), "End Filter", group=G_FILTER, tooltip="End date & time to stop searching for setups")

// Risk Management
i_persent_diff = input.int(6, "Stop Loss percentage", group=G_EXIT, tooltip="What persentage whould your Stop Loss be")
stop_loss = strategy.position_avg_price * (1 - i_persent_diff / 100)

// Define custom security function which does not repaint
RequestSecurity_NonRP(_market, _res, _exp) => request.security(_market, _res, _exp[barstate.isrealtime ? 1 : 0])[barstate.isrealtime ? 0 : 1]

// Define date filter check
DateFilter(int start, int end) => time >= start and time <= end

// Get indicator values
float   atrValue    = ta.atr(i_AtrLength)
float   emaValue    = ta.ema(close, i_EmaLength)
float   htfEmaValue = RequestSecurity_NonRP(syminfo.tickerid, i_HigherTimeframe, emaValue)

// Check if trading timeframe price is above higher timeframe EMA
float   marketPrice = close

// Check for bullishness / bearish volatility caution
bool    isBullish   = marketPrice > htfEmaValue
bool    isCaution   = isBullish and (ta.highest(high, 7) - low > (atrValue * 1.5) or marketPrice < emaValue) 

// Set momentum color
color bgCol = color.red
if isBullish[1]
    bgCol := color.green
if isCaution[1]
    bgCol := color.orange

Tenkan_sen = input.int(9, minval=1, title='Tenkan-Sen')
Kijun_sen = input.int(26, minval=1, title='Kijun-Sen')
Senku_spanB = input.int(52, minval=1, title='Senkou-Span B')
Chiku_span_offset = input.int(26, minval=1, title='Chikou-Span Offset')
Kumo_offset = input.int(26, minval=1, title='Kumo Offset')

middle(len) => math.avg(ta.lowest(len), ta.highest(len))

// Ichimoku Components
tenkan = middle(Tenkan_sen)
kijun = middle(Kijun_sen)
senkouA = math.avg(tenkan, kijun)
senkouB = middle(Senku_spanB)

ss_high = math.max(senkouA[Kumo_offset - 1], senkouB[Kumo_offset - 1])
ss_low = math.min(senkouA[Kumo_offset - 1], senkouB[Kumo_offset - 1])
tk_cross_bull = tenkan > kijun

// Conditions for exit
tk_cross_bear = tenkan < kijun

// Handle strategy entry, and reset trailing stop
var float trailStop = na
if tk_cross_bull and isBullish and strategy.position_size == 0 and DateFilter(i_StartTime, i_EndTime) and not isCaution
    strategy.entry(id = 'Buy', direction = strategy.long, comment = "Enter on Open price: $"+str.tostring(close), alert_message = 'Open Long Position')
    alert('Long entry signal at $' + str.tostring(close), alert.freq_once_per_bar_close)
    trailStop := na

// Update Trail Profit
float temp_trailStop = ta.highest(i_TrailStopSource, i_TrailStopLookback) - (isCaution[1] ? atrValue * i_TrailStopMulti : atrValue)
if strategy.position_size > 0
    if temp_trailStop > trailStop or na(trailStop)
        trailStop := temp_trailStop

// Handle strategy exit
if strategy.position_size > 0 and ((close < trailStop or close < htfEmaValue and tk_cross_bear) and barstate.isconfirmed) or (i_ExitOnLastBar and barstate.islastconfirmedhistory)
    strategy.close(id = 'Buy', comment = 'Trailing Hit - Exit on Open price: $'+str.tostring(close) ,alert_message = 'Close Long Position')
    alert('Traling Take Profit hit. Exit your position at $' + str.tostring(close), alert.freq_once_per_bar_close)
    
if strategy.position_size > 0 and (close < stop_loss) and barstate.isconfirmed
    strategy.exit('Stop Loss', 'Buy', stop = stop_loss)
    alert('Stop Loss Hit at $' + str.tostring(close), alert.freq_once_per_bar_close)

// Draw Trail Profit, HTF EMA and color-coded momentum indicator
plotshape(true, color=bgCol, style=shape.square, location=location.bottom, size=size.auto, title="Momentum Strength")
plot(htfEmaValue, color=close > htfEmaValue ? color.green : color.red, linewidth=2, title="HTF EMA", display=display.none)
plot(emaValue, color=close > emaValue ? color.green : color.red, linewidth=1, title="CTF EMA", display=display.none)
plot(strategy.position_size[1] > 0 ? trailStop : na, style=plot.style_steplinebr, color=color.yellow, title="Trailing Take Profit")
plot(strategy.position_size[1] > 0 ? stop_loss : na, style=plot.style_steplinebr, color=color.rgb(250, 0, 0), title="Stop Loss")

// Plot Ichimoku Kinko Hyo
plot(tenkan, color=color.new(#04ff19, 0), title='Tenkan-Sen',display=display.none)
plot(kijun, color=color.new(#991515, 0), title='Kijun-Sen')
plot(close, offset=-Chiku_span_offset + 1, color=color.new(#459915, 0), title='Chikou-Span', display=display.none)
senkouAplot = plot(senkouA, offset=Kumo_offset - 1, color=color.new(color.green, 0), title='Senkou-Span A', display=display.none)
senkouBplot = plot(senkouB, offset=Kumo_offset - 1, color=color.new(color.red, 0), title='Senkou-Span B', display=display.none)
fill(senkouAplot, senkouBplot, color = senkouA > senkouB ? color.rgb(67, 160, 71, 90) : color.rgb(244, 67, 54, 90), title='Cloud color', display=display.none)

//-------------------------------------------------------------------------//

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// MONTHLY TABLE based on the script written by QuantNomad - Copy & Paste code from here down into your strategy script /{
// Original Script: https://tradingview.com/script/kzp8e4X3-Monthly-Returns-in-PineScript-Strategies/                   //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
var string GROUP_PERFORMANCE_TABLE = "Monthly Performance Table"
mptable_on = input.bool(title="Turn On |", defval=true, display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Toggles")
mptable_debug = input.bool(title="Debug Mode |", defval=false, display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Toggles")
mptable_precision = input.int(title="Decimal Precision", defval=2, minval=1, display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Toggles", tooltip="Decimal precision of each cell")
mptable_titleColor = input.color(title="Title Cell Color", defval=#cccccc, display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Colors")
mptable_titleTextColor = input.color(title="Title Text Color", defval=#363a45, display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Colors")
mptable_textColor = input.color(title="Cell Text Color", defval=color.white, display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Colors")
mptable_ProfitColor = input.color(title="Year Profit Color", defval=color.new(color.green, 50), display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Colors")
mptable_LossColor = input.color(title="Year Loss Color", defval=color.new(color.red, 50), display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Colors")
mptable_BreakEvenColor = input.color(title="Year B/E Color", defval=color.new(color.white, 50), display=display.none, group=GROUP_PERFORMANCE_TABLE, inline="MPT_Colors")
mptable_pageNumber = input.int(title="Page Number", defval=1, minval=1, step=1, maxval=10, display=display.none, group=GROUP_PERFORMANCE_TABLE, tooltip="Which page of results to display") - 1 // -1 is for corection for arrays. Aaray index start with 0 
mptable_pageSize = input.int(title="Page Size (Rows)", defval=20, minval=1, display=display.none, group=GROUP_PERFORMANCE_TABLE, tooltip="How many rows to display") - 1  // 9 is used to show 10 rows of data due to array start from 0
mptable_tableTextSize = input.string(title="Text Size", defval="Small", options=["Auto",  "Huge",  "Large", "Normal", "Small", "Tiny"], display=display.none, group=GROUP_PERFORMANCE_TABLE)

// Custom function for getting table text sized based on user input
table_text_size(_size) =>
    switch _size
        "Auto"   => size.auto   
        "Huge"   => size.huge   
        "Large"  => size.large  
        "Normal" => size.normal 
        "Small"  => size.small
        => size.tiny
tableTextSize = table_text_size(mptable_tableTextSize)

// Custom function for getting decimal precision based on given number 
// (eg. if number is > 0 but < 0.05 or < 0 and > -0.05, set precision to 3 to avoid rounding to 0 which is misleading)
GetRoundingPrecision(float num) =>
    if (num > 0 and num < 0.05) or (num < 0 and num > -0.05)
        3
    else
        mptable_precision

// Define an open trade's cost (used to calculate commission cost)
type TradeCost
    int entryTime
    float entryPrice
    float cost

// Define a monthly/yearly return type
type StrategyReturn
    float profit
    float drawdown
    float peak
    int timestamp

// Store accumulated P&L values
var float accumulatedMonthlyPL = 0
var float accumulatedYearlyPL = 0
var float bestAccumulatedMonthlyPL = 0
var float bestAccumulatedYearlyPL = 0

// Store drawdown values
var float equityPeak = strategy.initial_capital
var float yearlyEquityHigh = 0
var float currentYearlyDrawdown = 0
var float yearlyMaxDrawdown = 0
var float worstDrawdown = 0
var float monthlyEquityHigh = 0
var float currentMonthlyDrawdown = 0
var float monthlyMaxDrawdown = 0
var int currentDrawdownBars = 0
var int maxDrawdownBars = 0

// Store stat arrays
var totalDrawdownBars = array.new<int>(0)
var totalDrawdowns = array.new<float>(0)

// Store long & short trade count
var int totalBreakEvenTrades = 0
var int totalLongTrades = 0
var int totalLongTradeWins = 0
var int totalShortTrades = 0
var int totalShortTradeWins = 0

// Store open trade commission costs in array
var costOfOpenTrades = array.new<TradeCost>(0)

// Detect opened trade and save cost of trade (I tried many methods to get my numbers to match the Cumulative Profit list in the Strategy Tester, no idea why, but none of them worked without doing this)
if strategy.opentrades != strategy.opentrades[1] and strategy.closedtrades == strategy.closedtrades[1]
    costOfTrade = strategy.grossloss - strategy.grossloss[1]
    costOfOpenTrades.push(TradeCost.new(strategy.opentrades.entry_time(strategy.opentrades - 1), strategy.opentrades.entry_price(strategy.opentrades - 1), costOfTrade))

// Detect a closed trade
// TV Documentation: Trade List's Cumulative Profit % Formula = TradeProfit / (InitialCapital + Cumulative Profit of the previous trades) * 100%
if strategy.closedtrades != strategy.closedtrades[1]
    
    // Retrieve trade cost for the closed trade
    float tradeCost = 0
    int removeIdx = -1
    if costOfOpenTrades.size() > 0
        for i = 0 to costOfOpenTrades.size() - 1
            TradeCost tc = costOfOpenTrades.get(i)
            if tc.entryTime == strategy.closedtrades.entry_time(strategy.closedtrades - 1) and tc.entryPrice == strategy.closedtrades.entry_price(strategy.closedtrades - 1)
                tradeCost := tc.cost
                removeIdx := i 
                break
    
    // Remove cost
    if removeIdx != -1
        costOfOpenTrades.remove(removeIdx)

    // Calculate equity before trade closed (strategy.equity will not do, because it changes bar-by-bar based on open P&L not realized P&L)
    float preEquity = strategy.initial_capital + strategy.netprofit[1]

    // Calculate P&L + cost of this trade
    float profitLoss = 0 
    if strategy.losstrades > strategy.losstrades[1]
        profitLoss := (strategy.grossloss - (strategy.grossloss[1] - tradeCost)) * -1
    else
        profitLoss := strategy.grossprofit - strategy.grossprofit[1]
    
    // Check if this was a long or short trade and if it won or lost
    if strategy.position_size[1] > 0
        totalLongTrades := totalLongTrades + 1
        if profitLoss > 0
            totalLongTradeWins := totalLongTradeWins + 1
    else if strategy.position_size[1] < 0
        totalShortTrades := totalShortTrades + 1
        if profitLoss > 0
            totalShortTradeWins := totalShortTradeWins + 1
        
    // Check if the trade broke even
    if profitLoss == 0
        totalBreakEvenTrades := totalBreakEvenTrades + 1

    // Calculate cumulative profit % for this trade 
    float cumulativeProfitPercent = (profitLoss / preEquity) * 100

    // Store highest peak value of equity (we can now use strategy.equity since equity has updated to realized P&L on this bar)
    if strategy.equity > equityPeak
        equityPeak := strategy.equity

    // Calculate total system drawdown %
    float equityDD = ((strategy.equity - equityPeak) / equityPeak) * 100
    if equityDD < worstDrawdown
        worstDrawdown := equityDD

    // Store accumulated monthly + yearly P&L
    accumulatedMonthlyPL := cumulativeProfitPercent + accumulatedMonthlyPL[1]
    accumulatedYearlyPL := accumulatedYearlyPL + cumulativeProfitPercent

    // Save max favourable excursion for this month (ie. peak return as %)
    if accumulatedMonthlyPL > bestAccumulatedMonthlyPL
        bestAccumulatedMonthlyPL := accumulatedMonthlyPL

    // Save max favourable excursion for this year (ie. peak return as %)
    if accumulatedYearlyPL > bestAccumulatedYearlyPL
        bestAccumulatedYearlyPL := accumulatedYearlyPL

    // Track max equity high over current year for max yearly drawdown calculation
    if accumulatedYearlyPL > yearlyEquityHigh
        yearlyEquityHigh := accumulatedYearlyPL
    
    // Check if our yearly realized equity high minus current realized equity exceeds our stored max drawdown for the year, update if necessary, and save worst drawdown
    if accumulatedYearlyPL - yearlyEquityHigh < 0
        currentYearlyDrawdown := accumulatedYearlyPL - yearlyEquityHigh
        if currentYearlyDrawdown < yearlyMaxDrawdown
            yearlyMaxDrawdown := currentYearlyDrawdown
        currentDrawdownBars := currentDrawdownBars + 1
    
    // Track max equity high over current month for max monthly drawdown calculation
    if accumulatedMonthlyPL > monthlyEquityHigh
        monthlyEquityHigh := accumulatedMonthlyPL
    
    // Check if our monthly realized equity high minus current realized equity exceeds our stored max drawdown for the month, update if necessary, and save worst drawdown
    if accumulatedMonthlyPL - monthlyEquityHigh < 0
        currentMonthlyDrawdown := accumulatedMonthlyPL - monthlyEquityHigh
        if currentMonthlyDrawdown < monthlyMaxDrawdown
            monthlyMaxDrawdown := currentMonthlyDrawdown
    
    // Debug label
    if mptable_debug
        string debugTip = "Equity = $" + str.tostring(strategy.equity, "#.##") + 
         "\nP&L=" + str.tostring(cumulativeProfitPercent) + "%" +
         "\nAccumMonthlyP&L=" + str.tostring(math.round(accumulatedMonthlyPL, GetRoundingPrecision(accumulatedMonthlyPL))) + "%" +
         "\nAccumYearlyP&L=" + str.tostring(math.round(accumulatedYearlyPL, GetRoundingPrecision(accumulatedYearlyPL))) + "%" +
         "\nMonthlyMaxDD=" + str.tostring(math.round(monthlyMaxDrawdown, GetRoundingPrecision(monthlyMaxDrawdown))) + "%" +
         "\nYearlyMaxDD=" + str.tostring(math.round(yearlyMaxDrawdown, GetRoundingPrecision(yearlyMaxDrawdown))) + "%" +
         "\nTotalMaxDD=" + str.tostring(math.round(worstDrawdown, GetRoundingPrecision(worstDrawdown))) + "%" +
         "\nCurrentDDBars=" + str.tostring(currentDrawdownBars) +
         "\nMaxDDBars=" + str.tostring(maxDrawdownBars) +
         "\nTotalBreakEven=" + str.tostring(totalBreakEvenTrades) +
         "\nTotalLongs=" + str.tostring(totalLongTrades) +
         "\nTotalLongWins=" + str.tostring(totalLongTradeWins) +
         "\nTotalShorts=" + str.tostring(totalShortTrades) +
         "\nTotalShortWins=" + str.tostring(totalShortTradeWins)
        label.new(bar_index, high + (high * 0.01), "P&L " + str.tostring(math.round(cumulativeProfitPercent, GetRoundingPrecision(cumulativeProfitPercent))) + "%", tooltip=debugTip, textcolor=color.white)

// Calculate drawdown since last equity high (NOT max drawdown, just the current max DD since we were out of DD)
float t_equityDD = ((strategy.equity - equityPeak) / equityPeak) * 100
var float currentMaxDrawdownSinceLast = 0

// Update Max Drawdown bar count and current DD if equity is under water, check isconfirmed to prevent double-counting bars with recalc_on_order_fills on
if strategy.equity < equityPeak and barstate.isconfirmed
    currentDrawdownBars := currentDrawdownBars + 1
    if currentDrawdownBars > maxDrawdownBars
        maxDrawdownBars := currentDrawdownBars
    if t_equityDD < currentMaxDrawdownSinceLast
        currentMaxDrawdownSinceLast := t_equityDD
else
    if currentDrawdownBars > 0
        totalDrawdownBars.push(currentDrawdownBars)
        totalDrawdowns.push(currentMaxDrawdownSinceLast)
    currentDrawdownBars := 0
    currentMaxDrawdownSinceLast := 0

// Prepare arrays to store Yearly and Monthly P&Ls
var monthlyReturns = array.new<StrategyReturn>(0)
var yearlyReturns = array.new<StrategyReturn>(0)

var bool firstEntryTime = false
// Retrieve entry time of initial entry in open position
if not firstEntryTime and strategy.opentrades.entry_time(0)
    firstEntryTime := true

// Detect new month and year
new_month = month(time) != month(time[1])
new_year = year(time) != year(time[1])

// Detect a new month and store its return profile
if not barstate.isfirst and new_month and firstEntryTime or barstate.islastconfirmedhistory
    StrategyReturn mr = StrategyReturn.new(accumulatedMonthlyPL, monthlyMaxDrawdown, bestAccumulatedMonthlyPL, time[1]) // time)
    monthlyReturns.push(mr)
    accumulatedMonthlyPL := 0
    monthlyMaxDrawdown := 0
    monthlyEquityHigh := 0
    currentMonthlyDrawdown := 0
    bestAccumulatedMonthlyPL := 0

    // Detect a new year and reset tracking variables
if not barstate.isfirst and new_year and firstEntryTime or barstate.islastconfirmedhistory
    StrategyReturn yr = StrategyReturn.new(accumulatedYearlyPL, yearlyMaxDrawdown, bestAccumulatedYearlyPL, time[1])
    yearlyReturns.push(yr)
    accumulatedYearlyPL := 0
    yearlyMaxDrawdown := 0
    yearlyEquityHigh := 0
    currentYearlyDrawdown := 0
    bestAccumulatedYearlyPL := 0

// DEBUG code
bgcolor(mptable_debug and new_month ? color.lime : na, title="New Month", display=display.none)
bgcolor(mptable_debug and new_year ? color.red : na, title="New Year", display=display.none)
// END DEBUG CODE

// Define Monthly P&L Table    
var table performance_table = table(na)

//Adjust mptable_pageSize if the years are less than the mptable_pageSize
if yearlyReturns.size() < mptable_pageSize
    mptable_pageSize := yearlyReturns.size()

// Caluclate the start and end of page to display
startIndex = math.max(math.min(yearlyReturns.size() - 1, yearlyReturns.size() - 1 - (mptable_pageSize + 1) * mptable_pageNumber), mptable_pageSize - 1)
endIndex = math.max(startIndex - mptable_pageSize, 0)
mptable_pageSize := endIndex <= mptable_pageSize ? endIndex : mptable_pageSize

// If this is the last bar on our chart, display the performance table
var int EXTRA_STAT_ROWS = 5 // This ensures table includes enough rows for CAGR etc
if mptable_on and monthlyReturns.size() > 0 and barstate.islastconfirmedhistory

    // Create table (100 rows = 100 years of data, should be plenty for all markets!)
    performance_table := table.new(position.bottom_right, columns=17, rows=yearlyReturns.size() + EXTRA_STAT_ROWS, border_width=1)

    // Set column headers
    performance_table.cell(0, 0, "Year", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(1, 0, "Jan", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(2, 0, "Feb", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(3, 0, "Mar", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(4, 0, "Apr", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(5, 0, "May", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(6, 0, "Jun", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(7, 0, "Jul", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(8, 0, "Aug", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(9, 0, "Sep", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(10, 0, "Oct", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(11, 0, "Nov", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(12, 0, "Dec", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(13, 0, "TOTAL", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
    performance_table.cell(14, 0, "MaxDD", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)

    // Set yearly values
    for year_index = startIndex to yearlyReturns.size() == 0 ? na : endIndex
        
        // Get yearly return for this loop, set year number in first column, determine color of cell
        StrategyReturn yearlyReturn = yearlyReturns.get(year_index)

        // Set year title and determine color
        performance_table.cell(0, year_index + 1, str.tostring(year(yearlyReturn.timestamp)), bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_size=tableTextSize)
        color y_color = yearlyReturn.profit > 0 ? mptable_ProfitColor : yearlyReturn.profit == 0 ? mptable_BreakEvenColor : mptable_LossColor

        // Populate yearly cell values
        string yearlyTip = "MaxDD: " + str.tostring(math.round(yearlyReturn.drawdown, GetRoundingPrecision(yearlyReturn.drawdown))) + "%" +
             "\nMFE: " + str.tostring(math.round(yearlyReturn.peak, GetRoundingPrecision(yearlyReturn.peak))) + "%"
        performance_table.cell(13, year_index + 1, (yearlyReturn.profit > 0 ? "+" : "") + str.tostring(math.round(yearlyReturn.profit, GetRoundingPrecision(yearlyReturn.profit))) + "%", bgcolor=y_color, text_color=color.white, text_size=tableTextSize, tooltip=yearlyTip)
        performance_table.cell(14, year_index + 1, str.tostring(math.round(yearlyReturn.drawdown, GetRoundingPrecision(yearlyReturn.drawdown))) + "%", bgcolor=mptable_BreakEvenColor, text_color=color.white, text_size=tableTextSize)

        // Set monthly values
        for month_index = 0 to monthlyReturns.size() - 1

            // Get monthly return for this loop, get current year for this loop, then calculate the corresponding table column and row
            StrategyReturn monthlyReturn = monthlyReturns.get(month_index)
            int yearOfMonth = year(monthlyReturn.timestamp)
            int monthCol = month(monthlyReturn.timestamp)
            
            // populate monthly profit only if the years of the yearly return match with the monthly return. 
            if yearOfMonth == year(yearlyReturn.timestamp)
                 // Determine color for monthly P&L
                color m_color = monthlyReturn.profit > 0 ? color.new(mptable_ProfitColor, color.t(mptable_ProfitColor) + 20) : monthlyReturn.profit == 0 ? color.new(mptable_BreakEvenColor, color.t(mptable_BreakEvenColor) + 20) : color.new(mptable_LossColor, color.t(mptable_LossColor) + 20)
                
                // Set monthly P&L cell
                string monthlyTip = "MaxDD: " + str.tostring(math.round(monthlyReturn.drawdown, GetRoundingPrecision(monthlyReturn.drawdown))) + "%" +
                     "\nMFE: " + str.tostring(math.round(monthlyReturn.peak, GetRoundingPrecision(monthlyReturn.peak))) + "%"
                performance_table.cell(monthCol, year_index + 1, str.tostring(math.round(monthlyReturn.profit, GetRoundingPrecision(monthlyReturn.profit))), bgcolor=m_color, text_color=color.white, text_size=tableTextSize, tooltip=monthlyTip)

    float percentReturn = (strategy.netprofit / strategy.initial_capital) * 100
    float cagr = (math.pow((strategy.netprofit + strategy.initial_capital) / strategy.initial_capital, 1 / yearlyReturns.size()) - 1) * 100
    float mar = cagr / math.abs(worstDrawdown)
    lastMonthRowIndex = startIndex < 5 ? 5 : startIndex

    // Populate table data
    float totalWinRate = (strategy.wintrades / strategy.closedtrades) * 100
    float longWinRate = nz((totalLongTradeWins / totalLongTrades) * 100)
    float shortWinRate = nz((totalShortTradeWins / totalShortTrades) * 100)
    string returnTip = "Based on a total of " + str.tostring(strategy.closedtrades) + " trades" +
         "\nWin Rate = " + str.tostring(math.round(totalWinRate, GetRoundingPrecision(totalWinRate))) + "%" +
         "\nLong Trades = " + str.tostring(totalLongTrades) + " (Win " + str.tostring(math.round(longWinRate, GetRoundingPrecision(longWinRate))) + "%)" +
         "\nShort Trades = " + str.tostring(totalShortTrades) + " (Win " + str.tostring(math.round(shortWinRate, GetRoundingPrecision(shortWinRate))) + "%)"
    performance_table.cell(15, lastMonthRowIndex, "Return: " + (percentReturn > 0 ? "+" : "") + str.tostring(math.round(percentReturn, GetRoundingPrecision(percentReturn))) + "%", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_font_family=font.family_monospace, text_size=tableTextSize, tooltip=returnTip)
    performance_table.cell(15, lastMonthRowIndex - 1, "MAR: " + str.tostring(mar, "#.##"), bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_font_family=font.family_monospace, text_size=tableTextSize, tooltip="Measure of return adjusted for risk: CAGR divided by Max Drawdown. Indicates how comfortable the system might be to trade. Higher than 0.5 is ideal, 1.0 and above is very good, and anything 3.0 or above should be considered suspicious.")
    performance_table.cell(15, lastMonthRowIndex - 2, "DD Bars: " + str.tostring(maxDrawdownBars), bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_font_family=font.family_monospace, text_size=tableTextSize, tooltip="Average Drawdown Bars: " + str.tostring(totalDrawdownBars.avg(), "#.#") + "\n\nThis is how many bars it took to recover the longest drawdown (note: this is different to the MAX drawdown, and represents time drawdown)")
    performance_table.cell(15, lastMonthRowIndex - 3, "MaxDD: " + str.tostring(math.round(worstDrawdown, GetRoundingPrecision(worstDrawdown))) + "%", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_font_family=font.family_monospace, text_size=tableTextSize, tooltip="Average Drawdown: " + str.tostring(totalDrawdowns.avg(), "#.##") + "%\n\nThis number is different to the Strategy Tester because this number is based on closed trade equity while the Tester's MaxDD is based on open equity.")
    performance_table.cell(15, lastMonthRowIndex - 4, "CAGR: " + (cagr > 0 ? "+" : "") + str.tostring(math.round(cagr, GetRoundingPrecision(cagr))) + "%", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_font_family=font.family_monospace, text_size=tableTextSize, tooltip="Compounded annual growth rate")
    performance_table.cell(15, lastMonthRowIndex - 5, "REALIZED P&L", bgcolor=mptable_titleColor, text_color=mptable_titleTextColor, text_font_family=font.family_monospace, text_size=tableTextSize, tooltip="These numbers are based on Realized equity (closed trades)")
// } END MONTHLY TABLE

/////////// END OF PERFORMANCE TABLE CODE. ///////////

Overview

Ichimoku Kinko Hyo Momentum Strategy triggers long entries when price trades above a higher timeframe EMA and a bullish Tenkan-Sen cross above Kijun-Sen occurs under favorable momentum conditions. Positions are protected by a configurable percentage stop loss and managed with an ATR-based dynamic trailing stop loss. The strategy plots directly on the main chart overlay and includes a performance statistics table.

Details

Published by

SynergOps on GitHub

Code credit

Salih Emin (cerebrux), as written in the script's header

Type

Strategy, Pine Script v5

Draws

On the price chart

Category

Trend, Oscillators & momentum, Moving averages, Screeners & dashboards

Markets

Crypto

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Higher TimeframetimeframeWi_HigherTimeframe
EMA Lengthint20i_EmaLength
ATR Lengthint5i_AtrLength
Trail Profit Sourcesourcelowi_TrailStopSource
Trail Profit Lookbackint7i_TrailStopLookback
Trail Profit Ratchet Multiplierfloat0.2i_TrailStopMulti
Exit On Last Historical Barboolfalsei_ExitOnLastBar
Start Filtertimetimestamp("09 Jan 2009 13:30 +0000")i_StartTime
End Filtertimetimestamp("1 Jan 2099 19:30 +0000")i_EndTime
Stop Loss percentageint6i_persent_diff
Tenkan-Senint9Tenkan_sen
Kijun-Senint26Kijun_sen
Show all 27 settings
SettingTypeDefaultVariable
Senkou-Span Bint52Senku_spanB
Chikou-Span Offsetint26Chiku_span_offset
Kumo Offsetint26Kumo_offset
Turn On |booltruemptable_on
Debug Mode |boolfalsemptable_debug
Decimal Precisionint2mptable_precision
Title Cell Colorcolor#ccccccmptable_titleColor
Title Text Colorcolor#363a45mptable_titleTextColor
Cell Text Colorcolorcolor.whitemptable_textColor
Year Profit Colorcolorcolor.new(color.green, 50)mptable_ProfitColor
Year Loss Colorcolorcolor.new(color.red, 50)mptable_LossColor
Year B/E Colorcolorcolor.new(color.white, 50)mptable_BreakEvenColor
Page Numberint1mptable_pageNumber
Page Size (Rows)int20mptable_pageSize
Text SizestringSmallmptable_tableTextSize
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

330 stars

2 EMA/SMA + RSI Strategy [Alorse] combines three configurable moving averages with a short-period Relative Strength Index filter. It plots fast, slow, and trend moving averages on the price chart. Long entries trigger when the fast moving average is above the

Strategy v4 TrendOscillators & momentum
Updated 2 months ago · None stated GitHub repository of 2 EMA/SMA + RSI Strategy [Alorse]

Acrypto - Weighted Strategy

by AlbertoCuadra
318 stars

This strategy plots on the price chart and opens long or short positions when a weighted score from MACD, Stochastic RSI, RSI, Supertrend and a moving-average crossover exceeds a user-set trigger. Each component has its own weight and candle delay. Entries can

Strategy v5 TrendOscillators & momentum
Updated 3 years ago · Custom licence GitHub repository of Acrypto - Weighted Strategy

This strategy combines Arnaud Legoux Moving Average percentage price change calculations with an adaptive Gaussian Moving Average to generate trend-following signals. Long and short entries are triggered when the ALMA change calculation crosses above or below

Strategy v5 Moving averagesOscillators & momentum
Updated 6 weeks ago · None stated GitHub repository of ESM2024 - ALMA Smoothed Gaussian Moving Average
218 stars

This strategy generates trading signals based on a selectable momentum engine, supporting MACD, RSI, Stochastic, or Rate of Change. Entries trigger on signal crosses, zero-line crosses, or threshold crosses, with an optional moving average trend filter. Risk m

Strategy v6 Oscillators & momentumTrend
Updated 3 months ago · None stated GitHub repository of Momentum Strategy Template

255 EMA Strategy

by hasnocool
106 stars

255 EMA Strategy combines an exponential moving average with Average True Range bands and standard MACD crossovers to generate entry signals. Drawn directly on the price chart, it plots the central 255 EMA along with upper and lower volatility envelopes. Trade

Strategy v4 Moving averagesOscillators & momentum
Updated 2 years ago · GPL-3.0 GitHub repository of 255 EMA Strategy
104 stars

This overlay strategy combines a machine-learning distance model with a MACD oscillator to open trades on the price chart. It compares recent history using cosine similarity or Euclidean and Lorentzian distance, then filters direction with selectable moving-av

Strategy v5 AI / machine learningOscillators & momentum
Updated 3 years ago · MIT GitHub repository of MLCEL and MACD Strategy

More strategys

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers