From bc168ae10120e2e25b233e6fe51efe131b7eb761 Mon Sep 17 00:00:00 2001 From: Dave Herman Date: Wed, 5 Jul 2023 18:01:04 -0400 Subject: [PATCH] Add docs and tests for Monowave Adding tests and documentation to MonoWave feature --- example_monowave.py | 4 +- models/MonoWave.py | 199 ++++++++++++++++-- models/__pycache__/MonoWave.cpython-39.pyc | Bin 0 -> 11081 bytes models/__pycache__/WavePattern.cpython-39.pyc | Bin 0 -> 4416 bytes models/__pycache__/WaveRules.cpython-39.pyc | Bin 0 -> 9817 bytes models/__pycache__/__init__.cpython-39.pyc | Bin 0 -> 144 bytes models/__pycache__/functions.cpython-39.pyc | Bin 0 -> 1690 bytes models/__pycache__/helpers.cpython-39.pyc | Bin 0 -> 2813 bytes setup.py | 12 ++ .../__pycache__/test_monowave.cpython-39.pyc | Bin 0 -> 2561 bytes tests/test_monowave.py | 67 +++++- 11 files changed, 257 insertions(+), 25 deletions(-) create mode 100644 models/__pycache__/MonoWave.cpython-39.pyc create mode 100644 models/__pycache__/WavePattern.cpython-39.pyc create mode 100644 models/__pycache__/WaveRules.cpython-39.pyc create mode 100644 models/__pycache__/__init__.cpython-39.pyc create mode 100644 models/__pycache__/functions.cpython-39.pyc create mode 100644 models/__pycache__/helpers.cpython-39.pyc create mode 100644 setup.py create mode 100644 tests/__pycache__/test_monowave.cpython-39.pyc diff --git a/example_monowave.py b/example_monowave.py index acce0da..34f8ef3 100644 --- a/example_monowave.py +++ b/example_monowave.py @@ -9,9 +9,9 @@ dates = np.array(list(df['Date'])) # find a monowave down starting from the low at the 3rd index -mw_up = MonoWaveUp(lows=lows, highs=highs, dates=dates, idx_start=3, skip=5) +mw_up = MonoWaveUp(lows=lows, highs=highs, dates=dates, idx_start=3, skip=2) plot_monowave(df, mw_up) # find a monowave down from the end of the monowave up -mw_down = MonoWaveDown(lows=lows, highs=highs, dates=dates, idx_start=mw_up.idx_end, skip=0) +mw_down = MonoWaveDown(lows=lows, highs=highs, dates=dates, idx_start=mw_up.idx_end, skip=3) plot_monowave(df, mw_down) \ No newline at end of file diff --git a/models/MonoWave.py b/models/MonoWave.py index c58c4bf..4b671ec 100644 --- a/models/MonoWave.py +++ b/models/MonoWave.py @@ -1,8 +1,43 @@ from __future__ import annotations + import numpy as np + from models.functions import hi, lo, next_hi, next_lo + class MonoWave: + """ + A class representing a monowave pattern in financial time series data. + + Attributes: + - lows_arr (numpy.array): An array containing the lows of the monowave pattern. + - highs_arr (numpy.array): An array containing the highs of the monowave pattern. + - dates_arr (numpy.array): An array containing the dates corresponding to the monowave pattern. + - idx_start (int): The index of the starting point of the monowave pattern. + - skip_n (int, optional): The number of elements to skip during pattern analysis (default is 0). + - idx_end (int): The index of the ending point of the monowave pattern. + - count (int): The count of the monowave (e.g., 1, 2, A, B, etc.). + - degree (int): The degree of the monowave (1 = lowest timeframe level, 2 as soon as a pattern like 12345 is found, + etc.). + - date_start (str): The start date of the monowave pattern. + - date_end (str): The end date of the monowave pattern. + - low (float): The lowest value in the monowave pattern. + - high (float): The highest value in the monowave pattern. + - low_idx (int): The index of the lowest value in the monowave pattern. + - high_idx (int): The index of the highest value in the monowave pattern. + + Properties: + - labels (str): A property that returns the count of the monowave as a string. + - length (float): A property that returns the absolute difference between high and low values. + - duration (int): A property that returns the duration (index difference) of the monowave. + + Methods: + - from_wavepattern(cls, wave_pattern): A class method to create a MonoWave object from a given wave pattern. + + Note: + - This class is designed for analyzing financial time series data and identifying monowave patterns. + """ + def __init__(self, lows: np.array, highs: np.array, @@ -10,6 +45,7 @@ def __init__(self, idx_start: int, skip: int = 0): + # Constructor initializes the attributes self.lows_arr = lows self.highs_arr = highs self.dates_arr = dates @@ -17,8 +53,8 @@ def __init__(self, self.idx_start = idx_start self.idx_end = int - self.count = int # the count of the monowave, e.g. 1, 2, A, B, etc - self.degree = 1 # 1 = lowest timeframe level, 2 as soon as a e.g. 12345 is found etc. + self.count = int + self.degree = 1 self.date_start = str self.date_end = str @@ -28,21 +64,66 @@ def __init__(self, self.low_idx = int self.high_idx = int + def __sub__(self, other): + """ + Subtracts the length of another MonoWave object from the length of the current object. + + Args: + - other (MonoWave): Another MonoWave object to subtract. + + Returns: + float: The difference in length between the two MonoWave objects. + """ + return self.length - other.length + @property def labels(self) -> str: + """ + Property that returns the count of the monowave as a string. + + Returns: + str: The count of the monowave. + """ return str(self.count) @property def length(self) -> float: + """ + Property that returns the absolute difference between the high and low values. + + Returns: + float: The length of the monowave. + """ return abs(self.high - self.low) @property def duration(self) -> int: + """ + Property that returns the duration (index difference) of the monowave. + + Returns: + int: The duration of the monowave. + """ return self.idx_end - self.idx_start @classmethod def from_wavepattern(cls, wave_pattern): - lows = highs = dates = np.zeros(10) # dummy arrays to init class + """ + Class method tocreate a MonoWave object from a given wave pattern. + + Args: + - wave_pattern (WavePattern): The wave pattern object to create the MonoWave from. + + Returns: + MonoWave: The created MonoWave object. + + Raises: + ValueError: If the wave pattern has a number of waves other than 3 or 5. + + Note: + - This method is used to convert a WavePattern object into a MonoWave object for further analysis. + """ + lows = highs = dates = np.zeros(10) # dummy arrays to initialize the class if len(wave_pattern.waves.keys()) == 5: low = wave_pattern.waves.get('wave1').low @@ -77,15 +158,40 @@ def from_wavepattern(cls, wave_pattern): return monowave_down else: - raise ValueError('WavePattern other than 3 or 5 waves implemented, yet.') + raise ValueError('WavePattern other than 3 or 5 waves not implemented, yet.') class MonoWaveUp(MonoWave): """ - Describes a upwards movement, which can have [skip_n] smaller downtrends + A subclass of MonoWave representing an upward monowave pattern. + + Attributes: + - Inherits all attributes from the parent class MonoWave. + + Methods: + - __init__(*args, **kwargs): Initializes the MonoWaveUp object. + - find_end(): Finds the end of the MonoWaveUp pattern. + + Properties: + - dates (list): Returns the start and end dates of the MonoWaveUp pattern. + - points: Returns the low and high values of the MonoWaveUp pattern. + + Note: + - This class extends the functionality of the MonoWave class specifically for upward monowave patterns. """ def __init__(self, *args, **kwargs): + """ + Initializes the MonoWaveUp object. + + Args: + - *args: Variable length argument list. + - **kwargs: Arbitrary keyword arguments. + + Note: + - This constructor calls the constructor of the parent class (MonoWave) using super(). + - It also sets additional attributes specific to MonoWaveUp. + """ super().__init__(*args, **kwargs) self.high, self.high_idx = self.find_end() @@ -97,10 +203,14 @@ def __init__(self, *args, **kwargs): def find_end(self): """ - Finds the end of this MonoWave + Finds the end of this MonoWaveUp pattern. + + Returns: + tuple: The high value and its index that marks the end of the MonoWaveUp pattern. - :param idx_start: - :return: + Note: + - This method searches for the end of the upward pattern based on the lows and highs arrays. + - It considers the skip_n attribute to skip a certain number of elements during the search. """ high, high_idx = hi(self.lows_arr, self.highs_arr, self.idx_start) low_at_start = self.lows_arr[self.idx_start] @@ -109,7 +219,6 @@ def find_end(self): return None, None for _ in range(self.skip_n): - act_high, act_high_idx = next_hi(self.lows_arr, self.highs_arr, high_idx, high) if act_high is None: return None, None @@ -117,22 +226,64 @@ def find_end(self): if act_high > high: high = act_high high_idx = act_high_idx - if np.min(self.lows_arr[self.idx_start:act_high_idx] < low_at_start): + # if np.min(self.lows_arr[self.idx_start:act_high_idx] < low_at_start): + # return None, None + if self.idx_start <= act_high_idx and np.min(self.lows_arr[self.idx_start:act_high_idx]) < low_at_start: return None, None - return high, high_idx @property def dates(self) -> list: + """ + Property that returns the start and end dates of the MonoWaveUp pattern. + + Returns: + list: The start and end dates of the MonoWaveUp pattern. + """ return [self.date_start, self.date_end] @property def points(self): + """ + Property that returns the low and high values of the MonoWaveUp pattern. + + Returns: + tuple: The low and high values of the MonoWaveUp pattern. + """ return self.low, self.high class MonoWaveDown(MonoWave): + """ + A subclass of MonoWave representing a downward monowave pattern. + + Attributes: + - Inherits all attributes from the parent class MonoWave. + + Methods: + - __init__(*args, **kwargs): Initializes the MonoWaveDown object. + - find_end(): Finds the end of the MonoWaveDown pattern. + + Properties: + - dates (list): Returns the start and end dates of the MonoWaveDown pattern. + - points: Returns the high and low values of the MonoWaveDown pattern. + + Note: + - This class extends the functionality of the MonoWave class specifically for downward monowave patterns. + """ + def __init__(self, *args, **kwargs): + """ + Initializes the MonoWaveDown object. + + Args: + - *args: Variable length argument list. + - **kwargs: Arbitrary keyword arguments. + + Note: + - This constructor calls the constructor of the parent class (MonoWave) using super(). + - It also sets additional attributes specific to MonoWaveDown. + """ super().__init__(*args, **kwargs) self.low, self.low_idx = self.find_end() @@ -149,19 +300,35 @@ def __init__(self, *args, **kwargs): @property def dates(self) -> list: + """ + Property that returns the start and end dates of the MonoWaveDown pattern. + + Returns: + list: The start and end dates of the MonoWaveDown pattern. + """ return [self.date_start, self.date_end] @property def points(self): + """ + Property that returns the high and low values of the MonoWaveDown pattern. + + Returns: + tuple: The high and low values of the MonoWaveDown pattern. + """ return self.high, self.low def find_end(self): """ - Finds the end of this MonoWave (downwards) + Finds the end of this MonoWaveDown pattern. - :return: - """ + Returns: + tuple: The low value and its index that marks the end of the MonoWaveDown pattern. + Note: + - This method searches for the end of the downward pattern based on the lows and highs arrays. + - It considers the skip_n attribute to skip a certain number of elements during the search. + """ low, low_idx = lo(self.lows_arr, self.highs_arr, self.idx_start) high_at_start = self.highs_arr[self.idx_start] if low is None: @@ -181,7 +348,7 @@ def find_end(self): # TODO what to do if no more minima can be found? # if act_low > low: # return None, None - #if low > np.min(self.lows_arr[low_idx:]): + # if low > np.min(self.lows_arr[low_idx:]): # return None, None - #else: + # else: return low, low_idx diff --git a/models/__pycache__/MonoWave.cpython-39.pyc b/models/__pycache__/MonoWave.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc995da37174c7bd52feeac3f6b06d2e75687855 GIT binary patch literal 11081 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/WavePattern.cpython-39.pyc b/models/__pycache__/WavePattern.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2385dc8286c07aa623d9493af4de52e46318f917 GIT binary patch literal 4416 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 I>oj!u4;C#4fdBvi literal 0 HcmV?d00001 diff --git a/models/__pycache__/WaveRules.cpython-39.pyc b/models/__pycache__/WaveRules.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d87b2360fc8118f0345a36d13f69bee0b173909 GIT binary patch literal 9817 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/__init__.cpython-39.pyc b/models/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6f98869632b097bd06fb631ca773d492ad68278 GIT binary patch literal 144 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 literal 0 HcmV?d00001 diff --git a/tests/test_monowave.py b/tests/test_monowave.py index 1547c50..5579dea 100644 --- a/tests/test_monowave.py +++ b/tests/test_monowave.py @@ -1,12 +1,65 @@ -from models.MonoWave import MonoWaveUp +from models.MonoWave import MonoWave, MonoWaveUp # , MonoWaveDown import numpy as np +import unittest +from models.functions import hi, lo, next_hi, next_lo +class MonoWaveTest(unittest.TestCase): + def test_monowave_instance_is_created(self): + """ + This function tests whether an instance of the MonoWaveUp class is created correctly. -def test_monowave_instance_is_created(): - lows = np.random.rand(100) - highs = np.random.rand(100) - dates = np.random.rand(100) + Returns: + None - monowave_up = MonoWaveUp(lows, highs, dates, 0) + Raises: + AssertionError: If the created object is not an instance of MonoWaveUp class. + """ - assert isinstance(monowave_up, MonoWaveUp) \ No newline at end of file + # Generate random data for testing + lows = np.random.rand(100) + highs = np.random.rand(100) + dates = np.random.rand(100) + + # Create an instance of MonoWaveUp class + monowave_up = MonoWaveUp(lows, highs, dates, 0) + + # Check if the created object is an instance of MonoWaveUp class + assert isinstance(monowave_up, MonoWaveUp) + + +class MonoWaveUpTest(unittest.TestCase): + def setUp(self): + # Set up test data - fully synthetic + self.lows_arr = np.array(list([10, 8, 12, 6, 10, 4, 8, 2, 6])) + self.highs_arr = np.array(list([20, 18, 22, 16, 20, 14, 18, 12, 16])) + self.dates_arr = np.array(list(['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05', + '2021-01-06', '2021-01-07', '2021-01-08', '2021-01-09'])) + self.idx_start = 2 + self.skip_n = 1 + + def test_find_end(self): + monowave_up = MonoWaveUp(self.lows_arr, self.highs_arr, self.dates_arr, self.idx_start, self.skip_n) + high, high_idx = monowave_up.find_end() + + # Assert the expected high value and index + self.assertEqual(high, 22) + self.assertEqual(high_idx, 2) + + def test_dates_property(self): + monowave_up = MonoWaveUp(self.lows_arr, self.highs_arr, self.dates_arr, self.idx_start, self.skip_n) + expected_dates = ['2021-01-03', '2021-01-03'] + + # Assert the expected start and end dates + self.assertEqual(monowave_up.dates, expected_dates) + + def test_points_property(self): + monowave_up = MonoWaveUp(self.lows_arr, self.highs_arr, self.dates_arr, self.idx_start, self.skip_n) + expected_points = (12, 22) + + # Assert the expected low and high values + self.assertEqual(monowave_up.points, expected_points) + + + +if __name__ == '__main__': + unittest.main()