Step 1: Project Setup and Version Control Initialization
In this step, we create the project directory, initialize a Git repository, and set up a Python virtual environment. This will ensure that dependencies are isolated and version control is ready for tracking changes. Run the following commands in your terminal:
Create project directory and enter it
mkdir StockPriceNotifier cd StockPriceNotifier
Initialize a Git repository
git init
Set up a virtual environment (using Python 3)
python3 -m venv env source env/bin/activate # On Windows, run: env\Scripts\activate
Create the directory structure and necessary files
mkdir src tests touch src/init.py touch README.md requirements.txt
Explanation: ⢠The virtual environment (env) ensures that your dependencies don’t conflict with the system libraries. ⢠The src folder will contain our application code, while tests will host our unit tests. ⢠Git is initialized to help you maintain version control over the project files.
Step 2: Design the Stock Data Model with OOP
We define a Stock class that will encapsulate the data and behaviors related to a stock, such as the symbol and price. In this improved version, we add type hints, docstrings, and enhanced logging support, which is very useful during debugging.
File: src/stock.py
#!/usr/bin/env python3 “”" Module: stock.py This module defines the Stock class for maintaining stock information. “”"
class Stock: def init(self, symbol: str, price: float) → None: “”" Initialize the Stock with a symbol and an initial price. :param symbol: The stock ticker symbol (e.g., ‘AAPL’). :param price: Initial price of the stock. “”" self.symbol = symbol self.price = price
def update_price(self, new_price: float) -> float:
"""
Update the stock price and compute the change.
:param new_price: The updated stock price.
:return: The difference between the new and the old price.
"""
change = new_price - self.price
self.price = new_price
return change
def __str__(self) -> str:
"""Return a string representation of the stock."""
return f"Stock(symbol={self.symbol}, price={self.price})"
if name == “main”: # Simple demonstration for debugging purposes apple = Stock(“AAPL”, 150.0) print(“Initial state:”, apple) change = apple.update_price(155.0) print(“After update:”, apple) print(“Price change:”, change)
Explanation: ⢠The Stock class models a stock with a symbol and a price. ⢠The update_price method calculates how much the price changed and then updates the price attribute. ⢠Type hints and docstrings have been added to improve code readability and maintenance.
Step 3: Implement the Notification System
We now design a Notifier class that will monitor stock price changes and notify the user when the change exceeds a certain threshold. In a production system, this could be expanded to send emails, SMS, etc. For now, we’ll simply print alerts to the console.
File: src/notifier.py
#!/usr/bin/env python3 “”" Module: notifier.py This module defines the Notifier class which triggers alerts when stock price changes exceed a threshold. “”"
class Notifier: def init(self, threshold: float) → None: “”" Initialize with a threshold value for alert notifications. :param threshold: Minimum absolute change required to trigger an alert. “”" self.threshold = threshold
def check_and_notify(self, stock, change: float) -> None:
"""
Check the stock price change and send notification if it exceeds the threshold.
:param stock: The Stock instance.
:param change: The price change computed.
"""
if abs(change) >= self.threshold:
self.notify(stock, change)
def notify(self, stock, change: float) -> None:
"""
Send a notification. Here we use print, but this can be replaced with more complex logic.
:param stock: The Stock instance.
:param change: The price change.
"""
direction = "increased" if change > 0 else "decreased"
message = (f"Alert: {stock.symbol} has {direction} by {abs(change)} "
f"to {stock.price}!")
print(message)
if name == “main”: # Direct testing of the notifier functionality from stock import Stock apple = Stock(“AAPL”, 150.0) notifier = Notifier(threshold=4) price_change = apple.update_price(156.0) notifier.check_and_notify(apple, price_change)
Explanation: ⢠Notifier is initialized with a threshold value used to determine whether or not to alert. ⢠The check_and_notify method calculates whether the absolute change meets or exceeds the threshold and then calls notify. ⢠The notify method outputs a simple message; you can replace this with any actual notification mechanism you prefer.
Step 4: Simulate Real-Time Data Fetching
In this step, we simulate real-time data by periodically updating the stock price with random fluctuations. This code uses Pythonâs time.sleep to mimic real-time delays.
File: src/simulator.py
#!/usr/bin/env python3 “”" Module: simulator.py Simulates real-time stock price updates by generating random fluctuations. “”"
import random import time from stock import Stock from notifier import Notifier
class StockSimulator: def init(self, stock: Stock, notifier: Notifier, interval: int = 2, iterations: int = 5) → None: “”" Initialize the simulator for a given stock and notifier. :param stock: Stock instance to simulate. :param notifier: Notifier instance to alert on significant price changes. :param interval: Time interval between updates in seconds. :param iterations: Number of simulated price updates. “”" self.stock = stock self.notifier = notifier self.interval = interval self.iterations = iterations
def simulate(self) -> None:
"""Run the simulation of price updates."""
print(f"Starting simulation for {self.stock.symbol}...")
for i in range(self.iterations):
# Simulate a random price change between -5 and +5
new_price = round(self.stock.price + random.uniform(-5, 5), 2)
change = self.stock.update_price(new_price)
print(f"[Iteration {i+1}] Updated {self.stock.symbol} to {self.stock.price}")
self.notifier.check_and_notify(self.stock, change)
time.sleep(self.interval)
print("Simulation complete.")
if name == “main”: apple = Stock(“AAPL”, 150.0) notifier = Notifier(threshold=3) simulator = StockSimulator(apple, notifier, interval=2, iterations=10) simulator.simulate()
Explanation: ⢠StockSimulator takes a Stock instance and a Notifier, simulating price updates at fixed intervals. ⢠Each iteration generates a new random price, applies the update, and checks if a notification is needed. ⢠Interval and iteration count are parameterized so that you can easily adjust the simulation timing and duration.
Step 5: Debugging and Unit Testing
Now we write unit tests to ensure that the functionality of our Stock and Notifier classes is as expected. We use Pythonâs built-in unittest framework for this. The tests include both price update validations and notifications trigger conditions.
File: tests/test_stock.py
#!/usr/bin/env python3 “”" Unit tests for the Stock class. “”"
import unittest from src.stock import Stock
class TestStock(unittest.TestCase): def setUp(self) → None: self.stock = Stock(“GOOGL”, 2000.0)
def test_update_price_increase(self) -> None:
change = self.stock.update_price(2005.0)
self.assertEqual(change, 5.0)
self.assertEqual(self.stock.price, 2005.0, "Price should be updated to 2005.0")
def test_update_price_decrease(self) -> None:
change = self.stock.update_price(1995.0)
self.assertEqual(change, -5.0)
self.assertEqual(self.stock.price, 1995.0, "Price should be updated to 1995.0")
if name == ‘main’: unittest.main()
File: tests/test_notifier.py
#!/usr/bin/env python3 “”" Unit tests for the Notifier class. “”"
import unittest from src.stock import Stock from src.notifier import Notifier
class TestNotifier(unittest.TestCase): def setUp(self) → None: self.stock = Stock(“TSLA”, 600.0) self.threshold = 10.0 # We capture notifications by overriding the notify method for testing. self.notifications =
class TestNotifierClass(Notifier):
def notify(inner_self, stock, change: float) -> None:
"""Override notify to capture the notification message."""
direction = "increased" if change > 0 else "decreased"
message = f"{stock.symbol} {direction} by {abs(change)} to {stock.price}"
self.notifications.append(message)
self.notifier = TestNotifierClass(threshold=self.threshold)
def test_notification_trigger(self) -> None:
# Price increase that exceeds the threshold (e.g., change of 15)
change = self.stock.update_price(615.0)
self.notifier.check_and_notify(self.stock, change)
self.assertTrue(len(self.notifications) > 0, "A notification should have been triggered.")
def test_no_notification(self) -> None:
# Price change that does not exceed the threshold (e.g., change of 5)
change = self.stock.update_price(605.0)
self.notifier.check_and_notify(self.stock, change)
self.assertEqual(len(self.notifications), 0, "No notification should be triggered for small changes.")
if name == ‘main’: unittest.main()
Explanation: ⢠The test_stock.py file checks that the Stock class correctly updates its price and calculates the change. ⢠The test_notifier.py file uses a subclass of Notifier (TestNotifierClass) to override notify â this way, we capture the notification messages instead of printing them. This makes it easier to assert whether notifications were sent. ⢠Running these tests helps ensure our classes work correctly before integrating them into the simulator.
Step 6: Final Integration and Commit
After confirming that your code works fine with the unit tests, integrate all modules by running the simulation, perform final debugging, and commit your code using Git.
In your terminal, execute the following commands:
Run unit tests from the tests directory to ensure everything is working correctly
python -m unittest discover -s tests
Once tests pass, add all files to Git
git add .
Commit with an informative message summarizing your work
git commit -m “Initial commit: Added stock model, notifier, simulator, and unit tests for real-time stock price notifier”
Explanation: ⢠Running the tests ensures that any new code meets the defined requirements immediately. ⢠Using Git for commit management helps in tracking changes, and provides the ability to roll back if necessary.
Congratulations!
Youâve now built a comprehensive, real-time stock price notifier using Python. This project demonstrates effective object-oriented design, simulates real-time data fetching, includes a notification mechanism, and employs debugging and testing practices. As you further develop this project, consider integrating real APIs or asynchronous processing (asyncio) for more realistic data handling. Happy coding!