Skip to content

Commit

Permalink
added rank operator
Browse files Browse the repository at this point in the history
  • Loading branch information
wegamekinglc committed Sep 8, 2017
1 parent 1fd60a0 commit 8497f91
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 26 deletions.
22 changes: 22 additions & 0 deletions PyFin/Analysis/TechnicalAnalysis/StatefulTechnicalAnalysers.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ from PyFin.Math.Accumulators.StatefulAccumulators cimport MovingResidue
from PyFin.Math.Accumulators.StatefulAccumulators cimport MovingHistoricalWindow
from PyFin.Math.Accumulators.StatefulAccumulators cimport MovingLogReturn
from PyFin.Math.Accumulators.StatefulAccumulators cimport MovingCorrelation
from PyFin.Math.Accumulators.StatefulAccumulators cimport MovingRank
from PyFin.Math.MathConstants cimport NAN


Expand Down Expand Up @@ -437,6 +438,27 @@ cdef class SecurityMovingCorrelation(SecuritySingleValueHolder):
return SecurityMovingCorrelation, (self._window, self._dependency), d


cdef class SecurityMovingRank(SecuritySingleValueHolder):

def __init__(self, window, dependency='x'):
super(SecurityMovingRank, self).__init__(window, MovingRank, dependency)

def __deepcopy__(self, memo):
if self._compHolder:
copied = SecurityMovingRank(self._window - self._compHolder._window, self._compHolder)
else:
copied = SecurityMovingRank(self._window, self._dependency)
copied.copy_attributes(self.collect_attributes(), is_deep=True)
return copied

def __reduce__(self):
d = self.collect_attributes()
if self._compHolder:
return SecurityMovingRank, (self._window - self._compHolder._window, self._compHolder), d
else:
return SecurityMovingRank, (self._window, self._dependency), d


cdef class SecurityMovingHistoricalWindow(SecuritySingleValueHolder):
def __init__(self, window, dependency='x'):
super(SecurityMovingHistoricalWindow, self).__init__(window, MovingHistoricalWindow, dependency)
Expand Down
4 changes: 3 additions & 1 deletion PyFin/Analysis/TechnicalAnalysis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
from PyFin.Analysis.TechnicalAnalysis.StatefulTechnicalAnalysers import SecurityMovingLogReturn
from PyFin.Analysis.TechnicalAnalysis.StatefulTechnicalAnalysers import SecurityMovingResidue
from PyFin.Analysis.TechnicalAnalysis.StatefulTechnicalAnalysers import SecurityMovingCorrelation
from PyFin.Analysis.TechnicalAnalysis.StatefulTechnicalAnalysers import SecurityMovingRank

__all__ = ['SecuritySignValueHolder',
'SecurityXAverageValueHolder',
Expand Down Expand Up @@ -79,4 +80,5 @@
'SecurityMovingHistoricalWindow',
'SecurityMovingLogReturn',
'SecurityMovingResidue',
'SecurityMovingCorrelation']
'SecurityMovingCorrelation',
'SecurityMovingRank']
2 changes: 1 addition & 1 deletion PyFin/Math/Accumulators/StatefulAccumulators.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ cdef class MACD(StatelessSingleValueAccumulator):

cdef class MovingRank(SortedValueHolder):

cdef public list _runningRank
cdef public double _runningRank

cpdef object result(self)

Expand Down
4 changes: 2 additions & 2 deletions PyFin/Math/Accumulators/StatefulAccumulators.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1352,10 +1352,10 @@ cdef class MACD(StatelessSingleValueAccumulator):
cdef class MovingRank(SortedValueHolder):
def __init__(self, window, dependency='x'):
super(MovingRank, self).__init__(window, dependency)
self._runningRank = []
self._runningRank = NAN

cpdef object result(self):
self._runningRank = [bisect.bisect_left(self._sortedArray, x) for x in self._deque.as_list()]
self._runningRank = bisect.bisect_left(self._sortedArray, self._deque[self._deque.size() - 1])
return self._runningRank

def __deepcopy__(self, memo):
Expand Down
5 changes: 5 additions & 0 deletions PyFin/api/Analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from PyFin.Analysis.TechnicalAnalysis import SecurityAsinhValueHolder
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingResidue
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingCorrelation
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingRank
from PyFin.Analysis.SecurityValueHolders import SecurityShiftedValueHolder
from PyFin.Analysis.SecurityValueHolders import SecurityLatestValueHolder
from PyFin.Analysis.SecurityValueHolders import SecurityIIFValueHolder
Expand Down Expand Up @@ -96,6 +97,10 @@ def CORR(window, dependency=('x', 'y')):
return SecurityMovingCorrelation(window, dependency)


