Refactor cycle detection and trend analysis; enhance trend detection with linear regression and moving averages. Update main script for improved data handling and visualization.
This commit is contained in:
parent
cbc6a7493d
commit
e9bfcd03eb
1
main.py
1
main.py
@ -6,6 +6,7 @@ from cycle_detector import CycleDetector
|
|||||||
# Load data from CSV file instead of database
|
# Load data from CSV file instead of database
|
||||||
data = pd.read_csv('data/btcusd_1-day_data.csv')
|
data = pd.read_csv('data/btcusd_1-day_data.csv')
|
||||||
|
|
||||||
|
|
||||||
# Convert datetime column to datetime type
|
# Convert datetime column to datetime type
|
||||||
start_date = pd.to_datetime('2025-04-01')
|
start_date = pd.to_datetime('2025-04-01')
|
||||||
stop_date = pd.to_datetime('2025-05-06')
|
stop_date = pd.to_datetime('2025-05-06')
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import logging
|
|||||||
from scipy.signal import find_peaks
|
from scipy.signal import find_peaks
|
||||||
import matplotlib.dates as mdates
|
import matplotlib.dates as mdates
|
||||||
from scipy import stats
|
from scipy import stats
|
||||||
|
from scipy import stats
|
||||||
|
|
||||||
class TrendDetectorSimple:
|
class TrendDetectorSimple:
|
||||||
def __init__(self, data, verbose=False):
|
def __init__(self, data, verbose=False):
|
||||||
@ -18,6 +19,44 @@ class TrendDetectorSimple:
|
|||||||
self.data = data
|
self.data = data
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
|
|
||||||
|
# Plot style configuration
|
||||||
|
self.plot_style = 'dark_background'
|
||||||
|
self.bg_color = '#181C27'
|
||||||
|
self.plot_size = (12, 8)
|
||||||
|
|
||||||
|
# Candlestick configuration
|
||||||
|
self.candle_width = 0.6
|
||||||
|
self.candle_up_color = '#089981'
|
||||||
|
self.candle_down_color = '#F23645'
|
||||||
|
self.candle_alpha = 0.8
|
||||||
|
self.wick_width = 1
|
||||||
|
|
||||||
|
# Marker configuration
|
||||||
|
self.min_marker = '^'
|
||||||
|
self.min_color = 'red'
|
||||||
|
self.min_size = 100
|
||||||
|
self.max_marker = 'v'
|
||||||
|
self.max_color = 'green'
|
||||||
|
self.max_size = 100
|
||||||
|
self.marker_zorder = 100
|
||||||
|
|
||||||
|
# Line configuration
|
||||||
|
self.line_width = 2
|
||||||
|
self.min_line_style = 'g--' # green dashed
|
||||||
|
self.max_line_style = 'r--' # red dashed
|
||||||
|
self.sma7_line_style = 'y-' # yellow solid
|
||||||
|
self.sma15_line_style = 'm-' # magenta solid
|
||||||
|
|
||||||
|
# Text configuration
|
||||||
|
self.title_size = 14
|
||||||
|
self.title_color = 'white'
|
||||||
|
self.axis_label_size = 12
|
||||||
|
self.axis_label_color = 'white'
|
||||||
|
|
||||||
|
# Legend configuration
|
||||||
|
self.legend_loc = 'best'
|
||||||
|
self.legend_bg_color = '#333333'
|
||||||
|
|
||||||
# Configure logging
|
# Configure logging
|
||||||
logging.basicConfig(level=logging.INFO if verbose else logging.WARNING,
|
logging.basicConfig(level=logging.INFO if verbose else logging.WARNING,
|
||||||
format='%(asctime)s - %(levelname)s - %(message)s')
|
format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
@ -34,6 +73,7 @@ class TrendDetectorSimple:
|
|||||||
|
|
||||||
self.logger.info(f"Initialized TrendDetectorSimple with {len(self.data)} data points")
|
self.logger.info(f"Initialized TrendDetectorSimple with {len(self.data)} data points")
|
||||||
|
|
||||||
|
def detect_trends(self):
|
||||||
def detect_trends(self):
|
def detect_trends(self):
|
||||||
"""
|
"""
|
||||||
Detect trends by identifying local minima and maxima in the price data
|
Detect trends by identifying local minima and maxima in the price data
|
||||||
@ -47,10 +87,13 @@ class TrendDetectorSimple:
|
|||||||
- DataFrame with columns for timestamps, prices, and trend indicators
|
- DataFrame with columns for timestamps, prices, and trend indicators
|
||||||
"""
|
"""
|
||||||
self.logger.info(f"Detecting trends")
|
self.logger.info(f"Detecting trends")
|
||||||
|
self.logger.info(f"Detecting trends")
|
||||||
|
|
||||||
df = self.data.copy()
|
df = self.data.copy()
|
||||||
close_prices = df['close'].values
|
close_prices = df['close'].values
|
||||||
|
|
||||||
|
max_peaks, _ = find_peaks(close_prices)
|
||||||
|
min_peaks, _ = find_peaks(-close_prices)
|
||||||
max_peaks, _ = find_peaks(close_prices)
|
max_peaks, _ = find_peaks(close_prices)
|
||||||
min_peaks, _ = find_peaks(-close_prices)
|
min_peaks, _ = find_peaks(-close_prices)
|
||||||
|
|
||||||
@ -79,11 +122,9 @@ class TrendDetectorSimple:
|
|||||||
# Calculate Simple Moving Averages (SMA) for 7 and 15 periods
|
# Calculate Simple Moving Averages (SMA) for 7 and 15 periods
|
||||||
self.logger.info("Calculating SMA-7 and SMA-15")
|
self.logger.info("Calculating SMA-7 and SMA-15")
|
||||||
|
|
||||||
# Calculate SMA values and exclude NaN values
|
sma_7 = pd.Series(close_prices).rolling(window=7, min_periods=1).mean().values
|
||||||
sma_7 = df['close'].rolling(window=7).mean().dropna().values
|
sma_15 = pd.Series(close_prices).rolling(window=15, min_periods=1).mean().values
|
||||||
sma_15 = df['close'].rolling(window=15).mean().dropna().values
|
|
||||||
|
|
||||||
# Add SMA values to regression_results
|
|
||||||
analysis_results = {}
|
analysis_results = {}
|
||||||
analysis_results['linear_regression'] = {
|
analysis_results['linear_regression'] = {
|
||||||
'min': {
|
'min': {
|
||||||
@ -107,6 +148,7 @@ class TrendDetectorSimple:
|
|||||||
|
|
||||||
return result, analysis_results
|
return result, analysis_results
|
||||||
|
|
||||||
|
def plot_trends(self, trend_data, analysis_results):
|
||||||
def plot_trends(self, trend_data, analysis_results):
|
def plot_trends(self, trend_data, analysis_results):
|
||||||
"""
|
"""
|
||||||
Plot the price data with detected trends using a candlestick chart.
|
Plot the price data with detected trends using a candlestick chart.
|
||||||
@ -120,18 +162,18 @@ class TrendDetectorSimple:
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
from matplotlib.patches import Rectangle
|
from matplotlib.patches import Rectangle
|
||||||
|
|
||||||
# Create the figure and axis
|
# Create the figure and axis with specified background
|
||||||
fig, ax = plt.subplots(figsize=(12, 8))
|
plt.style.use(self.plot_style)
|
||||||
|
fig, ax = plt.subplots(figsize=self.plot_size)
|
||||||
|
|
||||||
|
# Set the custom background color
|
||||||
|
fig.patch.set_facecolor(self.bg_color)
|
||||||
|
ax.set_facecolor(self.bg_color)
|
||||||
|
|
||||||
# Create a copy of the data
|
# Create a copy of the data
|
||||||
df = self.data.copy()
|
df = self.data.copy()
|
||||||
|
|
||||||
# Plot candlestick chart
|
|
||||||
up_color = 'green'
|
|
||||||
down_color = 'red'
|
|
||||||
|
|
||||||
# Draw candlesticks manually
|
# Draw candlesticks manually
|
||||||
width = 0.6
|
|
||||||
x_values = range(len(df))
|
x_values = range(len(df))
|
||||||
|
|
||||||
for i in range(len(df)):
|
for i in range(len(df)):
|
||||||
@ -142,26 +184,29 @@ class TrendDetectorSimple:
|
|||||||
low_val = df['low'].iloc[i]
|
low_val = df['low'].iloc[i]
|
||||||
|
|
||||||
# Determine candle color
|
# Determine candle color
|
||||||
color = up_color if close_val >= open_val else down_color
|
color = self.candle_up_color if close_val >= open_val else self.candle_down_color
|
||||||
|
|
||||||
# Plot candle body
|
# Plot candle body
|
||||||
body_height = abs(close_val - open_val)
|
body_height = abs(close_val - open_val)
|
||||||
bottom = min(open_val, close_val)
|
bottom = min(open_val, close_val)
|
||||||
rect = Rectangle((i - width/2, bottom), width, body_height, color=color, alpha=0.8)
|
rect = Rectangle((i - self.candle_width/2, bottom), self.candle_width, body_height,
|
||||||
|
color=color, alpha=self.candle_alpha)
|
||||||
ax.add_patch(rect)
|
ax.add_patch(rect)
|
||||||
|
|
||||||
# Plot candle wicks
|
# Plot candle wicks
|
||||||
ax.plot([i, i], [low_val, high_val], color='black', linewidth=1)
|
ax.plot([i, i], [low_val, high_val], color=color, linewidth=self.wick_width)
|
||||||
|
|
||||||
min_indices = trend_data.index[trend_data['is_min'] == True].tolist()
|
min_indices = trend_data.index[trend_data['is_min'] == True].tolist()
|
||||||
if min_indices:
|
if min_indices:
|
||||||
min_y = [df['close'].iloc[i] for i in min_indices]
|
min_y = [df['close'].iloc[i] for i in min_indices]
|
||||||
ax.scatter(min_indices, min_y, color='darkred', s=200, marker='^', label='Local Minima', zorder=100)
|
ax.scatter(min_indices, min_y, color=self.min_color, s=self.min_size,
|
||||||
|
marker=self.min_marker, label='Local Minima', zorder=self.marker_zorder)
|
||||||
|
|
||||||
max_indices = trend_data.index[trend_data['is_max'] == True].tolist()
|
max_indices = trend_data.index[trend_data['is_max'] == True].tolist()
|
||||||
if max_indices:
|
if max_indices:
|
||||||
max_y = [df['close'].iloc[i] for i in max_indices]
|
max_y = [df['close'].iloc[i] for i in max_indices]
|
||||||
ax.scatter(max_indices, max_y, color='darkgreen', s=200, marker='v', label='Local Maxima', zorder=100)
|
ax.scatter(max_indices, max_y, color=self.max_color, s=self.max_size,
|
||||||
|
marker=self.max_marker, label='Local Maxima', zorder=self.marker_zorder)
|
||||||
|
|
||||||
if analysis_results:
|
if analysis_results:
|
||||||
x_vals = np.arange(len(df))
|
x_vals = np.arange(len(df))
|
||||||
@ -169,33 +214,38 @@ class TrendDetectorSimple:
|
|||||||
min_slope = analysis_results['linear_regression']['min']['slope']
|
min_slope = analysis_results['linear_regression']['min']['slope']
|
||||||
min_intercept = analysis_results['linear_regression']['min']['intercept']
|
min_intercept = analysis_results['linear_regression']['min']['intercept']
|
||||||
min_line = min_slope * x_vals + min_intercept
|
min_line = min_slope * x_vals + min_intercept
|
||||||
ax.plot(x_vals, min_line, 'g--', linewidth=2, label='Minima Regression')
|
ax.plot(x_vals, min_line, self.min_line_style, linewidth=self.line_width,
|
||||||
|
label='Minima Regression')
|
||||||
|
|
||||||
# Maxima regression line (resistance)
|
# Maxima regression line (resistance)
|
||||||
max_slope = analysis_results['linear_regression']['max']['slope']
|
max_slope = analysis_results['linear_regression']['max']['slope']
|
||||||
max_intercept = analysis_results['linear_regression']['max']['intercept']
|
max_intercept = analysis_results['linear_regression']['max']['intercept']
|
||||||
max_line = max_slope * x_vals + max_intercept
|
max_line = max_slope * x_vals + max_intercept
|
||||||
ax.plot(x_vals, max_line, 'r--', linewidth=2, label='Maxima Regression')
|
ax.plot(x_vals, max_line, self.max_line_style, linewidth=self.line_width,
|
||||||
|
label='Maxima Regression')
|
||||||
|
|
||||||
# SMA-7 line
|
# SMA-7 line
|
||||||
sma_7 = analysis_results['sma']['7']
|
sma_7 = analysis_results['sma']['7']
|
||||||
ax.plot(x_vals, sma_7, 'y-', linewidth=2, label='SMA-7')
|
ax.plot(x_vals, sma_7, self.sma7_line_style, linewidth=self.line_width,
|
||||||
|
label='SMA-7')
|
||||||
|
|
||||||
# SMA-15 line
|
# SMA-15 line
|
||||||
# sma_15 = analysis_results['sma']['15']
|
sma_15 = analysis_results['sma']['15']
|
||||||
# valid_idx_15 = ~np.isnan(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')
|
ax.plot(x_vals[valid_idx_15], sma_15[valid_idx_15], self.sma15_line_style,
|
||||||
|
linewidth=self.line_width, label='SMA-15')
|
||||||
|
|
||||||
# Set title and labels
|
# Set title and labels
|
||||||
ax.set_title('Price Candlestick Chart with Local Minima and Maxima', fontsize=14)
|
ax.set_title('Price Candlestick Chart with Local Minima and Maxima',
|
||||||
ax.set_xlabel('Date', fontsize=12)
|
fontsize=self.title_size, color=self.title_color)
|
||||||
ax.set_ylabel('Price', fontsize=12)
|
ax.set_xlabel('Date', fontsize=self.axis_label_size, color=self.axis_label_color)
|
||||||
|
ax.set_ylabel('Price', fontsize=self.axis_label_size, color=self.axis_label_color)
|
||||||
|
|
||||||
# Set appropriate x-axis limits
|
# Set appropriate x-axis limits
|
||||||
ax.set_xlim(-0.5, len(df) - 0.5)
|
ax.set_xlim(-0.5, len(df) - 0.5)
|
||||||
|
|
||||||
# Add a legend
|
# Add a legend
|
||||||
ax.legend(loc='best')
|
ax.legend(loc=self.legend_loc, facecolor=self.legend_bg_color)
|
||||||
|
|
||||||
# Adjust layout
|
# Adjust layout
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user