Monitoring and Alerting
SkillMonitoring & opsReal-time monitoring and alerting for live trading systems. Use when building observability for data pipelines, model drift, or execution quality.
Use Monitoring and Alerting in Claude, ChatGPT or Ahel Desktop
Free. Sign in, add Monitoring and Alerting and connect your AI. About a minute.
Also: Claude Code · Cursor · Codex
Then ask your AI: use the Monitoring and Alerting skill
Details
Instructions available. Your AI can read the instructions. Execution depends on the setup they require.
Account requirements not reviewed. Check the skill instructions before use; Ahel provides instructions and does not run this skill.
No other account needed.
Add Ahel to your AI once: Claude, ChatGPT, Cursor, Claude Code or Codex. Then ask it to use this.
What this skill tells your AI
The instructions your AI receives, as published by ml4t/skills in production/monitoring-alerting/SKILL.md and read by Ahel’s review.
Checking performance at end-of-day is too late. A stuck data feed, a rejected order, or a flash crash can cause irreversible losses in minutes. Automated real-time monitoring catches problems when they are still small.
The Problem
A strategy runs in production. The data feed silently stalls at 10:15 AM - the strategy stops generating signals but nobody notices until 4 PM. By then, the portfolio has drifted and missed the day's best opportunities. Worse: a position limit was breached because a partial fill was not tracked, and the strategy doubled down. End-of-day review catches the problem 6 hours too late.
The Pattern
WRONG
# End-of-day check - too late for anything but damage assessment
def daily_report():
pnl = portfolio.value() - portfolio.start_of_day_value()
if pnl < -10_000:
send_email("Bad day", f"Lost ${abs(pnl):,.0f}")
schedule.every().day.at("16:30").do(daily_report) # 6+ hours after it started
CORRECT
import time
from dataclasses import dataclass
from datetime import UTC, datetime
@dataclass
class AlertThresholds:
max_drawdown: float = 0.05 # 5% intraday drawdown
max_daily_loss: float = 0.02 # 2% daily loss
max_position_pct: float = 0.10 # 10% in single name
data_stale_seconds: int = 120 # 2 minutes without new data
fill_rate_floor: float = 0.80 # 80% of orders must fill
def monitor_loop(portfolio, data_feed, thresholds: AlertThresholds, debounce=3):
"""Continuous monitoring with immediate alerting."""
streak = {}
def check(name, breached, message):
# One bad sample is noise; `debounce` of them in a row is an incident.
streak[name] = streak.get(name, 0) + 1 if breached else 0
if streak[name] == debounce:
alert(name, message)
while True:
now = datetime.now(UTC)
last = data_feed.last_timestamp()
if last.tzinfo is None: # naive: subtracting from `now` raises
last = last.replace(tzinfo=UTC)
# .seconds is the sub-day part, so a 24h stall would read as fresh
stale = (now - last).total_seconds()
check("DATA_STALE", stale > thresholds.data_stale_seconds, f"Stale {stale:.0f}s")
dd = portfolio.current_drawdown()
check("DRAWDOWN", dd > thresholds.max_drawdown, f"Intraday DD: {dd:.1%}")
loss = -portfolio.daily_return() # the threshold was defined and never read
check("DAILY_LOSS", loss > thresholds.max_daily_loss, f"Daily loss: {loss:.1%}")
for sym, weight in portfolio.weights().items():
check(f"CONCENTRATION:{sym}", abs(weight) > thresholds.max_position_pct,
f"{sym}: {weight:.1%}")
fill_rate = portfolio.fill_rate(window_minutes=30)
check("LOW_FILLS", fill_rate < thresholds.fill_rate_floor, f"Fills {fill_rate:.0%}")
time.sleep(10) # every 10s, so a 3-sample debounce alerts after 30s
What to Monitor
Five categories: P&L (intraday drawdown > 5%, daily loss > 2%), Positions (single-name > 10%, gross leverage > 2x), Execution (fill rate < 80%, slippage > 2x model), Data (bar age > 2x interval, stale prices), System (latency > 500ms, memory > 80%).
Alert Escalation
Four levels: INFO (log only), WARNING (Slack), CRITICAL (page on-call), HALT (trigger kill switch). Route by name from check above; the debounce there is what keeps a single bad sample off the pager.
Guardrails
- Monitor loop must run independently from the trading process - if trading crashes, monitoring must still work
- Data staleness check must use wall clock, not data timestamps (which stop updating when the feed dies)
- Test the alerting path end-to-end: trigger a fake alert and verify it reaches the right person
Production Implementation
ml4t-live provides monitoring hooks integrated with the trading engine:
from ml4t.live import SafeBroker, LiveRiskConfig
config = LiveRiskConfig(
execution_mode="shadow", # required: "shadow", "paper" or "live"
max_drawdown_pct=0.05, # positive fraction below the high-water mark
max_daily_loss=5_000.0,
max_data_staleness_seconds=120.0,
)
broker = SafeBroker(inner_broker, config)
# A breach raises RiskLimitError in the TRADING process and latches the switch.
# The monitor above runs separately and never sees that exception - the trading
# process has to record the incident somewhere the monitor reads.
Checklist
- Monitoring loop runs independently from trading process
- Data staleness detected within 2x expected bar interval
- Drawdown and daily loss alerts configured and tested
- Position concentration limits enforced
- Fill rate tracked with 30-minute rolling window
- Alert escalation tested end-to-end; all thresholds documented before go-live
Signals
- GitHub stars
- 22
- Forks
- 11
- Last commit
- Oct 2026
Advanced
- Item type
- skill
- Key
ml4t-monitoring-alerting- Source
- github.com/ml4t/skills
More in Monitoring & ops
Skill · anthropics
More in Monitoring & opsagent-eval
Skill · affaan-m
More in Monitoring & opspricing
Skill · coreyhaines31
More in Monitoring & opslark-okr
Skill · larksuite
More in Monitoring & opsdashboard-builder
Skill · affaan-m
More in Monitoring & opsbabysit
Skill · thedotmack
More in Monitoring & ops