Skip to content

Commit

Permalink
[GH-116] Add ichimoku cloud width (#147)
Browse files Browse the repository at this point in the history
The Ichimoku Cloud is a collection of technical indicators
that show support and resistance levels, as well as momentum
and trend direction.

In this implementation, we only calculate the delta between
lead A and lead B (which is the width of the cloud).

It contains three windows:
* window for the conversion line, default to 9
* window for the baseline and the shifts, default to 26
* window for the leading line, default to 52

Formular:
* conversion line = (PH9 + PL9) / 2
* baseline = (PH26 + PL26) / 2
* leading span A = (conversion line + baseline) / 2
* leading span B = (PH52 + PL52) / 2
* result = leading span A - leading span B

Where:
* PH = Period High
* PL = Period Low

Examples:
* `df['ichimoku']` returns the ichimoku cloud width with default windows
* `df['ichimoku_7,22,44']` returns the ichimoku cloud width with window sizes
  7, 22, 44
  • Loading branch information
jealous committed Jun 18, 2023
1 parent fc0a86d commit b075674
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 4 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ Supported statistics/indicators are:
* MAD: Mean Absolute Deviation
* ROC: Rate of Change
* Coppock: Coppock Curve
* Ichimoku: Ichimoku Cloud

## Installation

Expand Down Expand Up @@ -806,6 +807,36 @@ Examples:
* `df['coppock_5,10,15']` returns the Coppock Curve with WMA window 5,
fast window 10, slow window 15.

#### [Ichimoku Cloud](https://www.investopedia.com/terms/i/ichimoku-cloud.asp)

The Ichimoku Cloud is a collection of technical indicators
that show support and resistance levels, as well as momentum
and trend direction.

In this implementation, we only calculate the delta between
lead A and lead B (which is the width of the cloud).

It contains three windows:
* window for the conversion line, default to 9
* window for the baseline and the shifts, default to 26
* window for the leading line, default to 52

Formular:
* conversion line = (PH9 + PL9) / 2
* baseline = (PH26 + PL26) / 2
* leading span A = (conversion line + baseline) / 2
* leading span B = (PH52 + PL52) / 2
* result = leading span A - leading span B

Where:
* PH = Period High
* PL = Period Low

Examples:
* `df['ichimoku']` returns the ichimoku cloud width with default windows
* `df['ichimoku_7,22,44']` returns the ichimoku cloud width with window sizes
7, 22, 44

## Issues

We use [Github Issues](https://github.com/jealous/stockstats/issues) to track
Expand Down
66 changes: 62 additions & 4 deletions stockstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ class StockDataFrame(pd.DataFrame):
AO_SLOW = 34
AO_FAST = 5

COPPOCK_PERIODS = (10, 11, 14)
COPPOCK = (10, 11, 14)

ICHIMOKU = (9, 26, 52)

MULTI_SPLIT_INDICATORS = ("kama",)

Expand Down Expand Up @@ -1133,9 +1135,9 @@ def _get_coppock(self, windows=None):
:return: None
"""
if windows is None:
window = self.COPPOCK_PERIODS[0]
fast = self.COPPOCK_PERIODS[1]
slow = self.COPPOCK_PERIODS[2]
window = self.COPPOCK[0]
fast = self.COPPOCK[1]
slow = self.COPPOCK[2]
column_name = 'coppock'
else:
periods = self.to_ints(windows)
Expand All @@ -1158,6 +1160,61 @@ def get_int_positive(self, windows):
raise IndexError("window must be greater than 0")
return window

def _hl_mid(self, period):
ph = self.mov_max(self['high'], period)
pl = self.mov_min(self['low'], period)
return (ph + pl) * 0.5

def _get_ichimoku(self, windows=None):
""" get Ichimoku Cloud
The Ichimoku Cloud is a collection of technical indicators
that show support and resistance levels, as well as momentum
and trend direction.
In this implementation, we only calculate the delta between
lead A and lead B.
https://www.investopedia.com/terms/i/ichimoku-cloud.asp
It contains three windows:
* window for the conversion line, default to 9
* window for the baseline and the shifts, default to 26
* window for the leading line, default to 52
Formular:
* conversion line = (PH9 + PL9) / 2
* baseline = (PH26 + PL26) / 2
* leading span A = (conversion line + baseline) / 2
* leading span B = (PH52 + PL52) / 2
* result = leading span A - leading span B
Where:
* PH = Period High
* PL = Period Low
"""
if windows is None:
conv = self.ICHIMOKU[0]
base = self.ICHIMOKU[1]
lead = self.ICHIMOKU[2]
column_name = 'ichimoku'
else:
periods = self.to_ints(windows)
conv = periods[0]
base = periods[1]
lead = periods[2]
column_name = 'ichimoku_{}'.format(windows)

conv_line = self._hl_mid(conv)
base_line = self._hl_mid(base)
lead_a = (conv_line + base_line) * 0.5
lead_b = self._hl_mid(lead)

lead_a_s = lead_a.shift(base, fill_value=lead_a.iloc[0])
lead_b_s = lead_b.shift(base, fill_value=lead_b.iloc[0])
self[column_name] = lead_a_s - lead_b_s

@classmethod
def mov_std(cls, series, window):
return cls._rolling(series, window).std()
Expand Down Expand Up @@ -1527,6 +1584,7 @@ def handler(self):
('bop',): self._get_bop,
('cmo',): self._get_cmo,
('coppock',): self._get_coppock,
('ichimoku',): self._get_ichimoku,
}

def __init_not_exist_column(self, key):
Expand Down
17 changes: 17 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,23 @@ def test_mad_raw():
res = StockDataFrame._mad(series, 6)
assert_that(res[5], near_to(2.667))

def test_ichimoku(self):
stock = self.get_stock_90days()
i0 = stock['ichimoku']
i1 = stock['ichimoku_9,26,52']
i2 = stock['ichimoku_5,10,20']
assert_that(i0[20110228], equal_to(0))
assert_that(i0[20110308], near_to(0.0275))
assert_that(i0[20110318], near_to(-0.0975))

assert_that(i1[20110228], equal_to(0))
assert_that(i1[20110308], near_to(0.0275))
assert_that(i1[20110318], near_to(-0.0975))

assert_that(i2[20110228], near_to(-0.11))
assert_that(i2[20110308], near_to(0.0575))
assert_that(i2[20110318], near_to(0.0175))

@staticmethod
def test_linear_wma():
series = pd.Series([10, 15, 15, 17, 18, 21])
Expand Down

0 comments on commit b075674

Please sign in to comment.