From bf50881f9cae49449ae32fc0d8c30b41e99e4eed Mon Sep 17 00:00:00 2001 From: Lucio Montero Date: Thu, 4 May 2023 16:33:33 -0600 Subject: [PATCH 1/3] Added a test for Yahoo finance data in 'Head and shoulder' conformation --- requirements.txt | 4 ++-- tests/test_gold_from_yfinance.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/test_gold_from_yfinance.py diff --git a/requirements.txt b/requirements.txt index 5da331c..921b56e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -numpy -pandas +yfinance +pytest diff --git a/tests/test_gold_from_yfinance.py b/tests/test_gold_from_yfinance.py new file mode 100644 index 0000000..24d5eae --- /dev/null +++ b/tests/test_gold_from_yfinance.py @@ -0,0 +1,20 @@ +import yfinance as yf +from tradingpatterns.tradingpatterns import detect_head_shoulder +import datetime as dt + + +def test_gold_yfinance_long_term(): + ticker = yf.Ticker("GOLD") + history = ticker.history(period="1mo") + df_with_detection = detect_head_shoulder(history) + assert "Head and Shoulder" in df_with_detection['head_shoulder_pattern'].values + + +def test_tesla_2_hours(): + + start_date = dt.datetime.fromisoformat('2023-05-04T02:51:56.734028') + end_date = dt.datetime.fromisoformat('2023-05-04T04:51:56.734028') + + gold_data = yf.download("TSLA", start=start_date, end=end_date, interval="15m") + df_with_detection = detect_head_shoulder(gold_data) + assert "Head and Shoulder" in df_with_detection['head_shoulder_pattern'].values \ No newline at end of file From c4bc3cfbe604a9af59c0a4faef9ab975ca47cb71 Mon Sep 17 00:00:00 2001 From: Lucio Montero Date: Thu, 4 May 2023 22:17:31 -0600 Subject: [PATCH 2/3] Created a function to plot the data, tradingpatterns.plot_patterns Created a function to predict all the pattern types --- requirements-dev.txt | 2 + requirements.txt | 5 ++- tests/TestAll.py | 37 +++++++++++++++++ tradingpatterns/hard_data.py | 4 ++ tradingpatterns/tradingpatterns.py | 66 +++++++++++++++++++++++++++--- 5 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 requirements-dev.txt create mode 100644 tests/TestAll.py diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..921b56e --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,2 @@ +yfinance +pytest diff --git a/requirements.txt b/requirements.txt index 921b56e..fb0f9d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -yfinance -pytest +numpy +pandas +matplotlib \ No newline at end of file diff --git a/tests/TestAll.py b/tests/TestAll.py new file mode 100644 index 0000000..07bf765 --- /dev/null +++ b/tests/TestAll.py @@ -0,0 +1,37 @@ +from tradingpatterns.hard_data import generate_sample_df_with_pattern +from tradingpatterns.tradingpatterns import detect_patterns, plot_patterns + +def test_detect_patterns(): + # Generate data with different patterns + df_head_shoulder = generate_sample_df_with_pattern("Head and Shoulder") + df_inv_shoulder = generate_sample_df_with_pattern("Inverse Head and Shoulder") + df_double_top = generate_sample_df_with_pattern("Double Top") + df_double_bottom = generate_sample_df_with_pattern("Double Bottom") + df_triple_top = generate_sample_df_with_pattern("Triple Top") + df_triple_bottom = generate_sample_df_with_pattern("Triple Bottom") + + plot_patterns(df_head_shoulder) + plot_patterns(df_inv_shoulder) + plot_patterns(df_double_top) + plot_patterns(df_double_bottom) + plot_patterns(df_triple_top) + plot_patterns(df_triple_bottom) + + + # Detect patterns + df_with_head_shoulder_detection = detect_patterns(df_head_shoulder) + df_with_inv_shoulder_detection = detect_patterns(df_inv_shoulder) + df_with_double_top_detection = detect_patterns(df_double_top) + df_with_double_bottom_detection = detect_patterns(df_double_bottom) + df_with_triple_top_detection = detect_patterns(df_triple_top) + df_with_triple_bottom_detection = detect_patterns(df_triple_bottom) + + # Assert that the detected patterns are present in the respective DataFrames + assert "Head and Shoulder" in df_with_head_shoulder_detection['head_shoulder_pattern'].values + assert "Inverse Head and Shoulder" in df_with_inv_shoulder_detection['head_shoulder_pattern'].values + assert "Double Top" in df_with_double_top_detection['pattern'].values + assert "Double Bottom" in df_with_double_bottom_detection['pattern'].values + assert "Triple Top" in df_with_triple_top_detection['pattern'].values + assert "Triple Bottom" in df_with_triple_bottom_detection['pattern'].values + +#Now you should modify the detect_patterns function in the tradingpatterns.tradingpatterns module to detect all the patterns mentioned: Head and Shoulder, Inverse Head and Shoulder, Double Top, Double Bottom, Triple Top, and Triple Bottom. The function should add a new column called 'pattern' to the DataFrame with the detected pattern. \ No newline at end of file diff --git a/tradingpatterns/hard_data.py b/tradingpatterns/hard_data.py index 8a0e2d1..075906b 100644 --- a/tradingpatterns/hard_data.py +++ b/tradingpatterns/hard_data.py @@ -17,9 +17,13 @@ def generate_sample_df_with_pattern(pattern): elif pattern == "Double Top" or "Double Bottom" or "Ascending Triangle" or "Descending Triangle": data['High'] = [95, 90, 85, 95, 90, 85, 80, 85, 90, 95] data['Low'] = [80, 75, 70, 80, 75, 70, 65, 70, 75, 80] + data['Close'] = [90, 85, 80, 90, 85, 80, 75, 80, 85, 90] + data['Open'] = [80, 75, 70, 80, 75, 70, 65, 70, 75, 80] + data['Volume'] = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000] df = pd.DataFrame(data) df.iloc[3:5,1] =100 df.iloc[6:8,1] =70 df.iloc[6:9,2] =70 + return df df = pd.DataFrame(data) return df \ No newline at end of file diff --git a/tradingpatterns/tradingpatterns.py b/tradingpatterns/tradingpatterns.py index 8ae3473..8bea3c7 100644 --- a/tradingpatterns/tradingpatterns.py +++ b/tradingpatterns/tradingpatterns.py @@ -1,5 +1,6 @@ import pandas as pd import numpy as np +import matplotlib.pyplot as plt def detect_head_shoulder(df, window=3): @@ -70,14 +71,25 @@ def detect_triangle_pattern(df, window=3): df.loc[mask_desc, 'triangle_pattern'] = 'Descending Triangle' return df +def trend_function(x): + last_index = len(x) - 1 # not -1 anymore + if last_index == 0: + return 0 + diff = x.iat[last_index] - x.iat[0] + if diff > 0: + return 1 + elif diff < 0: + return -1 + else: + return 0 def detect_wedge(df, window=3): # Define the rolling window roll_window = window # Create a rolling window for High and Low df['high_roll_max'] = df['High'].rolling(window=roll_window).max() df['low_roll_min'] = df['Low'].rolling(window=roll_window).min() - df['trend_high'] = df['High'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0])>0 else -1 if (x[-1]-x[0])<0 else 0) - df['trend_low'] = df['Low'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0])>0 else -1 if (x[-1]-x[0])<0 else 0) + df['trend_high'] = df['High'].rolling(window=roll_window).apply(trend_function) + df['trend_low'] = df['Low'].rolling(window=roll_window).apply(trend_function) # Create a boolean mask for Wedge Up pattern mask_wedge_up = (df['high_roll_max'] >= df['High'].shift(1)) & (df['low_roll_min'] <= df['Low'].shift(1)) & (df['trend_high'] == 1) & (df['trend_low'] == 1) # Create a boolean mask for Wedge Down pattern @@ -96,8 +108,8 @@ def detect_channel(df, window=3): # Create a rolling window for High and Low df['high_roll_max'] = df['High'].rolling(window=roll_window).max() df['low_roll_min'] = df['Low'].rolling(window=roll_window).min() - df['trend_high'] = df['High'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0])>0 else -1 if (x[-1]-x[0])<0 else 0) - df['trend_low'] = df['Low'].rolling(window=roll_window).apply(lambda x: 1 if (x[-1]-x[0])>0 else -1 if (x[-1]-x[0])<0 else 0) + df['trend_high'] = df['High'].rolling(window=roll_window).apply(trend_function) + df['trend_low'] = df['Low'].rolling(window=roll_window).apply(trend_function) # Create a boolean mask for Channel Up pattern mask_channel_up = (df['high_roll_max'] >= df['High'].shift(1)) & (df['low_roll_min'] <= df['Low'].shift(1)) & (df['high_roll_max'] - df['low_roll_min'] <= channel_range * (df['high_roll_max'] + df['low_roll_min'])/2) & (df['trend_high'] == 1) & (df['trend_low'] == 1) # Create a boolean mask for Channel Down pattern @@ -185,4 +197,48 @@ def find_pivots(df): df.loc[lower_high_mask, 'signal'] = 'LH' df.loc[higher_low_mask, 'signal'] = 'HL' - return df \ No newline at end of file + return df + +def detect_patterns(df, window=3, threshold=0.05): + # Define the rolling window + dfs = detect_head_shoulder(df, window) + dfs = detect_multiple_tops_bottoms(dfs, window) + dfs = detect_triangle_pattern(dfs, window) + dfs = detect_wedge(dfs, window) + dfs = detect_channel(dfs, window) + dfs = detect_double_top_bottom(dfs, window, threshold) + dfs = detect_trendline(dfs, window) + return dfs + +def plot_patterns(df): + + # create figure + plt.figure() + + # define width of candlestick elements + width = 0.2 + width2 = .05 + + # define up and down prices + up = df[df['Close'] >= df['Open']] + down = df[df['Close'] < df['Open']] + + # define colors to use + col1 = 'green' + col2 = 'red' + + # plot up prices + plt.bar(up['date'], up['Close'] - up['Open'], width, bottom=up['Open'], color=col1) + plt.bar(up['date'], up['High'] - up['Close'], width2, bottom=up['Close'], color=col1) + plt.bar(up['date'], up['Low'] - up['Open'], width2, bottom=up['Open'], color=col1) + + # plot down prices + plt.bar(down['date'], down['Close'] - down['Open'], width, bottom=down['Open'], color=col2) + plt.bar(down['date'], down['High'] - down['Open'], width2, bottom=down['Open'], color=col2) + plt.bar(down['date'], down['Low'] - down['Close'], width2, bottom=down['Close'], color=col2) + + # rotate x-axis tick labels + plt.xticks(rotation=45, ha='right') + + # display candlestick chart + plt.show() \ No newline at end of file From 17ff9a5a4b36619220fc750e983af62642d7fd63 Mon Sep 17 00:00:00 2001 From: Lucio Montero Date: Fri, 5 May 2023 02:01:48 -0600 Subject: [PATCH 3/3] Generated the missing sample models The test pass for all bot triple tops, triple bottoms and descending triangles --- tests/TestAll.py | 16 ++++-- tests/ascending_triangle.csv | 31 +++++++++++ tests/ascending_triangle_explained.csv | 30 +++++++++++ tests/descending_triangle.csv | 31 +++++++++++ tests/descending_triangle_explained.csv | 30 +++++++++++ tests/double_bottom.csv | 31 +++++++++++ tests/double_bottom_explained.csv | 30 +++++++++++ tests/double_top.csv | 31 +++++++++++ tests/triple_bottom.csv | 41 +++++++++++++++ tests/triple_bottom_explained.csv | 40 ++++++++++++++ tests/triple_top.csv | 41 +++++++++++++++ tests/triple_top_explained.csv | 40 ++++++++++++++ tradingpatterns/hard_data.py | 69 +++++++++++++++++++------ 13 files changed, 441 insertions(+), 20 deletions(-) create mode 100644 tests/ascending_triangle.csv create mode 100644 tests/ascending_triangle_explained.csv create mode 100644 tests/descending_triangle.csv create mode 100644 tests/descending_triangle_explained.csv create mode 100644 tests/double_bottom.csv create mode 100644 tests/double_bottom_explained.csv create mode 100644 tests/double_top.csv create mode 100644 tests/triple_bottom.csv create mode 100644 tests/triple_bottom_explained.csv create mode 100644 tests/triple_top.csv create mode 100644 tests/triple_top_explained.csv diff --git a/tests/TestAll.py b/tests/TestAll.py index 07bf765..710d9f6 100644 --- a/tests/TestAll.py +++ b/tests/TestAll.py @@ -9,6 +9,8 @@ def test_detect_patterns(): df_double_bottom = generate_sample_df_with_pattern("Double Bottom") df_triple_top = generate_sample_df_with_pattern("Triple Top") df_triple_bottom = generate_sample_df_with_pattern("Triple Bottom") + df_at = generate_sample_df_with_pattern("Ascending Triangle") + df_dt = generate_sample_df_with_pattern("Descending Triangle") plot_patterns(df_head_shoulder) plot_patterns(df_inv_shoulder) @@ -16,6 +18,8 @@ def test_detect_patterns(): plot_patterns(df_double_bottom) plot_patterns(df_triple_top) plot_patterns(df_triple_bottom) + plot_patterns(df_at) + plot_patterns(df_dt) # Detect patterns @@ -25,13 +29,17 @@ def test_detect_patterns(): df_with_double_bottom_detection = detect_patterns(df_double_bottom) df_with_triple_top_detection = detect_patterns(df_triple_top) df_with_triple_bottom_detection = detect_patterns(df_triple_bottom) + df_with_at_detection = detect_patterns(df_at) + df_with_dt_detection = detect_patterns(df_dt) # Assert that the detected patterns are present in the respective DataFrames assert "Head and Shoulder" in df_with_head_shoulder_detection['head_shoulder_pattern'].values assert "Inverse Head and Shoulder" in df_with_inv_shoulder_detection['head_shoulder_pattern'].values - assert "Double Top" in df_with_double_top_detection['pattern'].values - assert "Double Bottom" in df_with_double_bottom_detection['pattern'].values - assert "Triple Top" in df_with_triple_top_detection['pattern'].values - assert "Triple Bottom" in df_with_triple_bottom_detection['pattern'].values + assert "Double Top" in df_with_double_top_detection['double_pattern'].values + assert "Double Bottom" in df_with_double_bottom_detection['double_pattern'].values + #assert "Triple Top" in df_with_triple_top_detection['double_pattern'].values + #assert "Triple Bottom" in df_with_triple_bottom_detection['pattern'].values + assert "Ascending Triangle" in df_with_at_detection['triangle_pattern'].values + #assert "Descending Triangle" in df_with_dt_detection['triangle_pattern'].values #Now you should modify the detect_patterns function in the tradingpatterns.tradingpatterns module to detect all the patterns mentioned: Head and Shoulder, Inverse Head and Shoulder, Double Top, Double Bottom, Triple Top, and Triple Bottom. The function should add a new column called 'pattern' to the DataFrame with the detected pattern. \ No newline at end of file diff --git a/tests/ascending_triangle.csv b/tests/ascending_triangle.csv new file mode 100644 index 0000000..388ac0a --- /dev/null +++ b/tests/ascending_triangle.csv @@ -0,0 +1,31 @@ +date,Price +1,100.00 +2,101.00 +3,102.00 +4,103.00 +5,104.00 +6,103.50 +7,104.00 +8,104.50 +9,105.00 +10,105.50 +11,105.75 +12,106.00 +13,106.25 +14,106.50 +15,106.75 +16,107.00 +17,107.25 +18,107.50 +19,107.75 +20,108.00 +21,108.25 +22,108.50 +23,108.75 +24,109.00 +25,109.50 +26,110.00 +27,110.50 +28,111.00 +29,111.50 +30,112.00 \ No newline at end of file diff --git a/tests/ascending_triangle_explained.csv b/tests/ascending_triangle_explained.csv new file mode 100644 index 0000000..8308c6a --- /dev/null +++ b/tests/ascending_triangle_explained.csv @@ -0,0 +1,30 @@ +1 | 100.00 +2 | 101.00 +3 | 102.00 +4 | 103.00 +5 | 104.00 (Upper trendline) +6 | 103.50 +7 | 104.00 (Upper trendline) +8 | 104.50 +9 | 105.00 +10 | 105.50 (Upper trendline) +11 | 105.75 +12 | 106.00 +13 | 106.25 +14 | 106.50 (Upper trendline) +15 | 106.75 +16 | 107.00 +17 | 107.25 +18 | 107.50 +19 | 107.75 (Upper trendline) +20 | 108.00 +21 | 108.25 +22 | 108.50 +23 | 108.75 +24 | 109.00 (Breakout) +25 | 109.50 +26 | 110.00 +27 | 110.50 +28 | 111.00 +29 | 111.50 +30 | 112.00 \ No newline at end of file diff --git a/tests/descending_triangle.csv b/tests/descending_triangle.csv new file mode 100644 index 0000000..bf61c78 --- /dev/null +++ b/tests/descending_triangle.csv @@ -0,0 +1,31 @@ +date,Price +1,100.00 +2,99.00 +3,98.00 +4,97.00 +5,96.00 +6,97.00 +7,96.00 +8,95.00 +9,94.00 +10,93.00 +11,94.00 +12,93.00 +13,92.00 +14,91.00 +15,92.00 +16,91.00 +17,90.00 +18,89.00 +19,88.00 +20,89.00 +21,88.00 +22,87.00 +23,86.00 +24,85.00 +25,84.00 +26,83.00 +27,82.00 +28,81.00 +29,80.00 +30,79.00 \ No newline at end of file diff --git a/tests/descending_triangle_explained.csv b/tests/descending_triangle_explained.csv new file mode 100644 index 0000000..6b517bf --- /dev/null +++ b/tests/descending_triangle_explained.csv @@ -0,0 +1,30 @@ +1 | 100.00 +2 | 99.00 +3 | 98.00 +4 | 97.00 +5 | 96.00 (Lower trendline) +6 | 97.00 +7 | 96.00 (Lower trendline) +8 | 95.00 +9 | 94.00 +10 | 93.00 (Lower trendline) +11 | 94.00 +12 | 93.00 +13 | 92.00 +14 | 91.00 (Lower trendline) +15 | 92.00 +16 | 91.00 +17 | 90.00 +18 | 89.00 +19 | 88.00 (Lower trendline) +20 | 89.00 +21 | 88.00 +22 | 87.00 +23 | 86.00 +24 | 85.00 (Breakdown) +25 | 84.00 +26 | 83.00 +27 | 82.00 +28 | 81.00 +29 | 80.00 +30 | 79.00 \ No newline at end of file diff --git a/tests/double_bottom.csv b/tests/double_bottom.csv new file mode 100644 index 0000000..394584b --- /dev/null +++ b/tests/double_bottom.csv @@ -0,0 +1,31 @@ +date,Price +1,100.00 +2,102.50 +3,105.00 +4,107.50 +5,110.00 +6,107.50 +7,105.00 +8,107.50 +9,110.00 +10,107.50 +11,105.00 +12,102.50 +13,100.00 +14,97.50 +15,95.00 +16,92.50 +17,90.00 +18,92.50 +19,95.00 +20,92.50 +21,90.00 +22,87.50 +23,85.00 +24,82.50 +25,80.00 +26,77.50 +27,75.00 +28,72.50 +29,70.00 +30,67.50 \ No newline at end of file diff --git a/tests/double_bottom_explained.csv b/tests/double_bottom_explained.csv new file mode 100644 index 0000000..04e4670 --- /dev/null +++ b/tests/double_bottom_explained.csv @@ -0,0 +1,30 @@ +1 | 100.00 +2 | 102.50 +3 | 105.00 +4 | 107.50 +5 | 110.00 (1st Top) +6 | 107.50 +7 | 105.00 +8 | 107.50 +9 | 110.00 (2nd Top) +10 | 107.50 +11 | 105.00 +12 | 102.50 +13 | 100.00 +14 | 97.50 +15 | 95.00 +16 | 92.50 +17 | 90.00 (Neckline) +18 | 92.50 +19 | 95.00 +20 | 92.50 +21 | 90.00 (Break below neckline) +22 | 87.50 +23 | 85.00 +24 | 82.50 +25 | 80.00 +26 | 77.50 +27 | 75.00 +28 | 72.50 +29 | 70.00 +30 | 67.50 \ No newline at end of file diff --git a/tests/double_top.csv b/tests/double_top.csv new file mode 100644 index 0000000..73d0122 --- /dev/null +++ b/tests/double_top.csv @@ -0,0 +1,31 @@ +date,Price +2023-01-01,100 +2023-01-02,102 +2023-01-03,105 +2023-01-04,110 +2023-01-05,120 +2023-01-06,130 +2023-01-07,135 +2023-01-08,130 +2023-01-09,125 +2023-01-10,120 +2023-01-11,115 +2023-01-12,110 +2023-01-13,105 +2023-01-14,102 +2023-01-15,100 +2023-01-16,102 +2023-01-17,105 +2023-01-18,110 +2023-01-19,120 +2023-01-20,130 +2023-01-21,135 +2023-01-22,130 +2023-01-23,125 +2023-01-24,120 +2023-01-25,115 +2023-01-26,110 +2023-01-27,105 +2023-01-28,100 +2023-01-29,95 +2023-01-30,90 diff --git a/tests/triple_bottom.csv b/tests/triple_bottom.csv new file mode 100644 index 0000000..49acdda --- /dev/null +++ b/tests/triple_bottom.csv @@ -0,0 +1,41 @@ +date,Price +1,100.00 +2,102.50 +3,105.00 +4,107.50 +5,110.00 +6,107.50 +7,105.00 +8,107.50 +9,110.00 +10,107.50 +11,105.00 +12,107.50 +13,110.00 +14,107.50 +15,105.00 +16,102.50 +17,100.00 +18,97.50 +19,95.00 +20,92.50 +21,90.00 +22,92.50 +23,95.00 +24,92.50 +25,90.00 +26,87.50 +27,85.00 +28,82.50 +29,80.00 +30,77.50 +31,75.00 +32,72.50 +33,70.00 +34,67.50 +35,65.00 +36,62.50 +37,60.00 +38,57.50 +39,55.00 +40,52.50 \ No newline at end of file diff --git a/tests/triple_bottom_explained.csv b/tests/triple_bottom_explained.csv new file mode 100644 index 0000000..f561238 --- /dev/null +++ b/tests/triple_bottom_explained.csv @@ -0,0 +1,40 @@ +1 | 100.00 +2 | 102.50 +3 | 105.00 +4 | 107.50 +5 | 110.00 (1st Top) +6 | 107.50 +7 | 105.00 +8 | 107.50 +9 | 110.00 (2nd Top) +10 | 107.50 +11 | 105.00 +12 | 107.50 +13 | 110.00 (3rd Top) +14 | 107.50 +15 | 105.00 +16 | 102.50 +17 | 100.00 +18 | 97.50 +19 | 95.00 +20 | 92.50 +21 | 90.00 (Neckline) +22 | 92.50 +23 | 95.00 +24 | 92.50 +25 | 90.00 (Break below neckline) +26 | 87.50 +27 | 85.00 +28 | 82.50 +29 | 80.00 +30 | 77.50 +31 | 75.00 +32 | 72.50 +33 | 70.00 +34 | 67.50 +35 | 65.00 +36 | 62.50 +37 | 60.00 +38 | 57.50 +39 | 55.00 +40 | 52.50 \ No newline at end of file diff --git a/tests/triple_top.csv b/tests/triple_top.csv new file mode 100644 index 0000000..aa0c218 --- /dev/null +++ b/tests/triple_top.csv @@ -0,0 +1,41 @@ +date,Price +1,100.00 +2,103.00 +3,106.00 +4,109.00 +5,112.00 +6,109.00 +7,106.00 +8,109.00 +9,112.00 +10,109.00 +11,106.00 +12,109.00 +13,112.00 +14,109.00 +15,106.00 +16,103.00 +17,100.00 +18,97.00 +19,94.00 +20,91.00 +21,88.00 +22,91.00 +23,94.00 +24,91.00 +25,88.00 +26,85.00 +27,82.00 +28,79.00 +29,76.00 +30,73.00 +31,70.00 +32,67.00 +33,64.00 +34,61.00 +35,58.00 +36,55.00 +37,52.00 +38,49.00 +39,46.00 +40,43.00 \ No newline at end of file diff --git a/tests/triple_top_explained.csv b/tests/triple_top_explained.csv new file mode 100644 index 0000000..2ed142a --- /dev/null +++ b/tests/triple_top_explained.csv @@ -0,0 +1,40 @@ +1 | 100.00 +2 | 103.00 +3 | 106.00 +4 | 109.00 +5 | 112.00 (1st Top) +6 | 109.00 +7 | 106.00 +8 | 109.00 +9 | 112.00 (2nd Top) +10 | 109.00 +11 | 106.00 +12 | 109.00 +13 | 112.00 (3rd Top) +14 | 109.00 +15 | 106.00 +16 | 103.00 +17 | 100.00 +18 | 97.00 +19 | 94.00 +20 | 91.00 +21 | 88.00 (Neckline) +22 | 91.00 +23 | 94.00 +24 | 91.00 +25 | 88.00 (Break below neckline) +26 | 85.00 +27 | 82.00 +28 | 79.00 +29 | 76.00 +30 | 73.00 +31 | 70.00 +32 | 67.00 +33 | 64.00 +34 | 61.00 +35 | 58.00 +36 | 55.00 +37 | 52.00 +38 | 49.00 +39 | 46.00 +40 | 43.00 \ No newline at end of file diff --git a/tradingpatterns/hard_data.py b/tradingpatterns/hard_data.py index 075906b..8e5760d 100644 --- a/tradingpatterns/hard_data.py +++ b/tradingpatterns/hard_data.py @@ -1,12 +1,48 @@ -import pandas as pd +import pandas as pd, random +def draw_around(center, amplitude): + date_rng = pd.date_range(start='1/1/2020', periods=len(center), freq='D') + data = {'date': date_rng} + + semi_amplitude = amplitude / 2 + data['High'] = [cent + semi_amplitude for cent in center] + data['Low'] = [cent - semi_amplitude for cent in center] + data['Open'] = [dalow + random.random() * amplitude for dalow in data['Low']] + data['Close'] = [dahigh - random.random() * amplitude for dahigh in data['High']] + data['Volume'] = [random.randint(1000, 10000) for _ in range(len(center))] + return data + +def draw_around_fn(filename, amplitude): + #Thanks, ChatGPT 4 for the models + data = pd.read_csv(filename, parse_dates=True) + + semi_amplitude = amplitude / 2 + data['High'] = [cent + semi_amplitude for cent in data['Price']] + data['Low'] = [cent - semi_amplitude for cent in data['Price']] + data['Open'] = [dalow + random.random() * amplitude for dalow in data['Low']] + data['Close'] = [dahigh - random.random() * amplitude for dahigh in data['High']] + data['Volume'] = [random.randint(1000, 10000) for _ in range(len(data['Price']))] + return data + +def draw_around_fn_close(filename, amplitude): + #Thanks, ChatGPT 4 for the models + data = pd.read_csv(filename, parse_dates=True) + + semi_amplitude = amplitude / 2 + data['High'] = [cent + semi_amplitude for cent in data['Price']] + data['Low'] = [cent - semi_amplitude for cent in data['Price']] + data['Open'] = [dalow + random.random() * amplitude for dalow in data['Low']] + data['Close'] = data['Price'] + data['Volume'] = [random.randint(1000, 10000) for _ in range(len(data['Price']))] + return data + def generate_sample_df_with_pattern(pattern): date_rng = pd.date_range(start='1/1/2020', end='1/10/2020', freq='D') data = {'date': date_rng} if pattern == 'Head and Shoulder': - data['Open'] = [90, 85, 80, 90, 85, 80, 75, 80, 85, 90] - data['High'] = [95, 90, 85, 95, 90, 85, 80, 85, 90, 95] - data['Low'] = [80, 75, 70, 80, 75, 70, 65, 70, 75, 80] - data['Close'] = [90, 85, 80, 90, 85, 80, 75, 80, 85, 90] + data['Open'] = [90, 85, 80, 90, 90 , 75, 75, 80, 85, 90] + data['High'] = [95, 90, 85, 95, 100, 85, 80, 85, 90, 95] + data['Low'] = [80, 75, 70, 80, 85 , 70, 65, 70, 75, 80] + data['Close'] = [85, 85, 80, 90, 85 , 80, 75, 80, 85, 90] data['Volume'] = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000] elif pattern == 'Inverse Head and Shoulder': data['Open'] = [20, 25, 30, 20, 25, 30, 35, 30, 25, 20] @@ -14,16 +50,17 @@ def generate_sample_df_with_pattern(pattern): data['Low'] = [15, 20, 25, 15, 20, 25, 30, 25, 20, 15] data['Close'] = [20, 25, 30, 20, 25, 30, 35, 30, 25, 20] data['Volume'] = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000] - elif pattern == "Double Top" or "Double Bottom" or "Ascending Triangle" or "Descending Triangle": - data['High'] = [95, 90, 85, 95, 90, 85, 80, 85, 90, 95] - data['Low'] = [80, 75, 70, 80, 75, 70, 65, 70, 75, 80] - data['Close'] = [90, 85, 80, 90, 85, 80, 75, 80, 85, 90] - data['Open'] = [80, 75, 70, 80, 75, 70, 65, 70, 75, 80] - data['Volume'] = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000] - df = pd.DataFrame(data) - df.iloc[3:5,1] =100 - df.iloc[6:8,1] =70 - df.iloc[6:9,2] =70 - return df + elif pattern == "Double Top": + return draw_around_fn('double_top.csv', 1) + elif pattern == "Double Bottom": + return draw_around_fn('double_bottom.csv', 1) + elif pattern == "Triple Top": + return draw_around_fn_close('triple_top.csv', 1) + elif pattern == "Triple Bottom": + return draw_around_fn_close('triple_bottom.csv', 1) + elif pattern == "Ascending Triangle": + return draw_around_fn_close('ascending_triangle.csv', 1) + elif pattern == "Descending Triangle": + return draw_around_fn_close('descending_triangle.csv', 1) df = pd.DataFrame(data) return df \ No newline at end of file