def RANK(window, dependency='x'):
return SecurityMovingRank(window, dependency)


def MA(window, dependency='x'):
return SecurityMovingAverage(window, dependency)

Expand Down
2 changes: 2 additions & 0 deletions PyFin/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
from PyFin.api.Analysis import IIF
from PyFin.api.Analysis import RES
from PyFin.api.Analysis import CORR
from PyFin.api.Analysis import RANK

from PyFin.api.Analysis import CSRank
from PyFin.api.Analysis import CSMean
Expand Down Expand Up @@ -118,6 +119,7 @@
"ASINH",
"RES",
"CORR",
"RANK",
"SHIFT",
"IIF",
"CSRank",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingRSI
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingLogReturn
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingResidue
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingRank
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingCorrelation
from PyFin.Analysis.TechnicalAnalysis import SecurityMovingHistoricalWindow

Expand Down Expand Up @@ -526,10 +527,10 @@ def testSecurityMovingRSI(self):
expected = pos_avg.value[name] / (pos_avg.value[name] - neg_avg.value[name]) * 100
calculated = value[name]
self.assertAlmostEqual(expected, calculated, 12, 'at index {0}\n'
'expected: {1:.12f}\n'
'calculated: {2:.12f}'.format(i,
expected,
calculated))
'expected: {1:.12f}\n'
'calculated: {2:.12f}'.format(i,
expected,
calculated))

def testSecurityMovingRSIDeepcopy(self):
self.template_test_deepcopy(SecurityMovingRSI, window=10, dependency='x')
Expand Down Expand Up @@ -740,7 +741,7 @@ def testValueHolderCompounding(self):
self.assertAlmostEqual(np.mean((container)), compounded2.value['aapl'], 12)

def testSecurityMovingResidue(self):
window =100
window = 100
mr = SecurityMovingResidue(window, ('y', 'x'))
for i in range(len(self.aapl['close'])):
data = {'aapl': {'y': self.aapl['close'][i], 'x': self.aapl['open'][i]},
Expand All @@ -753,9 +754,9 @@ def testSecurityMovingResidue(self):
series_y = getattr(self, name)['close'][i - window + 1:i + 1]
expected_res = series_y[-1] - np.dot(series_x, series_y) / np.dot(series_x, series_x) * series_x[-1]
self.assertAlmostEqual(mr.value[name], expected_res, msg= \
"at index {0} and symbol {1}\n"
"expected: {2}\n"
"calculated: {3}".format(i, name, expected_res, mr.value[name]))
"at index {0} and symbol {1}\n"
"expected: {2}\n"
"calculated: {3}".format(i, name, expected_res, mr.value[name]))

def testSecurityMovingResidueDeepcopy(self):
ma = SecurityMovingResidue(10, ['y', 'x'])
Expand Down Expand Up @@ -882,3 +883,74 @@ def testSecurityMovingCorrelationPickle(self):
if i >= 10:
for name in ma.value.index():
self.assertAlmostEqual(ma.value[name], pickled.value[name])

def testSecurityMovingRank(self):
window = 100
mr = SecurityMovingRank(window, 'x')
for i in range(len(self.aapl['close'])):
data = {'aapl': {'y': self.aapl['close'][i], 'x': self.aapl['open'][i]},
'ibm': {'y': self.ibm['close'][i], 'x': self.ibm['open'][i]}}
mr.push(data)

if i >= window - 1:
for name in mr.value.index():
series_x = getattr(self, name)['open'][i - window + 1:i + 1]
expected_res = np.argsort(series_x.argsort())[-1]
self.assertAlmostEqual(mr.value[name], expected_res, msg= \
"at index {0} and symbol {1}\n"
"expected: {2}\n"
"calculated: {3}".format(i, name, expected_res, mr.value[name]))

def testSecurityMovingRankDeepcopy(self):
ma = SecurityMovingRank(10, 'x')

data = dict(aapl=dict(x=1., y=2),
ibm=dict(x=2., y=3))
data2 = dict(aapl=dict(x=2., y=3),
ibm=dict(x=3., y=4))

ma.push(data)
ma.push(data2)

copied = copy.deepcopy(ma)

