Skip to content

Conversation

@HARI21HP
Copy link

@HARI21HP HARI21HP commented Dec 7, 2025

No description provided.

Copilot AI review requested due to automatic review settings December 7, 2025 20:31
@github-actions github-actions bot requested a review from iamwatchdogs December 7, 2025 20:31
@github-actions
Copy link

github-actions bot commented Dec 7, 2025

👋 @HARI21HP
Thank you for raising your pull request.
Please make sure you have followed our contributing guidelines. We will review it as soon as possible.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a comprehensive Stock Price Tracker application that fetches stock data from Yahoo Finance, creates visualizations, and sends Telegram alerts for price movements. The implementation is well-structured with separate utility modules for data fetching, plotting, and notifications.

Key Changes:

  • New Stock Price Tracker application with CLI interface
  • Yahoo Finance integration for real-time and historical stock data
  • Matplotlib and Plotly-based visualization system with multiple chart types
  • Telegram bot integration for automated price alerts and notifications

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 28 comments.

Show a summary per file
File Description
Stock-Price-Tracker/stock_tracker.py Main CLI application orchestrating stock tracking, visualization, and alerts
Stock-Price-Tracker/utils/yahoo_finance.py Yahoo Finance API wrapper providing stock data fetching and price calculations
Stock-Price-Tracker/utils/telegram_alert.py Telegram bot integration for sending formatted stock price alerts
Stock-Price-Tracker/utils/plotter.py Visualization module creating price trends, candlestick charts, and summary reports
Stock-Price-Tracker/utils/__init__.py Package initialization exposing main utility classes
Stock-Price-Tracker/requirements.txt Python dependencies including yfinance, pandas, matplotlib, plotly, and telegram bot
Stock-Price-Tracker/config/.env.example Environment variable template for Telegram bot configuration
Stock-Price-Tracker/README.md Comprehensive documentation with features, installation, usage examples, and project structure
Comments suppressed due to low confidence (1)

Stock-Price-Tracker/utils/yahoo_finance.py:204

  • Except block directly handles BaseException.
        except:

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

'52_week_low': info.get('fiftyTwoWeekLow', 'N/A'),
}
except Exception as e:
print(f"Error fetching stock info: {e}")
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message only prints the exception object but doesn't provide context about which ticker failed or guidance for users. Consider enhancing it to: print(f"Error fetching stock info for {self.ticker}: {e}") to make debugging easier.

Suggested change
print(f"Error fetching stock info: {e}")
print(f"Error fetching stock info for {self.ticker}: {e}")

Copilot uses AI. Check for mistakes.
'lowest': round(lowest_price, 2)
}
except Exception as e:
print(f"Error calculating price change: {e}")
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message should include the ticker symbol for context: print(f"Error calculating price change for {self.ticker}: {e}")

Suggested change
print(f"Error calculating price change: {e}")
print(f"Error calculating price change for {self.ticker}: {e}")

Copilot uses AI. Check for mistakes.
Change: ${stats.get('change', 'N/A')} ({stats.get('change_percent', 'N/A')}%)
Highest: ${stats.get('highest', 'N/A')}
Lowest: ${stats.get('lowest', 'N/A')}
Range: ${stats.get('highest', 0) - stats.get('lowest', 0):.2f}
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calculation ${stats.get('highest', 0) - stats.get('lowest', 0):.2f} can produce incorrect results if either 'highest' or 'lowest' is missing (defaulting to 0). The range calculation should handle missing values more gracefully, perhaps by checking if both values exist first or using a more appropriate default like 'N/A'.

Copilot uses AI. Check for mistakes.
Comment on lines +202 to +203
info = self.stock.info
return 'symbol' in info or 'longName' in info
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to get_current_price(), this method fetches all stock info via an API call just to validate the ticker. This is inefficient if called multiple times. Consider caching the validation result or implementing a lighter validation method.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +81

