Home Trading Strategy Backtest Ichimoku Trading Strategy With Python

Ichimoku Trading Strategy With Python

by Stuart Jamieson

I thought it was about time for another blog post, and this time I have decided to take a look at the “Ichimoku Kinko Hyo” trading strategy, or just “Ichimoku” strategy for short. The Ichimoku system is a Japanese charting and technical analysis method and was published in 1969 by a reporter in Japan.

I thought I would spend this post on the creation of the indicator elements themselves, along with a couple of plotting examples usikng both Matplotlib and then Plotly.

So the Ichimoku “set up” is a technical indicator that is used to gauge momentum, along with future areas of support and resistance and consists of five main components.

1 – Tenkan-Sen line, also called the Conversion Line, represents the midpoint of the last 9 candlesticks. It’s calculated by adding the highest high and the lowest low over the past nine periods and then dividing the result by two.

2 – Kijun-Sen line, also called the Base Line, represents the midpoint of the last 26 candlesticks. It’s calculated in a similar fashion to the Tenkan-Sen line however we use the last 26 candlesticks as mentioned rather than the last 9 – just add the highest high and the lowest low over the past 26 periods and then divide the result by two.

3 – Chiou Span, also called the Lagging Span, lags behind the price (as the name suggests). The Lagging Span is plotted 26 periods back.

4 – Senkou Span A, also called the Leading Span A, represents one of the two Cloud boundaries and it’s the midpoint between the Conversion Line (Tenkan-Sen) and the Base Line (Kijun-Sen). It’s calculated by adding the Tenkan-Sen line and the Kijun-Sen line together and dividng by 2. This value is plotted 26 periods into the future and it’s the faster Cloud boundary.

5 – Senkou Span B, or the Leading Span B, represents the second Cloud boundaries and it’s the midpoint of the last 52 price bars. Add the highest high and the lowest low over the past 52 periods and then divide the result by two. This value is plotted 26 periods into the future and it’s the slower Cloud boundary. (n.b. This was corrected on 13.08.2019 to a 26 period future projection from the original, incorrect figure of 52 periods – many thanks to Duch for bringing that mistake to my attention in the comments section…The code has been amended where necessary, but please note the analysis was not rerun, nor the displayed results updated).

There is an optional sixth element and that is the Chikou Span, representing the closing price and is plotted 26 days back. This line is used to show possible areas of support and resistance.

Now we know the calculations for our indicator elements, let’s get some code down…

Make the necessary imports:

import pandas as pd
from pandas_datareader import data, wb
import matplotlib as mpl
from mpl_finance import candlestick_ohlc
import matplotlib.dates as dates
import datetime
import matplotlib.pyplot as plt
%matplotlib inline

Download some stock data from Yahoo – in this instance I have chosen the Ford stock, but feel free to choose something different if you so wish.

start = datetime.datetime(2017, 1, 1)
end = datetime.datetime(2019, 1, 27)
d=data.DataReader("F", 'yahoo', start, end)
# convert dates to integer values for later use with the 
# matplotlib candlestick charting function (it doesn't accept datetimes)
d['Dates'] = dates.date2num(d.index)
# Tenkan-sen (Conversion Line): (9-period high + 9-period low)/2))
nine_period_high = d['High'].rolling(window= 9).max()
nine_period_low = d['Low'].rolling(window= 9).min()
d['tenkan_sen'] = (nine_period_high + nine_period_low) /2
# Kijun-sen (Base Line): (26-period high + 26-period low)/2))
period26_high = d['High'].rolling(window=26).max()
period26_low = d['Low'].rolling(window=26).min()
d['kijun_sen'] = (period26_high + period26_low) / 2
# Senkou Span A (Leading Span A): (Conversion Line + Base Line)/2))
d['senkou_span_a'] = ((d['tenkan_sen'] + d['kijun_sen']) / 2).shift(26)
# Senkou Span B (Leading Span B): (52-period high + 52-period low)/2))
period52_high = d['High'].rolling(window=52).max()
period52_low = d['Low'].rolling(window=52).min()
d['senkou_span_b'] = ((period52_high + period52_low) / 2).shift(26)
# The most current closing price plotted 26 time periods behind (optional)
d['chikou_span'] = d['Close'].shift(-26)
# create a quick plot of the results to see what we have created
d.drop(['Dates', 'Volume'], axis=1).plot(figsize=(15,8))

Well it’s currently pretty busy looking, we havn’t gotten around to correctly creating a candlestick chart yet – we have just instead plotted all the series in the DataFrame as lines, including our “Open, High, Low, Close” data.

Let’s now work on hopefully cleaning it up a bit by condensing those 4 prices series points into candlsticks and plotting them instead.

