Skip to content

Commit

Permalink
Merge pull request #2 from kylejusticemagnuson/add-indicators
Browse files Browse the repository at this point in the history
Add new Indicators
  • Loading branch information
Kyle Magnuson authored Apr 5, 2017
2 parents 830c334 + 114be67 commit e769ac3
Show file tree
Hide file tree
Showing 13 changed files with 858 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Bollinger Bands
-Bandwidth
-Percent Bandwidth
-Range
-%B
Chaikin Money Flow
Chande Momentum Oscillator
Commodity Channel Index
Expand Down Expand Up @@ -62,18 +63,23 @@ Standard Variance
Stochastic
-%K
-%D
StochRSI
Rate of Change
Relative Strength Index
Triangular Moving Average
Triple Exponential Moving Average
True Range
Typical Price
Ultimate Oscillator
Vertical Horizontal Filter
Volatility
Volume Adjusted Moving Average
Volume Index
-Positive Volume Index
-Negative Volume Index
Volume Oscillator
Weighted Moving Average
Williams %R
```
pyti is currently only compatible with Python 2.7

Expand Down
13 changes: 13 additions & 0 deletions pyti/bollinger_bands.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,16 @@ def percent_bandwidth(data, period, std=2.0):
)

return percent_bandwidth


def percent_b(data, period, upper_bb_std=2.0, lower_bb_std=2.0):
"""
%B.
Formula:
%B = ((data - lb) / (ub - lb)) * 100
"""
lb = lower_bollinger_band(data, period, lower_bb_std)
ub = upper_bollinger_band(data, period, upper_bb_std)
percent_b = ((np.array(data) - lb) / (ub - lb)) * 100
return percent_b
16 changes: 16 additions & 0 deletions pyti/stochrsi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import numpy as np
from pyti.function_helper import fill_for_noncomputable_vals
from pyti.relative_strength_index import relative_strength_index


def stochrsi(data, period):
"""
StochRSI.
Formula:
SRSI = ((RSIt - RSI LOW) / (RSI HIGH - LOW RSI)) * 100
"""
rsi = relative_strength_index(data, period)[period:]
stochrsi = map(lambda idx: 100 * ((rsi[idx] - np.min(rsi[idx+1-period:idx+1])) / (np.max(rsi[idx+1-period:idx+1]) - np.min(rsi[idx+1-period:idx+1]))), range(period-1, len(rsi)))
stochrsi = fill_for_noncomputable_vals(data, stochrsi)
return stochrsi
79 changes: 79 additions & 0 deletions pyti/ultimate_oscillator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import numpy as np
from pyti import catch_errors
from pyti.function_helper import fill_for_noncomputable_vals
from pyti.true_range import true_range


def buying_pressure(close_data, low_data):
"""
Buying Pressure.
Formula:
BP = current close - min()
"""
catch_errors.check_for_input_len_diff(close_data, low_data)
bp = map(
lambda idx:
close_data[idx] - np.min([low_data[idx], close_data[idx-1]]),
range(1, len(close_data))
)
bp = fill_for_noncomputable_vals(close_data, bp)
return bp


def avg_helper(close_data, low_data, period):
catch_errors.check_for_input_len_diff(close_data, low_data)
catch_errors.check_for_period_error(close_data, period)
bp = buying_pressure(close_data, low_data)
tr = true_range(close_data, period)
avg = map(
lambda idx:
sum(bp[idx+1-period:idx+1]) / sum(tr[idx+1-period:idx+1]),
range(period-1, len(close_data))
)
avg = fill_for_noncomputable_vals(close_data, avg)
return avg


def average_7(close_data, low_data, period=7):
"""
Average7.
Formula:
AVG7 = SUM(BP) / SUM(TR) for 7 days
"""
return avg_helper(close_data, low_data, period)


def average_14(close_data, low_data, period=14):
"""
Averag14.
Formula:
AVG14 = SUM(BP) / SUM(TR) for 14 days
"""
return avg_helper(close_data, low_data, period)


def average_28(close_data, low_data, period=28):
"""
average_28.
Formula:
AVG14 = SUM(BP) / SUM(TR) for 28 days
"""
return avg_helper(close_data, low_data, period)


def ultimate_oscillator(close_data, low_data):
"""
Ultimate Oscillator.
Formula:
UO = 100 * ((4 * AVG7) + (2 * AVG14) + AVG28) / (4 + 2 + 1)
"""
a7 = 4 * average_7(close_data, low_data)
a14 = 2 * average_14(close_data, low_data)
a28 = average_28(close_data, low_data)
uo = 100 * ((a7 + a14 + a28) / 7)
return uo
26 changes: 26 additions & 0 deletions pyti/volume_adjusted_moving_average.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import numpy as np
from pyti import catch_errors
from pyti.function_helper import fill_for_noncomputable_vals


def volume_adjusted_moving_average(close_data, volume, period):
"""
Volume Adjusted Moving Average.
Formula:
VAMA = SUM(CLOSE * VolumeRatio) / period
"""
catch_errors.check_for_input_len_diff(close_data, volume)
catch_errors.check_for_period_error(close_data, period)

avg_vol = np.mean(volume)
vol_incr = avg_vol * 0.67
vol_ratio = map(lambda val: val / vol_incr, volume)
close_vol = np.array(close_data) * vol_ratio
vama = map(
lambda idx:
sum(close_vol[idx+1-period:idx+1]) / period,
range(period-1, len(close_data))
)
vama = fill_for_noncomputable_vals(close_data, vama)
return vama
17 changes: 17 additions & 0 deletions pyti/volume_oscillator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pyti import catch_errors
from pyti.simple_moving_average import simple_moving_average as sma


def volume_oscillator(volume, short_period, long_period):
"""
Volume Oscillator.
Formula:
vo = 100 * (SMA(vol, short) - SMA(vol, long) / SMA(vol, long))
"""
catch_errors.check_for_period_error(volume, short_period)
catch_errors.check_for_period_error(volume, long_period)

vo = (100 * ((sma(volume, short_period) - sma(volume, long_period)) /
sma(volume, long_period)))
return vo
18 changes: 18 additions & 0 deletions pyti/williams_percent_r.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import numpy as np


def williams_percent_r(close_data):
"""
Williams %R.
Formula:
wr = (HighestHigh - close / HighestHigh - LowestLow) * -100
"""
highest_high = np.max(close_data)
lowest_low = np.min(close_data)
wr = map(
lambda close:
((highest_high - close) / (highest_high - lowest_low)) * -100,
close_data
)
return wr
55 changes: 55 additions & 0 deletions tests/test_bollinger_bands.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,49 @@ def setUp(self):
0.25008520904313003, 0.015916940699370632, 0.042754393107139112,
0.2073341509427761, 0.22254729716543903, 0.41858526202755941]

self.percent_b_period_6_expected = [np.nan, np.nan, np.nan, np.nan,
np.nan, 71.416541403692662, 83.866195707846202, 89.900444691314974,
63.426415365090115, 35.767677651338673, 83.799788142320565,
87.889224421599337, 59.106910290885928, 11.96458507468704,
28.479369714165294, 0.48133863682257844, 12.285215487954284,
6.3393602452485505, 21.267111658762513, 13.49736048726756,
20.633721149749988, 52.14494904093273, 92.609473280722213,
74.639976904914889, 59.094630772307845, 49.66553801448412,
63.325797147897603, 55.426874977952444, 10.017435200369071,
79.003157061501014, 37.672193277556708, 15.974639983239609,
-1.8601440885751042, 45.20795907730156, 63.972845478064585,
95.207225499258882, 85.985227580047791, 67.435119671099159,
31.630463162422441, 27.571484422038111, 35.722372871057829,
71.618605297219872, 82.955372856384173, 87.151285854545762,
74.171319134171384, 75.559113110015375, 87.343525582840115,
91.1782305681959, 47.955111759473482, 36.062995266478268,
52.91782297838521, 24.865780679396728, 3.6577182625657372,
12.333006154254761, 22.670145389320972, 47.500168554380693,
49.343559927296866, 88.429722905161483, 14.063096880802616,
28.815219042748808, 23.945630941870743, 37.480155171666482,
32.483867906988174, 87.427776919156557, 55.68901103712831,
101.87846525362536, 86.068033824415252, 34.418840302649009,
89.7315161347979, 80.552022188749376, 24.740327162381352,
15.609914152458346, 16.096651128534848, 28.621070615399287,
45.025280784200753, 7.1934543203424601, 12.890555832140956,
68.838787770707441, 22.066807075637879, 82.182016640801606,
89.183636347352618, 77.408650391055701, 36.670131889717432,
24.230650132629815, 17.0584502470041, 31.102653583047623,
50.715383366677443, 50.405819514057526, 37.336878890123955,
62.974998584373218, 87.183212709286494, 79.165024866150304,
89.803755087524308, 89.691355187229732, 89.388803667484723,
56.1341178471503, 81.511763342799497, 81.605555245978223,
82.110023720757951, 75.02531441281657, 60.992424485043436,
10.395174764351147, 42.909683423846282, -3.7954564196469489,
20.54151740274996, 34.300910469854976, 45.435543109144909,
7.397095218758114, -4.06520517146219, 12.416069594495738,
19.856034359944733, 28.095083994135567, 36.679927413666206,
15.436375290700454, 40.123343485150521, 10.908399076977785,
21.271305460156579, -3.6877472675652037, 15.23460112813585,
15.974639907730282, 30.562720118308356, 25.008520904313002,
1.5916940699370632, 4.275439310713911, 20.733415094277611,
22.254729716543903, 41.858526202755939]

def test_upper_bollinger_bands_period_6(self):
period = 6
upper_bb = bollinger_bands.upper_bollinger_band(self.data, period)
Expand Down Expand Up @@ -337,3 +380,15 @@ def test_percent_bandwidth_invalid_period(self):
bollinger_bands.percent_bandwidth(self.data, period)
expected = "Error: data_len < period"
self.assertEqual(str(cm.exception), expected)

def test_percent_b_period_6(self):
period = 6
percent_b = bollinger_bands.percent_b(self.data, period)
np.testing.assert_array_equal(percent_b, self.percent_b_period_6_expected)

def test_percent_b_invalid_period(self):
period = 128
with self.assertRaises(Exception) as cm:
bollinger_bands.percent_b(self.data, period)
expected = "Error: data_len < period"
self.assertEqual(str(cm.exception), expected)
117 changes: 117 additions & 0 deletions tests/test_stochrsi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import unittest
import numpy as np

from tests.sample_data import SampleData
from pyti import stochrsi


class TestStochRSI(unittest.TestCase):
def setUp(self):
"""Create data to use for testing."""
self.data = SampleData().get_sample_close_data()

self.stochrsi_period_6_expected = [np.nan, np.nan, np.nan, np.nan,
np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan,
44.471222157460431, 0.0, 0.0, 11.492848409063964, 0.0, 0.0, 0.0,
12.996028967803408, 0.0, 0.0, 100.0, 100.0, 88.834308685247748,
72.485626510038344, 68.948591144212699, 81.54139590783565,
72.069361538856384, 0.0, 100.0, 45.968610199984546, 9.057017058300568,
0.0, 72.780883175761147, 97.098272486847122, 100.0, 100.0,
78.357655504172754, 0.0, 3.3759971697837599, 23.819966852076472,
68.973352093845421, 100.0, 100.0, 88.361969800298795, 100.0, 100.0,
100.0, 0.0, 0.0, 25.101888829188145, 0.0, 0.0, 0.0, 0.0,
47.597870253604583, 52.570804308995712, 100.0, 0.14199939183552529,
24.059960445031265, 10.896141995479166, 30.513406175435293,
8.9065608580022868, 100.0, 68.068787936734168, 100.0, 100.0,
22.231267706492098, 100.0, 100.0, 10.017481472275215, 0.0, 0.0,
18.16126672568527, 40.521482277525919, 0.0, 0.0, 100.0,
35.793893628113686, 100.0, 100.0, 100.0, 33.57757547925862,
8.6247722964853466, 0.0, 17.302011810094726, 49.159702249924187,
60.932783640150376, 31.942145934203868, 82.095023751511064, 100.0,
100.0, 100.0, 100.0, 100.0, 15.042425515475383, 66.462588569115553,
74.790326011378099, 88.557628253482306, 92.354570404206441,
61.458440388782407, 0.0, 36.577660953654245, 0.0, 15.598256159532903,
31.358012359220449, 48.116305853565294, 0.0, 0.0, 0.0, 0.0, 0.0,
33.85258109852839, 57.650642272894835, 100.0, 75.843338626788722,
74.436615187163184, 0.0, 4.4097493664417158, 0.0, 94.669594611808265,
65.534738257501118, 27.780745298221497, 0.0, 47.709624772979161,
32.163553172474437, 100.0]

self.stochrsi_period_8_expected = [np.nan, np.nan, np.nan, np.nan,
np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan,
np.nan, np.nan, 0.0, 0.0, 0.0, 5.9170858267051587, 0.0, 0.0,
44.731067481598593, 100.0, 89.261888846566762, 73.506626263784852,
70.124518845179722, 92.473144438199071, 91.742030577620142,
15.037735074756913, 100.0, 47.306749309021193, 10.140545886437421, 0.0,
72.278552326472152, 97.679213884699251, 100.0, 100.0,
80.347390204472063, 47.828534810102333, 49.538188855519977,
27.611275977441203, 67.963793266686267, 78.134869525725151,
99.252804098374185, 90.150479498192112, 100.0, 100.0, 100.0,
4.9013099702443785, 0.0, 24.68617122074053, 0.0, 0.0, 0.0, 0.0,
39.802519009096748, 29.010598833949796, 53.053222984371452, 0.0,
21.631906841014569, 8.3458733315535447, 25.22414006544841,
4.5780668059713552, 49.007350471187422, 34.273386605258764, 100.0,
100.0, 22.403954556738867, 100.0, 100.0, 12.105988070501787, 0.0, 0.0,
16.657204131414304, 37.35692115954221, 0.0, 0.0, 100.0,
32.997720377696496, 100.0, 100.0, 100.0, 51.334083599997683,
31.853266142141972, 8.1527836280558503, 22.745953245334995,
45.508879820782681, 27.946619216922286, 14.815380622177496,
80.088315251347964, 100.0, 100.0, 100.0, 100.0, 100.0,
56.481950024728867, 86.652579488979171, 93.049895919142116, 100.0,
100.0, 69.687224483627773, 4.7487429986927374, 40.824255220794718, 0.0,
14.82258516102512, 25.574101538565898, 33.214453816875086, 0.0, 0.0,
0.0, 0.0, 0.0, 12.028791696947424, 5.742958082055595,
46.151131514517758, 73.698400050314845, 72.171996877638023, 0.0,
3.9883199142603543, 0.0, 63.887076434178304, 43.192059380396671,
18.535460030504883, 0.0, 44.316525695640522, 29.344038948303997, 100.0]

self.stochrsi_period_10_expected = [np.nan, np.nan, np.nan, np.nan,
np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan,
np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, 0.0, 0.0,
27.66288026615667, 68.42179408282631, 60.95511012399831,
73.056366879654533, 69.632283416324114, 91.673870575592005,
90.973445004595106, 62.013426837224038, 99.412333298280316,
56.029604272708319, 9.5954111342724602, 0.0, 71.203658595987136,
97.102435963871216, 100.0, 100.0, 81.393245300884587,
49.654269940792076, 51.293355239394344, 60.927269295952172,
83.862439770570063, 81.039109157752904, 100.0, 90.97577696869304, 100.0,
100.0, 100.0, 58.073925890407921, 41.358292066458077,
25.654266421595828, 0.0, 0.0, 0.0, 0.0, 23.713848713037535,
16.806950663591991, 47.466120473538204, 0.0, 10.796474428727892,
4.690894374748388, 21.71719912790817, 1.5761771583204254,
41.050006983973205, 27.2736467372333, 100.0, 100.0, 21.771640646517728,
100.0, 100.0, 26.039977920480617, 0.0, 0.0, 15.737216917199534,
35.412360099771497, 0.0, 0.0, 50.704904555903788, 14.994477423420522,
100.0, 100.0, 100.0, 51.87903440907273, 31.867669596262672,
26.242262653493135, 37.395093360643585, 49.651518706252737,
34.777475814569073, 14.222945117349619, 34.122394180512103,
59.589894426757162, 100.0, 100.0, 100.0, 100.0, 60.387458746010005,
93.702909932596725, 100.0, 100.0, 100.0, 81.695205658391245,
18.390800382020387, 51.052985817884824, 0.0, 14.606027212675402,
25.21593064024956, 32.720972201555149, 0.0, 0.0, 0.0, 0.0, 0.0,
10.169626915478698, 3.627127999972207, 19.385791765467346,
13.371820642369503, 22.43112726583951, 0.0, 3.4785405767313109, 0.0,
54.371077241311575, 35.289162480238765, 7.8549519711197089, 0.0,
28.494276007229587, 18.19959392470588, 100.0]

def test_stochrsi_period_6(self):
period = 6
sr = stochrsi.stochrsi(self.data, period)
np.testing.assert_array_equal(sr, self.stochrsi_period_6_expected)

def test_stochrsi_period_8(self):
period = 8
sr = stochrsi.stochrsi(self.data, period)
np.testing.assert_array_equal(sr, self.stochrsi_period_8_expected)

def test_stochrsi_period_10(self):
period = 10
sr = stochrsi.stochrsi(self.data, period)
np.testing.assert_array_equal(sr, self.stochrsi_period_10_expected)

def test_stochrsi_invalid_period(self):
period = 128
with self.assertRaises(Exception) as cm:
stochrsi.stochrsi(self.data, period)
expected = "Error: data_len < period"
self.assertEqual(str(cm.exception), expected)
Loading

0 comments on commit e769ac3

Please sign in to comment.