# Plot closing price
plt.plot(data.index, data['Close'], label='Close Price',
color='#2E86C1', linewidth=2)

# Add moving averages
if len(data) >= 7:
ma7 = data['Close'].rolling(window=7).mean()
plt.plot(data.index, ma7, label='7-Day MA',
color='#E67E22', linestyle='--', linewidth=1.5)

if len(data) >= 30:
ma30 = data['Close'].rolling(window=30).mean()
plt.plot(data.index, ma30, label='30-Day MA',
color='#27AE60', linestyle='--', linewidth=1.5)

# Formatting
if title is None:
title = f'{self.ticker} Stock Price Trend'
plt.title(title, fontsize=16, fontweight='bold', pad=20)
plt.xlabel('Date', fontsize=12, fontweight='bold')
plt.ylabel('Price ($)', fontsize=12, fontweight='bold')
plt.legend(loc='best', fontsize=10)
plt.grid(True, alpha=0.3, linestyle='--')

# Format x-axis
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gcf().autofmt_xdate()

plt.tight_layout()

if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"Plot saved to {save_path}")

if show_plot:
plt.show()
else:
plt.close()
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating a new figure without explicitly closing it when show_plot=False can lead to memory leaks in long-running applications. While plt.close() is called on line 81, if any exception occurs between lines 42-80, the figure remains in memory. Consider using a context manager or adding a try-finally block to ensure cleanup.

Suggested change
# Plot closing price
plt.plot(data.index, data['Close'], label='Close Price',
color='#2E86C1', linewidth=2)
# Add moving averages
if len(data) >= 7:
ma7 = data['Close'].rolling(window=7).mean()
plt.plot(data.index, ma7, label='7-Day MA',
color='#E67E22', linestyle='--', linewidth=1.5)
if len(data) >= 30:
ma30 = data['Close'].rolling(window=30).mean()
plt.plot(data.index, ma30, label='30-Day MA',
color='#27AE60', linestyle='--', linewidth=1.5)
# Formatting
if title is None:
title = f'{self.ticker} Stock Price Trend'
plt.title(title, fontsize=16, fontweight='bold', pad=20)
plt.xlabel('Date', fontsize=12, fontweight='bold')
plt.ylabel('Price ($)', fontsize=12, fontweight='bold')
plt.legend(loc='best', fontsize=10)
plt.grid(True, alpha=0.3, linestyle='--')
# Format x-axis
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gcf().autofmt_xdate()
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"Plot saved to {save_path}")
if show_plot:
plt.show()
else:
plt.close()
try:
# Plot closing price
plt.plot(data.index, data['Close'], label='Close Price',
color='#2E86C1', linewidth=2)
# Add moving averages
if len(data) >= 7:
ma7 = data['Close'].rolling(window=7).mean()
plt.plot(data.index, ma7, label='7-Day MA',
color='#E67E22', linestyle='--', linewidth=1.5)
if len(data) >= 30:
ma30 = data['Close'].rolling(window=30).mean()
plt.plot(data.index, ma30, label='30-Day MA',
color='#27AE60', linestyle='--', linewidth=1.5)
# Formatting
if title is None:
title = f'{self.ticker} Stock Price Trend'
plt.title(title, fontsize=16, fontweight='bold', pad=20)
plt.xlabel('Date', fontsize=12, fontweight='bold')
plt.ylabel('Price ($)', fontsize=12, fontweight='bold')
plt.legend(loc='best', fontsize=10)
plt.grid(True, alpha=0.3, linestyle='--')
# Format x-axis
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gcf().autofmt_xdate()
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"Plot saved to {save_path}")
if show_plot:
plt.show()
finally:
if not show_plot:
plt.close()

