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 0000000..fc995da Binary files /dev/null and b/models/__pycache__/MonoWave.cpython-39.pyc differ diff --git a/models/__pycache__/WavePattern.cpython-39.pyc b/models/__pycache__/WavePattern.cpython-39.pyc new file mode 100644 index 0000000..2385dc8 Binary files /dev/null and b/models/__pycache__/WavePattern.cpython-39.pyc differ diff --git a/models/__pycache__/WaveRules.cpython-39.pyc b/models/__pycache__/WaveRules.cpython-39.pyc new file mode 100644 index 0000000..3d87b23 Binary files /dev/null and b/models/__pycache__/WaveRules.cpython-39.pyc differ diff --git a/models/__pycache__/__init__.cpython-39.pyc b/models/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..a6f9886 Binary files /dev/null and b/models/__pycache__/__init__.cpython-39.pyc differ diff --git a/models/__pycache__/functions.cpython-39.pyc b/models/__pycache__/functions.cpython-39.pyc new file mode 100644 index 0000000..b479c7d Binary files /dev/null and b/models/__pycache__/functions.cpython-39.pyc differ diff --git a/models/__pycache__/helpers.cpython-39.pyc b/models/__pycache__/helpers.cpython-39.pyc new file mode 100644 index 0000000..974ef62 Binary files /dev/null and b/models/__pycache__/helpers.cpython-39.pyc differ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..77250fa --- /dev/null +++ b/setup.py @@ -0,0 +1,12 @@ +# Copyright (c) 2023 Nightingale Professional Services, LLC +# +# This code is licensed under a commercial License. +# See LICENSE file for full text. +from setuptools import setup, find_packages + + +setup( + name="Elliott Wave Analyzer", + version="0.1", + packages=find_packages(), +) \ No newline at end of file diff --git a/tests/__pycache__/test_monowave.cpython-39.pyc b/tests/__pycache__/test_monowave.cpython-39.pyc new file mode 100644 index 0000000..b9fe28c Binary files /dev/null and b/tests/__pycache__/test_monowave.cpython-39.pyc differ 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()