for i, v in enumerate(np.random.rand(20)):
data['aapl']['x'] = v
data['aapl']['y'] = v + 0.1
data['ibm']['x'] = v + 0.1
data['ibm']['y'] = v + 0.2
ma.push(data)
copied.push(data)
if i >= 10:
for name in ma.value.index():
self.assertAlmostEqual(ma.value[name], copied.value[name])

def testSecurityMovingRankPickle(self):
ma = SecurityMovingRank(10, 'x')

data = dict(aapl=dict(x=1., y=2),
ibm=dict(x=2., y=3))
data2 = dict(aapl=dict(x=2., y=3),
ibm=dict(x=3., y=4))

ma.push(data)
ma.push(data2)

with tempfile.NamedTemporaryFile('w+b', delete=False) as f:
pickle.dump(ma, f)

with open(f.name, 'rb') as f2:
pickled = pickle.load(f2)
os.unlink(f.name)

for i, v in enumerate(np.random.rand(20)):
data['aapl']['x'] = v
data['aapl']['y'] = v + 0.1
data['ibm']['x'] = v + 0.1
data['ibm']['y'] = v + 0.2
ma.push(data)
pickled.push(data)

if i >= 10:
for name in ma.value.index():
self.assertAlmostEqual(ma.value[name], pickled.value[name])
29 changes: 15 additions & 14 deletions PyFin/tests/Math/Accumulators/testStatefulAccumulators.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,7 +650,8 @@ def testMovingQuantile(self):
expected = sorted_con.index(value) / (len(sorted_con) - 1.)
self.assertAlmostEqual(calculated, expected, 15, "at index {0:d}\n"
"Quantile expected: {1:f}\n"
"Quantile calculated: {2:f}".format(i, expected, calculated))
"Quantile calculated: {2:f}".format(i, expected,
calculated))

def testMovingQuantileDeepcopy(self):
test = MovingQuantile(10, 'close')
Expand Down Expand Up @@ -1841,9 +1842,9 @@ def testEMAMACD(self):
expected = macd.value - ema_macd.value
calculated = macd_diff.value
self.assertAlmostEqual(expected, calculated, 10, "at index {0:d}\n"
"expected ema macd diff: {1:f}\n"
"calculated ema macd diff: {2:f}".format(i, expected,
calculated))
"expected ema macd diff: {1:f}\n"
"calculated ema macd diff: {2:f}".format(i, expected,
calculated))

def testMovingRank(self):
window = 10
Expand All @@ -1857,10 +1858,10 @@ def testMovingRank(self):
con = con[1:]
if i >= window - 1:
calculated = mk.result()
expected = np.argsort(np.argsort(con))
self.assertListEqual(list(expected), calculated, "at index {0:d}\n"
"expected rank: {1}\n"
"calculated rank: {2}".format(i, expected, calculated))
expected = np.argsort(np.argsort(con))[-1]
self.assertEqual(expected, calculated, "at index {0:d}\n"
"expected rank: {1}\n"
"calculated rank: {2}".format(i, expected, calculated))

def tesMovingRankDeepcopy(self):
window = 10
Expand Down Expand Up @@ -2241,8 +2242,8 @@ def testMovingResidue(self):
mr.push(dict(x=x, y=y))

if i >= window - 1:
series_x = x_data[i-window+1:i+1]
series_y = self.sample[i-window+1:i+1]
series_x = x_data[i - window + 1:i + 1]
series_y = self.sample[i - window + 1:i + 1]

calculated_res = mr.result()
expected_res = y - np.dot(series_x, series_y) / np.dot(series_x, series_x) * x
Expand All @@ -2265,8 +2266,8 @@ def testMovingResidueDeepcopy(self):

datas = np.random.randn(40)
for i in datas:
mr.push(dict(x=i, y=i+1.))
copied.push(dict(x=i, y=i+1))
mr.push(dict(x=i, y=i + 1.))
copied.push(dict(x=i, y=i + 1))

self.assertAlmostEqual(copied.value, mr.value)

Expand All @@ -2286,8 +2287,8 @@ def testMovingResiduePickle(self):

datas = np.random.randn(40)
for i in datas:
mr.push(dict(x=i, y=i+1.))
pickled.push(dict(x=i, y=i+1.))
mr.push(dict(x=i, y=i + 1.))
pickled.push(dict(x=i, y=i + 1.))

self.assertAlmostEqual(mr.value, pickled.value)
os.unlink(f.name)
Expand Down

0 comments on commit 8497f91

Please sign in to comment.