# Re-arrange data so that each row contains values of a day: 'Date','Open','High','Low','Close'.
# The 'Date' can't be a "datetime" object as the function wont accept them. That's why we converted 
# our 'Date' column into integer values using the "date2num" function earlier.
quotes = [tuple(x) for x in d[['Dates','Open','High','Low','Close']].values]
# Plot candlestick, along with Ichimoku elements
fig, ax = plt.subplots(figsize=(15,8))
d[['tenkan_sen','kijun_sen','senkou_span_a','senkou_span_b','chikou_span']].plot(ax=ax, linewidth=0.5)
candlestick_ohlc(ax, quotes, width=1.0, colorup='g', colordown='r');

That’s looking a bit better but we have yet to correctly colour/shade the “cloud” area of the chart, and it’s also hard to see what’s going on in some areas as the time scale is quite long, causing the series to “bunch” up somewhat.

I thought the best way around this was to scrap the use of Matplotlib for the moment and move over to Plotly instead. With Plotly we can create HTML based charts with a certain level of interactivity, allowing us to drill down and expand, pan in and out etc to our heart’s content. Then, if there are areas of the plot where it’s hard to make out in detail what is happening, we can just focus in on that area and expand it for a better look.

Again, first port of call is to import the relevant modules we will be using, and this time, as I am using Jupyter Notebooks to help write this post and run the scripts, make a call to the “init_notebook_mode” function to allow me to render the charts straight into my notebook, rather than have them open in a new HTML document.

Below is a basic script to juet plot a basic candlestick chart in Plotly, using our Ford stock price data as inputs. If you are running this in a Jupyter Notebook the chart should just appear in the cell below, directly in the notebook itself. If you aren’t using a Jupyter Notebook you will have to adapt the code below slightly and the chart is probably most easily created by allowing Plotly to output it into a new HTML document – the name of which you can provide to the “plot()” function.

If you aren’t using a Jupyter Notebook, then instead of using “iplot()” as I have, just use “plot()”. If you come across any unsolvable issues, stick a comment in the comments section and I will do my best to help you debug it.

import plotly.plotly as py
import plotly.graph_objs as go
from plotly.offline import init_notebook_mode, plot, iplot
trace = go.Candlestick(x=d.index,
data = [trace]
iplot(data, filename='simple_candlestick')

Now because this site runs on WordPress, it’s more than a little bit difficult to render the interactive HTML chart itself on this webpage (WordPress really doesn’t play nicely with Plotly frames) so instead I have also created the plot in a stand alone HTML file which you can download here:

Just download the file, open it in your browser and that should work fine. Again, ifit doesn’t for any reason, let me know and we can tackle the issue.

So hopefully you should see something similar to the chart shown here…a candlestick chart with a “slider” on the bottom that allows you to span in and out of different time periods.

That’s a good first step, but now we are going to want to add a bit of functionality, along with the rest of the Ichimoku indicators we calculated earlier.

You may notice that the following code looks noticeably different from the code used above in the first plot. The fact is, Plotly allows a number of different approaches to be used and implemented when creating plots. Above, we have created a “plotly.graph_objs” oject and used a “Candlestick” method toget what we want.

But below, I have started by creating a list called “data” which in turn is there to hold our dictionary with all the relevant info that we need to provide. This dixtionary signifies our first “trace” or our “first series to be plotted”.

Next we create an empty dictionary which we then pass, along with the “trace” dictionary we just created above, to another dictionary which represents our chart “Figure”. The empty dictionary we just created will be to hold info regarding the design of our chart layout. The “Figure” requires information from both the “data” list (list of dictionaries holding info regarding the series values to plot), and also from the “layout” dictionary holding settings for our design and layout.

Hope that’s clear as mud…started to get a bit wordy there!

Once we have our “fig” object we can start to set values for both our layout variables and also append new “traces” onto the data list if we want to add more series to plot.

# Set colours for up and down candles
# create list to hold dictionary with data for our first series to plot
# (which is the candlestick element itself)
data = [ dict(
    type = 'candlestick',
    open = d.Open,
    high = d.High,
    low = d.Low,
    close = d.Close,
    x = d.index,
    yaxis = 'y2',
    name = 'F',
    increasing = dict( line = dict( color = INCREASING_COLOR ) ),
    decreasing = dict( line = dict( color = DECREASING_COLOR ) ),
) ]
# Create empty dictionary for later use to hold settings and layout options
# create our main chart "Figure" object which consists of data to plot and layout settings
fig = dict( data=data, layout=layout )
# Assign various seeting and choices - background colour, range selector etc
fig['layout']['plot_bgcolor'] = 'rgb(250, 250, 250)'
fig['layout']['xaxis'] = dict( rangeselector = dict( visible = True ) )
fig['layout']['yaxis'] = dict( domain = [0, 0.2], showticklabels = False )
fig['layout']['yaxis2'] = dict( domain = [0.2, 0.8] )
fig['layout']['legend'] = dict( orientation = 'h', y=0.9, x=0.3, yanchor='bottom' )
fig['layout']['margin'] = dict( t=40, b=40, r=40, l=40 )

# Populate the "rangeselector" object with necessary settings
    visible = True,
    x = 0, y = 0.9,
    bgcolor = 'rgba(150, 200, 250, 0.4)',
    font = dict( size = 13 ),
            label='3 mo',
            label='1 mo',
fig['layout']['xaxis']['rangeselector'] = rangeselector
# Append the Ichimoku elements to the plot
fig['data'].append( dict( x=d['tenkan_sen'].index, y=d['tenkan_sen'], type='scatter', mode='lines', 
                         line = dict( width = 1 ),
                         marker = dict( color = '#33BDFF' ),
                         yaxis = 'y2', name='tenkan_sen' ) )
fig['data'].append( dict( x=d['kijun_sen'].index, y=d['kijun_sen'], type='scatter', mode='lines', 
                         line = dict( width = 1 ),
                         marker = dict( color = '#F1F316' ),
                         yaxis = 'y2', name='kijun_sen' ) )
fig['data'].append( dict( x=d['senkou_span_a'].index, y=d['senkou_span_a'], type='scatter', mode='lines', 
                         line = dict( width = 1 ), 
                         marker = dict( color = '#228B22' ),
                         yaxis = 'y2', name='senkou_span_a' ) )
fig['data'].append( dict( x=d['senkou_span_b'].index, y=d['senkou_span_b'], type='scatter', mode='lines', 
                         line = dict( width = 1 ),fill='tonexty',
                         marker = dict( color = '#FF3342' ),
                         yaxis = 'y2', name='senkou_span_b' ) )
fig['data'].append( dict( x=d['chikou_span'].index, y=d['chikou_span'], type='scatter', mode='lines', 
                         line = dict( width = 1 ),
                         marker = dict( color = '#D105F5' ),
                         yaxis = 'y2', name='chikou_span' ) )

# Set colour list for candlesticks
colors = []
for i in range(len(d.Close)):
    if i != 0:
        if d.Close[i] > d.Close[i-1]:
iplot( fig, filename = 'candlestick-ichimoku' )

Great stuff so it looks like we are much closer to where we want to end up – we have our candlesticks, we have a chart that allows us to zoom in and out and focus on individual areas if need be. The ONLY thing left at this point is to deal with the fact that the Ichimoku “cloud” hasn’t been shaded in quite correctly. It is supposed to alternate between green and red, depending on the which “Senkou Span” (A or B) is above or below the other.

Also as an FYI I have created standalone HTML document with the chart also which can be downloaded from here:

I hate to admit it, but after having searched around the last short while for a solution to the cloud shading colour issue, I am currently coming up short. I think surely there must be a way to do it using Plotly and a combination of “filltonextx” and/or “filltonexty” etc but I havn’t been able to quite get there yet.

So as not to delay the whole post just due to the shading issue, I will publish it now anyway and if anyone fancies a challenge, then feel free to show me up and solve the problem yourselves! 😉

I’ll leave it here for now, and the next post will build on our work here in order to try to formulate a manageable set of systemtic rules using the Ichimoku signals, which we can then code and back test in various ways as usual.

Until next time!!

You may also like


Duch 13 August 2019 - 01:46

Are you sure the Senkou Span B should be shifted 52 period? From what I read, both Span A and Span B are plotted 26 periods into the future…

s666 13 August 2019 - 06:31

Hi Duch, good spot there! It should indeed only be projected forward by 26 periods – I can only assume I was lacking in attention somewhat and factored in a 52 period projection as well as a calculation based on a 52 period logic. Many thanks for bringing that to my attention – I have fixed the code in all the necessary places I believe (although for simplicity I haven’t rerun everything and updated the results…I shall make a note in the body of the post to bring it to people’s attention)

vaibhav 4 September 2019 - 15:48

I am getting following error

ImportError Traceback (most recent call last)
—-> 1 import plotly.plotly as py
2 import plotly.graph_objs as go
4 trace = go.Candlestick(x=d.index,
5 open=d[‘Open’],

c:\users\vm\appdata\local\programs\python\python37-32\lib\site-packages\plotly\plotly__init__.py in
2 from plotly_future import _chart_studio_error
—-> 4 _chart_studio_error(“plotly”)

c:\users\vm\appdata\local\programs\python\python37-32\lib\site-packages_plotly_future___init__.py in _chart_studio_error(submodule)
47 chart_studio.{submodule} module instead.
48 “””.format(
—> 49 submodule=submodule
50 )
51 )

The plotly.plotly module is deprecated,
please install the chart-studio package and use the
chart_studio.plotly module instead.

s666 5 September 2019 - 21:50

Hi there – indeed there have been some changes in the latest version of Plotly – you can follow the instructions here:


Basically you need to install the chart-studio package as follows:

pip install chart_studio

and then upgrade Plotly for completness:

pip install plotly --upgrade

Then hopefully you can just import the necessary modules as follows:

import chart_studio.plotly as py
import plotly.graph_objects as go

Let me know if you have any problems….

LEYDET 9 January 2020 - 14:41

Hi, when i run the code i get an error at this step:

trace = go.Candlestick(x=df.index,
data = [trace]
iplot(data, filename=’simple_candlestick’) <====

which is:

raise ImportError(“iplot can only run inside an IPython Notebook.”)
ImportError: iplot can only run inside an IPython Notebook.

does somebody know how could i solve it?

i’m french please, don’t take care my grammar..

s666 10 January 2020 - 19:33

Hi Leydet – may I ask what environment you are running your code in? It’s clear you’re not using a Jupyter Notebook – may I assume you are just using a standard IDE/text editor style set up?

I am hoping it may be as simple as just changing “iplot” to “plot”

The “i” at the start I believe stands for “interactive” which in turn relates to the interactive nature of Jupyter Notebook and “iPython” terminals etc.

Let me know if that works or not…

Euler Faria 16 March 2020 - 01:16

Hey Stuart,

Awesome work! Thanks for putting this all together.

Just an observation, I think that the chikou_span should be calculate with a positive shift:

df[‘chikou_span’] = df[‘Close’].shift(26)

For example if you would calculate the chikou_span right now you would have to look backwards 26 periods to obtain the last close price, so if with a positve number you can replicate this behavior. I’m going to put an example in what follows with a window of 2 periods:



2017-01-03 12.59
2017-01-04 13.17
2017-01-05 12.77
2017-01-06 12.76
2017-01-09 12.63


2017-01-03 NaN
2017-01-04 NaN
2017-01-05 12.59
2017-01-06 13.17
2017-01-09 12.77

shivraj bathe 12 April 2020 - 23:50


Thank you so much for the tutorial,

I was able to fill the lines between senkau spans with matplotlib
here is the code :

ax.fill_between(d[‘Dates’], d[‘senkou_span_a’], d[‘senkou_span_b’], d[‘senkou_span_a’] >= d[‘senkou_span_b’], facecolor=’green’, interpolate=True)
ax.fill_between(d[‘Dates’], d[‘senkou_span_a’], d[‘senkou_span_b’], d[‘senkou_span_a’] <= d[‘senkou_span_b’], facecolor=’red’, interpolate=True)

Shivraj Bathe

Linda Smith 30 December 2020 - 18:35

This is cool thank you! Going to try implement this since tradingview doesn’t show ichimoku by default

Tahiry 28 March 2021 - 19:27

Hi mate!
Very nice job, what would be the code if we have a .csv file instead of scrapping Yahoo! data? I’m struggling a lot to apply your code.


maexock 30 December 2021 - 20:30


great article but some mentions from my side:

1.) if i want to watch on a stock until now, i have to d.drop(‘chikou_span’, axis=1, inplace=True) before d.dropna(inplace=True) or alternatively we have to use dropna with the subset-attribute, otherwise the NaNs will cause a drop of the newest data (for which chikou isn’t calculated right now)

2.) i need a d.fillna({‘buy’:0, ‘sell’:0}, inplace=True) before d[‘position’] = d[‘buy’] + d[‘sell’] to get strategy_returns for the very first buy/sell signal in given timeframe, otherwise the NaNs in buy/sell will lead to NaNs in position & strategy_returns

3.) i don’t understand, why the stock_returns are handled with np.log but not the strategy_returns when we want to compare those values … could you give me a hint to get the idea behind it?

Thanks again for this TOP article, it was very valuable for me to understand the Ichimoku approach.


chris 18 August 2022 - 19:33

why did you decide to populate the code with such enormous amount of <dict([contents of dict])> ?
wouldn’t it be better to just do <{[key]:[value]}>?
i’m rather very beginner so it confuses me and decreases readability of the code
but anyway thank you very much for you work, will surely read your next posts

Krzysztof Wasylów 21 August 2022 - 19:36

also, i do not understand end of the code:
if i != 0:


i understand it, as setting the candlestick with index 0 with decresing color

Edwin Gutierrez 14 February 2023 - 05:28

How to autoupdate the PLOT with new data?


Leave a Reply

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More