Copilot uses AI. Check for mistakes.
Returns:
float: Current price or None if unavailable
"""
try:
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method calls self.stock.info which makes an API request to fetch all stock information just to get the current price. Consider caching the info data or using a more specific API call if available. If this method is called frequently, it could lead to rate limiting or unnecessary network overhead.

Suggested change
try:
try:
# Try fast_info for quick price access
price = None
if hasattr(self.stock, "fast_info"):
price = getattr(self.stock.fast_info, "last_price", None)
if price is not None:
return price
# Fallback: use history for the latest close price
hist = self.stock.history(period="1d", interval="1m")
if not hist.empty:
# Use the last available price (close or regularMarketPrice)
price = hist["Close"].iloc[-1]
if pd.notnull(price):
return float(price)
# Last resort: use info (slow)

Copilot uses AI. Check for mistakes.
Comment on lines +290 to +339
gs = fig.add_gridspec(3, 2, hspace=0.3, wspace=0.3)

# Title
fig.suptitle(f'{self.ticker} Stock Analysis Report',
fontsize=18, fontweight='bold', y=0.98)

# Main price chart
ax1 = fig.add_subplot(gs[0:2, :])
ax1.plot(data.index, data['Close'], label='Close Price',
color='#2E86C1', linewidth=2.5)
ax1.fill_between(data.index, data['Low'], data['High'],
alpha=0.2, color='#85C1E9', label='Daily Range')
ax1.set_title('Price Trend with Daily Range', fontweight='bold', fontsize=14)
ax1.set_ylabel('Price ($)', fontsize=12)
ax1.legend(loc='best')
ax1.grid(True, alpha=0.3)

# Statistics text box
ax2 = fig.add_subplot(gs[2, 0])
ax2.axis('off')
stats_text = f"""
STATISTICS SUMMARY
━━━━━━━━━━━━━━━━━━━━━━━━━
Start Price: ${stats.get('start_price', 'N/A')}
End Price: ${stats.get('end_price', 'N/A')}
Change: ${stats.get('change', 'N/A')} ({stats.get('change_percent', 'N/A')}%)
Highest: ${stats.get('highest', 'N/A')}
Lowest: ${stats.get('lowest', 'N/A')}
Range: ${stats.get('highest', 0) - stats.get('lowest', 0):.2f}
"""
ax2.text(0.1, 0.5, stats_text, fontsize=11, family='monospace',
verticalalignment='center', bbox=dict(boxstyle='round',
facecolor='wheat', alpha=0.5))

# Volume chart
ax3 = fig.add_subplot(gs[2, 1])
ax3.bar(data.index, data['Volume'], color='#3498DB', alpha=0.6)
ax3.set_title('Trading Volume', fontweight='bold', fontsize=12)
ax3.set_ylabel('Volume', fontsize=10)
ax3.grid(True, alpha=0.3, axis='y')
ax3.tick_params(axis='x', rotation=45)

if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"Summary report saved to {save_path}")

if show_plot:
plt.show()
else:
plt.close()
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same figure cleanup issue: figure created without guaranteed cleanup if an exception occurs before line 339. Consider using try-finally or a context manager.

Suggested change
gs = fig.add_gridspec(3, 2, hspace=0.3, wspace=0.3)
# Title
fig.suptitle(f'{self.ticker} Stock Analysis Report',
fontsize=18, fontweight='bold', y=0.98)
# Main price chart
ax1 = fig.add_subplot(gs[0:2, :])
ax1.plot(data.index, data['Close'], label='Close Price',
color='#2E86C1', linewidth=2.5)
ax1.fill_between(data.index, data['Low'], data['High'],
alpha=0.2, color='#85C1E9', label='Daily Range')
ax1.set_title('Price Trend with Daily Range', fontweight='bold', fontsize=14)
ax1.set_ylabel('Price ($)', fontsize=12)
ax1.legend(loc='best')
ax1.grid(True, alpha=0.3)
# Statistics text box
ax2 = fig.add_subplot(gs[2, 0])
ax2.axis('off')
stats_text = f"""
STATISTICS SUMMARY
━━━━━━━━━━━━━━━━━━━━━━━━━
Start Price: ${stats.get('start_price', 'N/A')}
End Price: ${stats.get('end_price', 'N/A')}
Change: ${stats.get('change', 'N/A')} ({stats.get('change_percent', 'N/A')}%)
Highest: ${stats.get('highest', 'N/A')}
Lowest: ${stats.get('lowest', 'N/A')}
Range: ${stats.get('highest', 0) - stats.get('lowest', 0):.2f}
"""
ax2.text(0.1, 0.5, stats_text, fontsize=11, family='monospace',
verticalalignment='center', bbox=dict(boxstyle='round',
facecolor='wheat', alpha=0.5))
# Volume chart
ax3 = fig.add_subplot(gs[2, 1])
ax3.bar(data.index, data['Volume'], color='#3498DB', alpha=0.6)
ax3.set_title('Trading Volume', fontweight='bold', fontsize=12)
ax3.set_ylabel('Volume', fontsize=10)
ax3.grid(True, alpha=0.3, axis='y')
ax3.tick_params(axis='x', rotation=45)
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"Summary report saved to {save_path}")
if show_plot:
plt.show()
else:
plt.close()
try:
gs = fig.add_gridspec(3, 2, hspace=0.3, wspace=0.3)
# Title
fig.suptitle(f'{self.ticker} Stock Analysis Report',
fontsize=18, fontweight='bold', y=0.98)
# Main price chart
ax1 = fig.add_subplot(gs[0:2, :])
ax1.plot(data.index, data['Close'], label='Close Price',
color='#2E86C1', linewidth=2.5)
ax1.fill_between(data.index, data['Low'], data['High'],
alpha=0.2, color='#85C1E9', label='Daily Range')
ax1.set_title('Price Trend with Daily Range', fontweight='bold', fontsize=14)
ax1.set_ylabel('Price ($)', fontsize=12)
ax1.legend(loc='best')
ax1.grid(True, alpha=0.3)
# Statistics text box
ax2 = fig.add_subplot(gs[2, 0])
ax2.axis('off')
stats_text = f"""
STATISTICS SUMMARY
━━━━━━━━━━━━━━━━━━━━━━━━━
Start Price: ${stats.get('start_price', 'N/A')}
End Price: ${stats.get('end_price', 'N/A')}
Change: ${stats.get('change', 'N/A')} ({stats.get('change_percent', 'N/A')}%)
Highest: ${stats.get('highest', 'N/A')}
Lowest: ${stats.get('lowest', 'N/A')}
Range: ${stats.get('highest', 0) - stats.get('lowest', 0):.2f}
"""
ax2.text(0.1, 0.5, stats_text, fontsize=11, family='monospace',
verticalalignment='center', bbox=dict(boxstyle='round',
facecolor='wheat', alpha=0.5))
# Volume chart
ax3 = fig.add_subplot(gs[2, 1])
ax3.bar(data.index, data['Volume'], color='#3498DB', alpha=0.6)
ax3.set_title('Trading Volume', fontweight='bold', fontsize=12)
ax3.set_ylabel('Volume', fontsize=10)
ax3.grid(True, alpha=0.3, axis='y')
ax3.tick_params(axis='x', rotation=45)
if save_path:
plt.savefig(save_path, dpi=300, bbox_inches='tight')
print(f"Summary report saved to {save_path}")
if show_plot:
plt.show()
finally:
plt.close(fig)

Copilot uses AI. Check for mistakes.
import matplotlib.dates as mdates
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from datetime import datetime
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'datetime' is not used.

Suggested change
from datetime import datetime

Copilot uses AI. Check for mistakes.
Author: Python-Projects Contributors
"""

import os
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'os' is not used.

Suggested change
import os

Copilot uses AI. Check for mistakes.

import yfinance as yf
import pandas as pd
from datetime import datetime, timedelta
Copy link

Copilot AI Dec 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import of 'datetime' is not used.
Import of 'timedelta' is not used.

Suggested change
from datetime import datetime, timedelta

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant