Python/Pandas 计算 Ichimoku 图表组件

Python/Pandas calculate Ichimoku chart components

我有 Pandas DataFrame 对象,其中包含每日股票数据的日期、开盘价、收盘价、最低价和最高价。我想计算 Ichimoku 图表的组成部分。我可以使用以下代码获取我的数据:

high_prices = data['High']
close_prices = data['Close']
low_prices = data['Low']
dates = data['Date']  # contains datetime objects

我需要计算以下系列(Ichimoku 称之为 Tenkan-Sen 线):

(9 期高点 + 9 期低点)/ 2

我找到了 R 语言的解决方案 here,但我很难将其翻译成 Python/Pandas 代码。

Ichimoku 图表包含更多组件,但是当我知道如何在 Pandas 中计算 Tenkan-Sen 线时,我将能够计算所有这些(我将共享代码)。

我不是金融专家或绘图专家,但以下显示了示例金融数据以及如何使用 rolling_max and rolling_min

In [60]:

import pandas.io.data as web
import datetime
start = datetime.datetime(2010, 1, 1)
end = datetime.datetime(2013, 1, 27)
data=web.DataReader("F", 'yahoo', start, end)
high_prices = data['High']
close_prices = data['Close']
low_prices = data['Low']
dates = data.index
nine_period_high = df['High'].rolling(window=9).max()
nine_period_low = df['Low'].rolling(window=9).min()
ichimoku = (nine_period_high + nine_period_low) /2
ichimoku
Out[60]:
Date
2010-01-04       NaN
2010-01-05       NaN
2010-01-06       NaN
2010-01-07       NaN
2010-01-08       NaN
2010-01-11       NaN
2010-01-12       NaN
2010-01-13       NaN
2010-01-14    11.095
2010-01-15    11.270
2010-01-19    11.635
2010-01-20    11.730
2010-01-21    11.575
2010-01-22    11.275
2010-01-25    11.220
...
2013-01-04    12.585
2013-01-07    12.685
2013-01-08    13.005
2013-01-09    13.030
2013-01-10    13.230
2013-01-11    13.415
2013-01-14    13.540
2013-01-15    13.675
2013-01-16    13.750
2013-01-17    13.750
2013-01-18    13.750
2013-01-22    13.845
2013-01-23    13.990
2013-01-24    14.045
2013-01-25    13.970
Length: 771

调用 data[['High', 'Low', 'Close', 'ichimoku']].plot() 结果如下图:

更新

在@PedroLobito 的评论指出 incomplete/incorrect 公式后,我采用了@chilliq 的答案并针对 pandas 0.16.1 及更高版本进行了修改:

import pandas as pd
from pandas_datareader import data, wb
import datetime
start = datetime.datetime(2010, 1, 1)
end = datetime.datetime(2013, 1, 27)
d=data.DataReader("F", 'yahoo', start, end)
high_prices = d['High']
close_prices = d['Close']
low_prices = d['Low']
dates = d.index
nine_period_high =  df['High'].rolling(window=9).max()
nine_period_low = df['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 = high_prices.rolling(window=26).max()
period26_low = low_prices.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 = high_prices.rolling(window=52).max()
period52_low = low_prices.rolling(window=52).min()
d['senkou_span_b'] = ((period52_high + period52_low) / 2).shift(26)

# The most current closing price plotted 22 time periods behind (optional)
d['chikou_span'] = close_prices.shift(-22) # 22 according to investopedia
d.plot()

以下情节的结果,不清楚,因为如前所述,我不是金融专家:

感谢前面的回答,有代码:

# Tenkan-sen (Conversion Line): (9-period high + 9-period low)/2))
period9_high = pd.rolling_max(high_prices, window=9)
period9_low = pd.rolling_min(low_prices, window=9)
tenkan_sen = (period9_high + period9_low) / 2

# Kijun-sen (Base Line): (26-period high + 26-period low)/2))
period26_high = pd.rolling_max(high_prices, window=26)
period26_low = pd.rolling_min(low_prices, window=26)
kijun_sen = (period26_high + period26_low) / 2

# Senkou Span A (Leading Span A): (Conversion Line + Base Line)/2))
senkou_span_a = ((tenkan_sen + kijun_sen) / 2).shift(26)

# Senkou Span B (Leading Span B): (52-period high + 52-period low)/2))
period52_high = pd.rolling_max(high_prices, window=52)
period52_low = pd.rolling_min(low_prices, window=52)
senkou_span_b = ((period52_high + period52_low) / 2).shift(26)

# The most current closing price plotted 22 time periods behind (optional)
chikou_span = close_prices.shift(-22) # 22 according to investopedia

我希望编写 Ichimoku 书籍的人在计算中的说明更加明确。查看上面的代码,我假设如下:

  1. tenkan-sen:(9 个周期的最高价 + 9 个周期的最低价)/2 选择一个日期。查看前九个时期的最高价。 寻找相同九个时期内的最低低价。添加两个价格 一起并除以二。在日期的 Y 轴上绘制结果。
  2. kiju-sen:(26 个周期的最高价 + 26 个周期的最低价)/2 使用与 tenkan-sen 相同的日期。寻找最高价超过 前二十六期。寻找相同的最低低价 二十六期。将两个价格相加并除以二。绘制结果 日期的 Y 轴。
  3. chikou 跨度:在 Y 轴上绘制日期的收盘价二十六个周期 在所选日期的左侧。
  4. senkou span A: (tenkan-sen + kiju-sen)/2 向右移动了 26 个周期。 从情节最左边的日期开始。添加的值 tenkan-sen 和 kiju-sen。将总和除以 2。将结果值绘制在 右边二十六个时期的日期。继续这个直到你到达 今天的日期。
  5. senkou跨度B:(52期最高价+52期最低价)/2 向右移动 26 个句点。 再次从情节最左边的日期开始。找到最高点 前 52 期的价格。求同52的最低低价 期间。将总和除以 2。将结果值绘制在 右边二十六个时期的日期。继续这个直到你到达 今天的日期。

绘制从所选日期到今天日期的前三个结果会得到三行。最后两个给出了绘图区域 ("cloud") 以及定义 upper/lower "cloud" 边界的两条可能的 support/resistance 行。所有这些都假设 'periods' 是日期(对于日间交易者来说,它们可能是 15 分钟的周期,作为其他周期的例子)。此外,有些书有 senkou 计划 B 转移 26 个周期,有些转移它 22 个周期。我知道Goichi Hosoda的原著有二十六期,所以我用那个值。

感谢您编写程序。虽然我认为我理解了关于这个主题的书籍作者的意思,但直到看到代码我才确定。显然,作者不是程序员或做证明的数学家。我想我太线性了!

EdChum 的答案在计算 Ichimoku 云的组件时非常接近。

方法是正确的,但未能适应 leading_spans 的未来日期。当我们将前导跨度移动 26 时,pandas 只是移动到最后一个日期或最后一个索引,并且忽略额外(或未来)26 个值。

这是一个适应未来日期或未来云形成的实现

from datetime import timedelta

high_9 = df['High'].rolling(window= 9).max()
low_9 = df['Low'].rolling(window= 9).min()
df['tenkan_sen'] = (high_9 + low_9) /2

high_26 = df['High'].rolling(window= 26).max()
low_26 = df['Low'].rolling(window= 26).min()
df['kijun_sen'] = (high_26 + low_26) /2

# this is to extend the 'df' in future for 26 days
# the 'df' here is numerical indexed df
last_index = df.iloc[-1:].index[0]
last_date = df['Date'].iloc[-1].date()
for i in range(26):
    df.loc[last_index+1 +i, 'Date'] = last_date + timedelta(days=i)

df['senkou_span_a'] = ((df['tenkan_sen'] + df['kijun_sen']) / 2).shift(26)

high_52 = df['High'].rolling(window= 52).max()
low_52 = df['Low'].rolling(window= 52).min()
df['senkou_span_b'] = ((high_52 + low_52) /2).shift(26)

# most charting softwares dont plot this line
df['chikou_span'] = df['Close'].shift(-22) #sometimes -26 

tmp = df[['Close','senkou_span_a','senkou_span_b','kijun_sen','tenkan_sen']].tail(300)
a1 = tmp.plot(figsize=(15,10))
a1.fill_between(tmp.index, tmp.senkou_span_a, tmp.senkou_span_b)

high_9 = pd.rolling_max(df.high, window= 9)
low_9 =  pd.rolling_min(df.low, window= 9)
df['conversion_line'] = (high_9 + low_9) /2

high_26 = pd.rolling_max(df.high, window= 26)
low_26 = pd.rolling_min(df.low, window= 26)
df['base_line'] = (high_26 + low_26) / 2

df['leading_span_A'] = ((df.conversion_line + df.base_line) / 2).shift(30)

high_52 = pd.rolling_max(df.high, window= 52)
low_52 = pd.rolling_min(df.high, window= 52)
df['leading_span_B'] = ((high_52 + low_52) / 2).shift(30)

df['lagging_span'] = df.close.shift(-30)

fig,ax = plt.subplots(1,1,sharex=True,figsize = (20,9)) #share x axis and set a figure size
ax.plot(df.index, df.close,linewidth=4) # plot Close with index on x-axis with a line thickness of 4


# use the fill_between call of ax object to specify where to fill the chosen color
# pay attention to the conditions specified in the fill_between call
ax.fill_between(df.index,leading_span_A,df.leading_span_B,where = df.leading_span_A >= df.leading_span_B, color = 'lightgreen')
ax.fill_between(df.index,df.leading_span_A,df.leading_span_B,where = leading_span_A < df.leading_span_B, color = 'lightcoral')

import mplfinance as mpf
import pandas as pd

#Import the data into a "df", with headers, with the name of the stock like "stk = 'AAPL'"
#MPLFinance does not fill-in-between,hence there is no cloud.

#Tenkan Sen
tenkan_max = df['High'].rolling(window = 9, min_periods = 0).max()
tenkan_min = df['Low'].rolling(window = 9, min_periods = 0).min()
df['tenkan_avg'] = (tenkan_max + tenkan_min) / 2

#Kijun Sen
kijun_max = df['High'].rolling(window = 26, min_periods = 0).max()
kijun_min = df['Low'].rolling(window = 26, min_periods = 0).min()
df['kijun_avg'] = (kijun_max + kijun_min) / 2

#Senkou Span A
#(Kijun + Tenkan) / 2 Shifted ahead by 26 periods
df['senkou_a'] = ((df['kijun_avg'] + df['tenkan_avg']) / 2).shift(26)

#Senkou Span B
#52 period High + Low / 2
senkou_b_max = df['High'].rolling(window = 52, min_periods = 0).max()
senkou_b_min = df['Low'].rolling(window = 52, min_periods = 0).min()
df['senkou_b'] = ((senkou_b_max + senkou_b_min) / 2).shift(52)

#Chikou Span
#Current close shifted -26
df['chikou'] = (df['Close']).shift(-26)


#Plotting Ichimoku

#m_plots = ['kijun_avg', 'tenkan_avg',df[df.columns[5:]][-250:] ]

add_plots= [
            mpf.make_addplot(df['kijun_avg'][-250:]),
            mpf.make_addplot(df['tenkan_avg'][-250:]),
            mpf.make_addplot(df['chikou'][-250:]),
            mpf.make_addplot(df['senkou_a'][-250:]),
            mpf.make_addplot(df['senkou_b'][-250:])
           ]

mpf.plot(df[-250:], type = 'candle', mav= 200, volume = True, ylabel = "Price", ylabel_lower  = 'Volume', style = 'nightclouds', figratio=(15,10), figscale = 1.5,  addplot = add_plots,  title = '%s' %stk)

我对@chilliq 的代码进行了更改,并制作了一个实时工作示例,该示例现在可以在 2021 年 7 月运行。为了使用实时数据,您需要按相反的顺序对其进行排序,以便最近的值不会NaN.

def Ichimoku_Cloud(df):
        '''
        Get the values of Lines for Ichimoku Cloud
        args:
            df: Dataframe
        '''
        d = df.sort_index(ascending=False) # my Live NSE India data is in Recent -> Oldest order

        # Tenkan-sen (Conversion Line): (9-period high + 9-period low)/2))
        period9_high = d['HIGH'].rolling(window=9).max()
        period9_low = d['LOW'].rolling(window=9).min()
        tenkan_sen = (period9_high + period9_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()
        kijun_sen = (period26_high + period26_low) / 2

        # Senkou Span A (Leading Span A): (Conversion Line + Base Line)/2))
        senkou_span_a = ((tenkan_sen + 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()
        senkou_span_b = ((period52_high + period52_low) / 2).shift(26)

        # The most current closing price plotted 22 time periods behind (optional)
        chikou_span = d['CLOSE'].shift(-22) # Given at Trading View.

        d['blue_line'] = tenkan_sen
        d['red_line'] = kijun_sen
        d['cloud_green_line_a'] = senkou_span_a
        d['cloud_red_line_b'] = senkou_span_b
        d['lagging_line'] = chikou_span
        return d.sort_index(ascending=True)

这是我的 Numba / Numpy 版本的 Ichimoku。您可以更改参数,它会计算未来的云。我不知道这个转变是否与tenkansen,kinjunsen或senkou b有关,但我把它放在一边,因为我懒得去查。

import numpy as np
from numba import jit   

@jit(nopython=True)
def ichimoku_calc(data, period, shift=0):
    size = len(data)
    calc = np.array([np.nan] * (size + shift))
    for i in range(period - 1, size):
        window = data[i + 1 - period:i + 1]
        calc[i + shift] = (np.max(window) + np.min(window)) / 2
    return calc


@jit(nopython=True)
def ichimoku(data, tenkansen=9, kinjunsen=26, senkou_b=52, shift=26):
    size = len(data)
    n_tenkansen = ichimoku_calc(data, tenkansen)
    n_kinjunsen = ichimoku_calc(data, kinjunsen)
    n_chikou = np.concatenate(((data[shift:]), (np.array([np.nan] * (size - shift)))))
    n_senkou_a = np.concatenate((np.array([np.nan] * shift), ((n_tenkansen + n_kinjunsen) / 2)))
    n_senkou_b = ichimoku_calc(data, senkou_b, shift)
    return n_tenkansen, n_kinjunsen, n_chikou, n_senkou_a, n_senkou_b

您必须将输入数据转换为 numpy 数组,并确保您的最终时间索引长度为 len(data) + shift,并使用正确的时间步长计算期货日期。 Ichimoku 工作量很大...

我的交易机器人的结果:

Ichimoku on ETHUSDT pair