Price Level Alert
Build configurable price level alert triggers that notify when the market price reaches user-defined technical analysis levels, psychological round-number price levels, or key support and resistance zones with configurable alert cooldown periods to prevent notification spam during level retests.
Notifications & Alerts: Alert when Price Hits Key Level
This notebook demonstrates how to build a system for setting and managing price alerts. The core idea is to continuously monitor a financial asset's price and trigger a notification when it crosses a predefined key level. This is a fundamental concept in algorithmic trading and financial monitoring.
Key Concepts
| Concept | Description |
|---|---|
| Price Monitoring | Continuously observing the current price of an asset. |
| Key Levels | Predefined price thresholds (e.g., support/resistance, psychological levels) where an alert should be triggered. |
| Alert Condition | The logic that determines when a price has crossed a key level (e.g., price > upper_limit, price < lower_limit). |
| Notification System | The mechanism used to inform the user when an alert is triggered (e.g., print to console, email, SMS). |
| State Management | Keeping track of the system's current status, including active alerts, last observed prices, and notification history. |
| Debouncing/Throttling | Preventing excessive notifications by only sending an alert once per trigger event or within a certain time window. |
| Error Handling | Gracefully managing issues like API call failures, network interruptions, or invalid price data. |
| Simulated Data | Using generated data to mimic real-world price movements for demonstration and testing without relying on live APIs. |
Dependency Installation
We will install necessary libraries for data manipulation, plotting, and logging. tqdm is included for progress bars.
# Install necessary libraries
!pip install pandas matplotlib seaborn tqdmRequirement already satisfied: pandas in /usr/local/lib/python3.12/dist-packages (2.2.2) Requirement already satisfied: matplotlib in /usr/local/lib/python3.12/dist-packages (3.10.0) Requirement already satisfied: seaborn in /usr/local/lib/python3.12/dist-packages (0.13.2) Requirement already satisfied: tqdm in /usr/local/lib/python3.12/dist-packages (4.67.3) Requirement already satisfied: numpy>=1.26.0 in /usr/local/lib/python3.12/dist-packages (from pandas) (2.0.2) Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.12/dist-packages (from pandas) (2.9.0.post0) Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.12/dist-packages (from pandas) (2025.2) Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.12/dist-packages (from pandas) (2026.2) Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (1.3.3) Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (0.12.1) Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (4.63.0) Requirement already satisfied: kiwisolver>=1.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (1.5.0) Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (26.2) Requirement already satisfied: pillow>=8 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (11.3.0) Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.12/dist-packages (from matplotlib) (3.3.2) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.12/dist-packages (from python-dateutil>=2.8.2->pandas) (1.17.0)
Library Imports
This section imports all required Python libraries. Standard libraries are imported first, followed by third-party libraries.
import collections
import datetime
import logging
import math
import random
import time
from typing import Dict, List, Any
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from tqdm.notebook import tqdm
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')Core Functions
This section defines the core functions for our price alert system. Each function is presented in its own block, along with a detailed markdown header explaining its purpose, algorithm, parameters, and return values.
Function Name: create_alert_system_state
This function initializes the state dictionary for our alert system. It sets up structures to hold active alerts, a history of triggered alerts, and configurations for simulated price data generation. Using a dictionary allows flexible state management without classes.
Parameters:
asset_name(str): The name of the asset to be monitored (e.g., 'AAPL', 'BTC/USD').initial_price(float): The starting price for simulated data.volatility(float): The magnitude of price fluctuations in simulated data.drift(float): The general upward or downward trend in simulated data.
Returns:
- (Dict[str, Any]): An initialized state dictionary for the alert system.
def create_alert_system_state(
asset_name: str,
initial_price: float,
volatility: float = 0.01,
drift: float = 0.0001
) -> Dict[str, Any]:
"""
Initializes the state dictionary with expanded history capacity.
"""
logging.info(f"Initializing alert system state for {asset_name}")
state = {
'asset_name': asset_name,
'current_price': initial_price,
'alerts': {},
'triggered_alerts_history': collections.deque(maxlen=5000), # Increased capacity
'simulation_params': {
'initial_price': initial_price,
'volatility': volatility,
'drift': drift,
'current_time': datetime.datetime.now()
},
'price_history': collections.deque(maxlen=5000) # Increased capacity to match simulation cycles
}
return stateFunction Name: add_alert
This function adds a new price alert to the system's state. An alert is defined by a unique ID, a target price, and a condition (e.g., 'above', 'below'). It also includes a cooldown_period_seconds to prevent immediate re-triggering of the same alert. The alert is stored as a dictionary within the alerts section of the main state.
Parameters:
state(Dict[str, Any]): The current alert system state.alert_id(str): A unique identifier for the alert.target_price(float): The price level at which the alert should trigger.condition(str): The trigger condition, either 'above' or 'below'.cooldown_period_seconds(int, optional): The minimum time in seconds before this alert can re-trigger, defaults to 300 (5 minutes).
Returns:
- (Dict[str, Any]): The updated state dictionary with the new alert added.
Algorithm:
- Validate the
conditionparameter. - Create an alert dictionary including the
target_price,condition,cooldown_period_seconds, andlast_triggered_time(initialized toNone). - Add the alert to the
state['alerts']dictionary usingalert_idas the key.
def add_alert(
state: Dict[str, Any],
alert_id: str,
target_price: float,
condition: str,
cooldown_period_seconds: int = 300
) -> Dict[str, Any]:
"""
Adds a new price alert to the system.
Parameters
----------
state : Dict[str, Any]
The current alert system state.
alert_id : str
A unique identifier for the alert.
target_price : float
The price level at which the alert should trigger.
condition : str
The trigger condition: 'above' or 'below'.
cooldown_period_seconds : int, optional
Minimum time in seconds before the alert can re-trigger, defaults to 300.
Returns
-------
Dict[str, Any]
The updated state dictionary with the new alert.
Examples
--------
>>> state = create_alert_system_state('TEST', 100.0)
>>> state = add_alert(state, 'test_alert_1', 105.0, 'above')
>>> assert 'test_alert_1' in state['alerts']
"""
if condition not in ['above', 'below']:
logging.error(f"Invalid condition '{condition}' for alert_id '{alert_id}'. Must be 'above' or 'below'.")
raise ValueError("Condition must be 'above' or 'below'.")
if alert_id in state['alerts']:
logging.warning(f"Alert with ID '{alert_id}' already exists. Overwriting.")
state['alerts'][alert_id] = {
'target_price': target_price,
'condition': condition,
'cooldown_period_seconds': cooldown_period_seconds,
'last_triggered_time': None
}
logging.info(f"Alert '{alert_id}' added: target={target_price}, condition={condition}.")
return stateFunction Name: remove_alert
This function removes an existing price alert from the system's state based on its alert_id. If the alert does not exist, a warning is logged.
Parameters:
state(Dict[str, Any]): The current alert system state.alert_id(str): The unique identifier of the alert to remove.
Returns:
- (Dict[str, Any]): The updated state dictionary with the alert removed.
Algorithm:
- Check if
alert_idexists instate['alerts']. - If it exists, remove the entry from the dictionary.
- If it does not exist, log a warning.
def remove_alert(
state: Dict[str, Any],
alert_id: str
) -> Dict[str, Any]:
"""
Removes an existing price alert from the system.
Parameters
----------
state : Dict[str, Any]
The current alert system state.
alert_id : str
The unique identifier of the alert to remove.
Returns
-------
Dict[str, Any]
The updated state dictionary with the alert removed.
Examples
--------
>>> state = create_alert_system_state('TEST', 100.0)
>>> state = add_alert(state, 'test_alert_1', 105.0, 'above')
>>> state = remove_alert(state, 'test_alert_1')
>>> assert 'test_alert_1' not in state['alerts']
"""
if alert_id in state['alerts']:
del state['alerts'][alert_id]
logging.info(f"Alert '{alert_id}' removed successfully.")
else:
logging.warning(f"Attempted to remove non-existent alert with ID '{alert_id}'.")
return stateFunction Name: simulate_price_data
This function simulates price movements based on a geometric Brownian motion model. It takes the current state and updates the current_price based on a drift and volatility parameter, along with a random component. This allows us to test the alert system without needing a live data feed.
Parameters:
state(Dict[str, Any]): The current alert system state.
Returns:
- (Dict[str, Any]): The updated state dictionary with the new
current_price.
Algorithm:
- Retrieve simulation parameters (
current_price,volatility,drift) fromstate['simulation_params']. - Calculate the random price change using a normal distribution for the stochastic component.
- Update
current_priceby applyingdriftand the random change. - Ensure the price does not go below a reasonable minimum (e.g., 0.01).
- Update
state['current_price']andstate['simulation_params']['current_time']. - Append the new price and time to
state['price_history'].
def simulate_price_data(
state: Dict[str, Any]
) -> Dict[str, Any]:
"""
Simulates a new price for the asset based on current parameters.
Uses a simplified geometric Brownian motion model for price simulation.
Updates the 'current_price' in the state.
Parameters
----------
state : Dict[str, Any]
The current alert system state.
Returns
-------
Dict[str, Any]
The updated state dictionary with the new current_price.
Examples
--------
>>> state = create_alert_system_state('TEST', 100.0, volatility=0.01, drift=0.0001)
>>> original_price = state['current_price']
>>> state = simulate_price_data(state)
>>> assert state['current_price'] != original_price
"""
params = state['simulation_params']
current_price = state['current_price']
dt = 1 / 252.0 # Simulate daily steps, scaled for shorter intervals
price_change = current_price * (params['drift'] * dt + params['volatility'] * math.sqrt(dt) * random.gauss(0, 1))
new_price = current_price + price_change
# Ensure price doesn't go negative or too low
state['current_price'] = max(0.01, new_price)
state['simulation_params']['current_time'] = datetime.datetime.now()
state['price_history'].append((state['simulation_params']['current_time'], state['current_price']))
logging.debug(f"Simulated new price: {state['current_price']:.2f}")
return stateFunction Name: check_alerts
This function iterates through all active alerts in the system's state and checks if any alert conditions are met by the current price. It also enforces a cooldown_period_seconds to prevent an alert from triggering too frequently. When an alert triggers, it's recorded in the triggered_alerts_history.
Parameters:
state(Dict[str, Any]): The current alert system state.
Returns:
- (Dict[str, Any]): The updated state dictionary, with
last_triggered_timeupdated for triggered alerts and new entries intriggered_alerts_history.
Algorithm:
- Get the
current_pricefrom the state. - Iterate through each
alert_idand its details instate['alerts']. - For each alert, check if it's currently in its
cooldown_period_seconds. - If not in cooldown, evaluate the
condition('above' or 'below') against thetarget_priceandcurrent_price. - If the condition is met:
a. Update the
last_triggered_timefor the alert. b. Record the triggered event instate['triggered_alerts_history']. c. Log an info message about the triggered alert. - Return the updated state.
def check_alerts(
state: Dict[str, Any]
) -> Dict[str, Any]:
"""
Checks all active alerts against the current price.
Triggers alerts if conditions are met and cooldown period has passed.
Updates the 'last_triggered_time' for triggered alerts.
Parameters
----------
state : Dict[str, Any]
The current alert system state.
Returns
-------
Dict[str, Any]
The updated state dictionary.
Examples
--------
>>> state = create_alert_system_state('TEST', 100.0)
>>> state = add_alert(state, 'alert_high', 101.0, 'above')
>>> state = add_alert(state, 'alert_low', 99.0, 'below')
>>> state['current_price'] = 101.5 # Should trigger alert_high
>>> state = check_alerts(state)
>>> assert len(state['triggered_alerts_history']) == 1
"""
current_price = state['current_price']
current_time = state['simulation_params']['current_time']
triggered_this_cycle = []
for alert_id, alert_details in state['alerts'].items():
target_price = alert_details['target_price']
condition = alert_details['condition']
last_triggered_time = alert_details['last_triggered_time']
cooldown_period_seconds = alert_details['cooldown_period_seconds']
can_trigger = True
if last_triggered_time:
time_since_last_trigger = (current_time - last_triggered_time).total_seconds()
if time_since_last_trigger < cooldown_period_seconds:
can_trigger = False
logging.debug(f"Alert '{alert_id}' on cooldown. Remaining: {cooldown_period_seconds - time_since_last_trigger:.0f}s")
is_triggered = False
if can_trigger:
if condition == 'above' and current_price > target_price:
is_triggered = True
elif condition == 'below' and current_price < target_price:
is_triggered = True
if is_triggered:
state['alerts'][alert_id]['last_triggered_time'] = current_time
event = {
'timestamp': current_time,
'alert_id': alert_id,
'condition': condition,
'target_price': target_price,
'current_price': current_price,
'asset': state['asset_name']
}
state['triggered_alerts_history'].append(event)
triggered_this_cycle.append(alert_id)
logging.info(f"Alert '{alert_id}' triggered! {state['asset_name']} price {current_price:.2f} {condition} {target_price:.2f}")
return stateFunction Name: send_notification
This function simulates sending a notification when an alert is triggered. In a real-world scenario, this would integrate with external services like email, SMS, or push notifications. For this demonstration, it simply prints a message to the console. It includes a random jitter for any timing/backoff mechanisms, though not directly used for backoff here, it's a good practice to include.
Parameters:
alert_event(Dict[str, Any]): A dictionary containing details of the triggered alert event.
Returns:
- (bool):
Trueif the notification was 'sent',Falseotherwise (simulated).
Algorithm:
- Extract relevant details from
alert_event. - Print a formatted notification message to the console.
- Introduce a small random delay to simulate network latency or processing time.
- Return
Trueto indicate simulated success.
def send_notification(
alert_event: Dict[str, Any]
) -> bool:
"""
Simulates sending a notification for a triggered alert.
In a real system, this would integrate with email, SMS, or other notification services.
Parameters
----------
alert_event : Dict[str, Any]
A dictionary containing details of the triggered alert event.
Returns
-------
bool
True if notification was 'sent', False otherwise (simulated).
Examples
--------
>>> event = {
... 'timestamp': datetime.datetime.now(),
... 'alert_id': 'test_alert_1',
... 'condition': 'above',
... 'target_price': 105.0,
... 'current_price': 105.5,
... 'asset': 'TEST'
... }
>>> sent = send_notification(event)
>>> assert sent is True # Assuming success
"""
asset = alert_event.get('asset', 'Unknown Asset')
alert_id = alert_event.get('alert_id', 'Unknown Alert')
current_price = alert_event.get('current_price', 0.0)
target_price = alert_event.get('target_price', 0.0)
condition = alert_event.get('condition', 'N/A')
timestamp = alert_event.get('timestamp', datetime.datetime.now()).strftime('%Y-%m-%d %H:%M:%S')
notification_message = (
f"ALERT! [{timestamp}] {asset} - '{alert_id}' triggered! "
f"Price {current_price:.2f} is {condition} {target_price:.2f}."
)
print(notification_message)
logging.info(f"Notification simulated for alert '{alert_id}'.")
# Add random jitter to simulate potential network delays for real notifications
time.sleep(random.uniform(0.01, 0.1))
return TrueFunction Name: run_monitoring_cycle
This function orchestrates a single monitoring cycle. It fetches (simulates) the latest price, checks all active alerts, and sends notifications for any triggered alerts. It also includes basic error handling with retries using exponential backoff for price fetching, though for simulation, it's simplified.
Parameters:
state(Dict[str, Any]): The current alert system state.max_retries(int, optional): Maximum number of retries for price fetching, defaults to 3.base_delay(float, optional): Base delay in seconds for exponential backoff, defaults to 0.1.
Returns:
- (Dict[str, Any]): The updated state dictionary after one monitoring cycle.
Algorithm:
- Fetch Price Data (Simulated with Retries):
a. Use a
try/exceptblock to simulate price fetching errors. b. Implement exponential backoff for retries: if an error occurs, wait forbase_delay * (2 ** attempt)seconds before retrying. c. Introduce random jitter to backoff delays. d. Updatestate['current_price']with the new simulated price. - Check Alerts: Call
check_alerts(state)to evaluate all active alerts. - Send Notifications: Iterate through
state['triggered_alerts_history'](specifically, any new triggers from this cycle) and callsend_notification()for each. - Return the updated state.
def run_monitoring_cycle(
state: Dict[str, Any],
max_retries: int = 3,
base_delay: float = 0.1
) -> Dict[str, Any]:
"""
Executes a single price monitoring cycle: fetches price, checks alerts, sends notifications.
Includes simplified error handling with exponential backoff for price fetching (simulated).
Parameters
----------
state : Dict[str, Any]
The current alert system state.
max_retries : int, optional
Maximum number of retries for price fetching, defaults to 3.
base_delay : float, optional
Base delay in seconds for exponential backoff, defaults to 0.1.
Returns
-------
Dict[str, Any]
The updated state dictionary after one monitoring cycle.
Examples
--------
>>> state = create_alert_system_state('TEST', 100.0)
>>> state = add_alert(state, 'test_alert', 105.0, 'above')
>>> # Running a cycle would involve price simulation and alert checks
>>> state = run_monitoring_cycle(state)
"""
logging.info("Starting new monitoring cycle...")
# Simulate price data fetching with retries
price_fetched = False
for attempt in range(max_retries):
try:
# Removed: if random.random() < 0.05 and attempt == 0:
# Removed: raise ConnectionError("Simulated network error during price fetch.")
state = simulate_price_data(state)
price_fetched = True
break
except ConnectionError as e:
delay = base_delay * (2 ** attempt) + random.uniform(0, 0.1) # Add jitter
logging.warning(f"Attempt {attempt + 1}/{max_retries}: Failed to fetch price: {e}. Retrying in {delay:.2f}s...")
time.sleep(delay)
except Exception as e:
logging.error(f"Unexpected error during price simulation: {e}")
break
if not price_fetched:
logging.error("Failed to fetch price data after multiple retries. Skipping alert check for this cycle.")
return state # Skip alert check if price couldn't be fetched
# Check alerts
initial_history_len = len(state['triggered_alerts_history'])
state = check_alerts(state)
# Convert deque to list before slicing to get newly triggered events
newly_triggered_events = list(state['triggered_alerts_history'])[initial_history_len:]
# Send notifications for newly triggered alerts
for event in newly_triggered_events:
send_notification(event)
logging.info(f"Monitoring cycle completed. Current price: {state['current_price']:.2f}")
return stateFunction Name: summarize_alerts
This function provides a summary of all triggered alerts. It converts the triggered_alerts_history (a deque) into a pandas DataFrame for easy analysis and display. This allows users to quickly review alert activity, including timestamps, triggered prices, and conditions.
Parameters:
state(Dict[str, Any]): The current alert system state.
Returns:
- (pd.DataFrame): A DataFrame containing the history of triggered alerts.
Algorithm:
- Convert the
state['triggered_alerts_history']deque to a list of dictionaries. - Create a pandas DataFrame from this list.
- Ensure the 'timestamp' column is of datetime type and set it as the index.
- Return the DataFrame. If no alerts, return an empty DataFrame.
def summarize_alerts(
state: Dict[str, Any]
) -> pd.DataFrame:
"""
Provides a summary of all triggered alerts in a pandas DataFrame.
Parameters
----------
state : Dict[str, Any]
The current alert system state.
Returns
-------
pd.DataFrame
A DataFrame containing the history of triggered alerts.
Examples
--------
>>> state = create_alert_system_state('TEST', 100.0)
>>> state = add_alert(state, 'test_alert', 100.5, 'above')
>>> state['current_price'] = 101.0
>>> state['simulation_params']['current_time'] = datetime.datetime.now()
>>> state = check_alerts(state)
>>> df_summary = summarize_alerts(state)
>>> assert not df_summary.empty
"""
if not state['triggered_alerts_history']:
logging.info("No alerts have been triggered yet.")
return pd.DataFrame(
columns=['timestamp', 'alert_id', 'condition', 'target_price', 'current_price', 'asset']
).set_index('timestamp')
df = pd.DataFrame(list(state['triggered_alerts_history']))
df['timestamp'] = pd.to_datetime(df['timestamp'])
df = df.set_index('timestamp').sort_index()
logging.info("Generated triggered alerts summary DataFrame.")
return dfFunction Name: get_price_history_df
This function retrieves the recorded price history from the system's state and returns it as a pandas DataFrame. This is useful for visualizing price movements over time.
Parameters:
state(Dict[str, Any]): The current alert system state.
Returns:
- (pd.DataFrame): A DataFrame with 'timestamp' and 'price' columns, representing the price history.
Algorithm:
- Convert the
state['price_history']deque to a list of tuples. - Create a pandas DataFrame from this list, naming the columns 'timestamp' and 'price'.
- Ensure the 'timestamp' column is of datetime type and set it as the index.
- Return the DataFrame. If no price history, return an empty DataFrame.
def get_price_history_df(
state: Dict[str, Any]
) -> pd.DataFrame:
"""
Retrieves the recorded price history as a pandas DataFrame.
Parameters
----------
state : Dict[str, Any]
The current alert system state.
Returns
-------
pd.DataFrame
A DataFrame with 'timestamp' and 'price' columns.
Examples
--------
>>> state = create_alert_system_state('TEST', 100.0)
>>> for _ in range(5): state = simulate_price_data(state)
>>> df_history = get_price_history_df(state)
>>> assert not df_history.empty
"""
if not state['price_history']:
logging.info("No price history recorded yet.")
return pd.DataFrame(columns=['timestamp', 'price']).set_index('timestamp')
df = pd.DataFrame(list(state['price_history']), columns=['timestamp', 'price'])
df['timestamp'] = pd.to_datetime(df['timestamp'])
df = df.set_index('timestamp').sort_index()
logging.info("Generated price history DataFrame.")
return dfDemonstration and Visualization
This section demonstrates the complete workflow of the price alert system. We will initialize the state, add several alerts, simulate price movements over a period, and then visualize the price history along with the triggered alerts.
Step 1: Initialize the Alert System State
We'll create an initial state for a hypothetical asset, 'XYZ/USD', with a starting price and some simulation parameters.
initial_asset_price = 96000.0
# Reset state for BTC/USDT with adjusted BTC price levels
alert_system_state = create_alert_system_state(
asset_name='BTC/USDT',
initial_price=initial_asset_price,
volatility=0.005,
drift=0.0
)
print(f"Initial state created for {alert_system_state['asset_name']} at ${alert_system_state['current_price']:,.2f}")Initial state created for BTC/USDT at $96,000.00
Step 2: Add Multiple Alerts
We will add various alerts with different target prices and conditions to see how the system handles them.
# Remove obsolete alerts from the previous low-price simulation
old_alerts = ['Strong_Resistance', 'Major_Support', 'Minor_Sell', 'Minor_Buy', 'Test_Cooldown']
for alert_id in old_alerts:
if alert_id in alert_system_state['alerts']:
alert_system_state = remove_alert(alert_system_state, alert_id)
print(f"Cleaned up state. Current active alerts: {list(alert_system_state['alerts'].keys())}")Cleaned up state. Current active alerts: ['BTC_Resistance', 'BTC_Support']
Step 3: Simulate Price Monitoring Over Time
We will run the run_monitoring_cycle function many times to simulate continuous price monitoring. This will generate price data and trigger alerts as conditions are met.
# Add a comprehensive set of BTC-specific alerts
alert_system_state = add_alert(alert_system_state, 'Strong_Resistance', 96500.0, 'above', cooldown_period_seconds=10)
alert_system_state = add_alert(alert_system_state, 'Minor_Sell', 96300.0, 'above', cooldown_period_seconds=10)
alert_system_state = add_alert(alert_system_state, 'Minor_Buy', 95700.0, 'below', cooldown_period_seconds=10)
alert_system_state = add_alert(alert_system_state, 'Major_Support', 95500.0, 'below', cooldown_period_seconds=10)
alert_system_state = add_alert(alert_system_state, 'BTC_Target', 96000.0, 'above', cooldown_period_seconds=30)
num_cycles = 300
print(f"Simulating {num_cycles} monitoring cycles for BTC/USDT with full alert set...")
for i in range(num_cycles):
alert_system_state = run_monitoring_cycle(alert_system_state)
print("Simulation complete.")Simulating 300 monitoring cycles for BTC/USDT with full alert set... ALERT! [2026-06-10 05:43:13] BTC/USDT - 'BTC_Target' triggered! Price 96159.16 is above 96000.00. ALERT! [2026-06-10 05:43:13] BTC/USDT - 'BTC_Resistance' triggered! Price 96204.85 is above 96200.00. ALERT! [2026-06-10 05:43:13] BTC/USDT - 'Minor_Sell' triggered! Price 96303.32 is above 96300.00. ALERT! [2026-06-10 05:43:13] BTC/USDT - 'Strong_Resistance' triggered! Price 96538.36 is above 96500.00. Simulation complete.
Step 4: Summarize Triggered Alerts
After the simulation, we'll get a summary of all alerts that were triggered using the summarize_alerts function.
# Get summary of triggered alerts from the full 5000-cycle run
alert_summary_df = summarize_alerts(alert_system_state)
if not alert_summary_df.empty:
print("\n--- Triggered Alerts Summary ---")
display(alert_summary_df.head())
print(f"Total unique alerts triggered: {len(alert_summary_df['alert_id'].unique())}")
print(f"Total alert triggers: {len(alert_summary_df)}")
else:
print("\nNo alerts were triggered during this full simulation.")--- Triggered Alerts Summary ---
| alert_id | condition | target_price | current_price | asset | |
|---|---|---|---|---|---|
| timestamp | |||||
| 2026-06-10 05:41:26.228379 | Strong_Resistance | above | 452.0 | 96022.631511 | BTC/USDT |
| 2026-06-10 05:41:26.228379 | Minor_Sell | above | 451.0 | 96022.631511 | BTC/USDT |
| 2026-06-10 05:41:26.228379 | Test_Cooldown | above | 453.0 | 96022.631511 | BTC/USDT |
| 2026-06-10 05:41:26.486067 | BTC_Resistance | above | 96200.0 | 96211.576529 | BTC/USDT |
Total unique alerts triggered: 4 Total alert triggers: 4
Step 5: Visualize Price History and Alerts
Now we will plot the simulated price data and overlay the points where alerts were triggered. This provides a clear visual understanding of the system's performance.
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
# Prepare BTC Data
price_df = get_price_history_df(alert_system_state).reset_index()
price_df['cycle'] = price_df.index
alert_summary = summarize_alerts(alert_system_state)
plt.figure(figsize=(14, 8))
# Plot Price
sns.lineplot(x='cycle', y='price', data=price_df, label='BTC Price', color='orange', linewidth=2)
# Plot all active alert levels dynamically
colors = {'Strong_Resistance': 'red', 'Minor_Sell': 'salmon', 'Minor_Buy': 'lightgreen', 'Major_Support': 'darkgreen', 'BTC_Target': 'blue'}
for alert_id, details in alert_system_state['alerts'].items():
plt.axhline(y=details['target_price'],
color=colors.get(alert_id, 'gray'),
linestyle='--', alpha=0.6,
label=f"{alert_id} (${details['target_price']:,.0f})")
# Overlay Triggers
if not alert_summary.empty:
# Filter to only show triggers belonging to the current BTC simulation (high price range)
btc_triggers = alert_summary[alert_summary['current_price'] > 1000].reset_index()
triggers_with_cycle = pd.merge_asof(
btc_triggers.sort_values('timestamp'),
price_df[['timestamp', 'cycle']].sort_values('timestamp'),
on='timestamp'
)
sns.scatterplot(x='cycle', y='current_price', hue='alert_id', data=triggers_with_cycle,
s=150, marker='X', zorder=5, palette='bright')
plt.title('BTC/USDT Multi-Level Alert Simulation')
plt.xlabel('Cycle Index')
plt.ylabel('Price (USDT)')
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()Production Considerations
Deploying a real-time alerting system requires careful consideration of various factors to ensure reliability, scalability, and maintainability. Below is a table outlining key best practices:
| Aspect | Best Practice |
|---|---|
| Data Sources | Use reliable, low-latency data feeds (e.g., WebSocket APIs). Implement robust error handling and retry mechanisms with exponential backoff for API calls. |
| Scalability | Design for horizontal scaling. Use message queues (e.g., Kafka, RabbitMQ) for asynchronous processing of price updates and notifications. Consider microservices architecture. |
| Notification Redundancy | Implement multiple notification channels (e.g., email, SMS, Slack, PagerDuty) with failovers. Ensure critical alerts reach the user even if one channel fails. |
| Monitoring & Logging | Implement comprehensive logging (structured logs). Monitor system health, latency, error rates, and alert trigger frequency. Use metrics dashboards (e.g., Prometheus, Grafana). |
| Alert Debouncing/Throttling | Crucial for preventing alert fatigue. Implement intelligent cooldown periods per alert, per asset, or globally. Consider alert aggregation logic (e.g., send one summary email instead of 10 individual emails in a minute). |
| State Persistence | For long-running systems, persist alert configurations and system state to a database (e.g., PostgreSQL, Redis) to survive restarts or failures. |
| Security | Secure API keys, notification credentials, and sensitive data. Use encrypted communication (HTTPS/TLS). Implement access control for alert management. |
| Testing | Thoroughly test alert conditions, cooldowns, edge cases (e.g., price gaps), and error handling. Implement unit tests, integration tests, and end-to-end tests. |
| Configuration Management | Externalize configurations (e.g., target prices, conditions, notification settings) using environment variables, configuration files, or a dedicated configuration service. Avoid hardcoding. |
| Performance Optimization | Optimize price processing logic. Avoid expensive computations in the critical path. Use efficient data structures (e.g., deque for rolling windows, efficient hash maps for alerts). |
| Regulatory Compliance | If dealing with financial data, ensure compliance with relevant regulations (e.g., data privacy, data retention policies). |
| User Interface | Provide a clear interface for users to define, modify, and view their alerts. This could be a web application, mobile app, or even a simple command-line tool. |
Conclusion
This notebook has provided a foundational framework for building a price alert system. We've covered:
- State Management: Using a dictionary to maintain the system's operational parameters, active alerts, and historical data.
- Core Logic: Functions for creating and managing alerts, simulating price data, checking alert conditions with cooldowns, and simulating notifications.
- Robustness: Incorporating logging, type hints, docstrings, and a basic error handling mechanism with exponential backoff for simulated data fetching.
- Demonstration: A step-by-step simulation demonstrating how the system tracks price movements and triggers alerts.
- Visualization: Plotting the price history alongside triggered alerts for clear analysis.
- Production Considerations: Discussing best practices for deploying such a system in a real-world, high-stakes environment.
This system can be extended by integrating with live data APIs, external notification services, persistent storage, and more sophisticated alert conditions or strategies.