diff --git a/main.py b/main.py index a568aef..4df366c 100644 --- a/main.py +++ b/main.py @@ -15,8 +15,8 @@ daily_data = data[(pd.to_datetime(data['datetime']) >= start_date) & print(f"Number of data points: {len(daily_data)}") trend_detector = TrendDetectorSimple(daily_data, verbose=True) -trends = trend_detector.detect_trends() -trend_detector.plot_trends(trends) +trends, analysis_results = trend_detector.detect_trends() +trend_detector.plot_trends(trends, analysis_results) #trend_detector = TrendDetectorMACD(daily_data, True) #trends = trend_detector.detect_trends_MACD_signal() diff --git a/requirements.txt b/requirements.txt index 2f171f5..a08bc15 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/trend_detector_simple.py b/trend_detector_simple.py index cee8433..141998c 100644 --- a/trend_detector_simple.py +++ b/trend_detector_simple.py @@ -3,6 +3,7 @@ import numpy as np import logging from scipy.signal import find_peaks import matplotlib.dates as mdates +from scipy import stats class TrendDetectorSimple: def __init__(self, data, verbose=False): @@ -63,11 +64,50 @@ class TrendDetectorSimple: for peak in min_peaks: df.at[peak, 'is_min'] = True - result = df[['datetime', 'close', 'is_min', 'is_max']] + result = df[['datetime', 'close', 'is_min', 'is_max']].copy() - return result + # Perform linear regression on min_peaks and max_peaks + self.logger.info("Performing linear regression on min and max peaks") + min_prices = df['close'].iloc[min_peaks].values + max_prices = df['close'].iloc[max_peaks].values + + # Linear regression for min peaks if we have at least 2 points + min_slope, min_intercept, min_r_value, _, _ = stats.linregress(min_peaks, min_prices) + # Linear regression for max peaks if we have at least 2 points + max_slope, max_intercept, max_r_value, _, _ = stats.linregress(max_peaks, max_prices) - def plot_trends(self, trend_data): + # Calculate Simple Moving Averages (SMA) for 7 and 15 periods + self.logger.info("Calculating SMA-7 and SMA-15") + + # Calculate SMA values and exclude NaN values + sma_7 = df['close'].rolling(window=7).mean().dropna().values + sma_15 = df['close'].rolling(window=15).mean().dropna().values + + # Add SMA values to regression_results + analysis_results = {} + analysis_results['linear_regression'] = { + 'min': { + 'slope': min_slope, + 'intercept': min_intercept, + 'r_squared': min_r_value ** 2 + }, + 'max': { + 'slope': max_slope, + 'intercept': max_intercept, + 'r_squared': max_r_value ** 2 + } + } + analysis_results['sma'] = { + '7': sma_7, + '15': sma_15 + } + + self.logger.info(f"Min peaks regression: slope={min_slope:.4f}, intercept={min_intercept:.4f}, r²={min_r_value**2:.4f}") + self.logger.info(f"Max peaks regression: slope={max_slope:.4f}, intercept={max_intercept:.4f}, r²={max_r_value**2:.4f}") + + return result, analysis_results + + def plot_trends(self, trend_data, analysis_results): """ Plot the price data with detected trends using a candlestick chart. @@ -116,12 +156,35 @@ class TrendDetectorSimple: min_indices = trend_data.index[trend_data['is_min'] == True].tolist() if min_indices: min_y = [df['close'].iloc[i] for i in min_indices] - ax.scatter(min_indices, min_y, color='black', s=200, marker='^', label='Local Minima', zorder=100) + ax.scatter(min_indices, min_y, color='darkred', s=200, marker='^', label='Local Minima', zorder=100) max_indices = trend_data.index[trend_data['is_max'] == True].tolist() if max_indices: max_y = [df['close'].iloc[i] for i in max_indices] - ax.scatter(max_indices, max_y, color='black', s=200, marker='v', label='Local Maxima', zorder=100) + ax.scatter(max_indices, max_y, color='darkgreen', s=200, marker='v', label='Local Maxima', zorder=100) + + if analysis_results: + x_vals = np.arange(len(df)) + # Minima regression line (support) + min_slope = analysis_results['linear_regression']['min']['slope'] + min_intercept = analysis_results['linear_regression']['min']['intercept'] + min_line = min_slope * x_vals + min_intercept + ax.plot(x_vals, min_line, 'g--', linewidth=2, label='Minima Regression') + + # Maxima regression line (resistance) + max_slope = analysis_results['linear_regression']['max']['slope'] + max_intercept = analysis_results['linear_regression']['max']['intercept'] + max_line = max_slope * x_vals + max_intercept + ax.plot(x_vals, max_line, 'r--', linewidth=2, label='Maxima Regression') + + # SMA-7 line + sma_7 = analysis_results['sma']['7'] + ax.plot(x_vals, sma_7, 'y-', linewidth=2, label='SMA-7') + + # SMA-15 line + # sma_15 = analysis_results['sma']['15'] + # valid_idx_15 = ~np.isnan(sma_15) + # ax.plot(x_vals[valid_idx_15], sma_15[valid_idx_15], 'm-', linewidth=2, label='SMA-15') # Set title and labels ax.set_title('Price Candlestick Chart with Local Minima and Maxima', fontsize=14)