diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 16:07:20 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-04-02 16:07:20 +0900 |
| commit | 86a0fa84ca6662ca931182880523c0b87f617f73 (patch) | |
| tree | f483d39698849a6d4cdbd4c79979217f05be78be | |
| parent | 4747400168279c6cfc1196d86ec77b5d7b513c61 (diff) | |
- Add asyncio.Lock to StockSelector._ensure_session() to prevent race condition
- Remove unused HEALTH_PORT_OFFSET constant from news-collector
- Auto-fix import sorting and formatting from ruff
| -rw-r--r-- | docker-compose.yml | 1 | ||||
| -rw-r--r-- | monitoring/prometheus.yml | 2 | ||||
| -rw-r--r-- | monitoring/prometheus/alert_rules.yml | 29 | ||||
| -rw-r--r-- | services/news-collector/src/news_collector/main.py | 3 | ||||
| -rw-r--r-- | services/strategy-engine/src/strategy_engine/stock_selector.py | 9 | ||||
| -rw-r--r-- | shared/alembic/versions/004_add_signal_detail_columns.py | 4 | ||||
| -rw-r--r-- | shared/src/shared/events.py | 4 |
7 files changed, 42 insertions, 10 deletions
diff --git a/docker-compose.yml b/docker-compose.yml index d11db2c..60462ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -228,6 +228,7 @@ services: - "9090:9090" volumes: - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml + - ./monitoring/prometheus/alert_rules.yml:/etc/prometheus/alert_rules.yml depends_on: - data-collector - strategy-engine diff --git a/monitoring/prometheus.yml b/monitoring/prometheus.yml index b6dc853..e177c9c 100644 --- a/monitoring/prometheus.yml +++ b/monitoring/prometheus.yml @@ -1,5 +1,7 @@ global: scrape_interval: 15s +rule_files: + - "/etc/prometheus/alert_rules.yml" scrape_configs: - job_name: "trading-services" authorization: diff --git a/monitoring/prometheus/alert_rules.yml b/monitoring/prometheus/alert_rules.yml new file mode 100644 index 0000000..aca2f1c --- /dev/null +++ b/monitoring/prometheus/alert_rules.yml @@ -0,0 +1,29 @@ +groups: + - name: trading-platform + rules: + - alert: ServiceDown + expr: up == 0 + for: 1m + labels: + severity: critical + annotations: + summary: "Service {{ $labels.job }} is down" + description: "{{ $labels.instance }} has been unreachable for 1 minute." + + - alert: HighErrorRate + expr: rate(errors_total[5m]) > 10 + for: 2m + labels: + severity: warning + annotations: + summary: "High error rate on {{ $labels.job }}" + description: "Error rate is {{ $value }} errors/sec over 5 minutes." + + - alert: HighProcessingLatency + expr: histogram_quantile(0.95, rate(processing_seconds_bucket[5m])) > 5 + for: 5m + labels: + severity: warning + annotations: + summary: "High p95 latency on {{ $labels.job }}" + description: "95th percentile processing time is {{ $value }}s." diff --git a/services/news-collector/src/news_collector/main.py b/services/news-collector/src/news_collector/main.py index 7265f00..c39fa67 100644 --- a/services/news-collector/src/news_collector/main.py +++ b/services/news-collector/src/news_collector/main.py @@ -25,9 +25,6 @@ from shared.sentiment import SentimentAggregator from shared.sentiment_models import MarketSentiment from shared.shutdown import GracefulShutdown -# Health check port: base + 4 -HEALTH_PORT_OFFSET = 4 - async def run_collector_once(collector, db: Database, broker: RedisBroker) -> int: """Run a single collector, store results in DB, publish to Redis. diff --git a/services/strategy-engine/src/strategy_engine/stock_selector.py b/services/strategy-engine/src/strategy_engine/stock_selector.py index 5acef0f..8657b93 100644 --- a/services/strategy-engine/src/strategy_engine/stock_selector.py +++ b/services/strategy-engine/src/strategy_engine/stock_selector.py @@ -1,5 +1,6 @@ """3-stage stock selector engine: sentiment → technical → LLM.""" +import asyncio import json import logging import re @@ -218,11 +219,13 @@ class StockSelector: self._model = anthropic_model self._max_picks = max_picks self._http_session: aiohttp.ClientSession | None = None + self._session_lock = asyncio.Lock() async def _ensure_session(self) -> aiohttp.ClientSession: - if self._http_session is None or self._http_session.closed: - self._http_session = aiohttp.ClientSession() - return self._http_session + async with self._session_lock: + if self._http_session is None or self._http_session.closed: + self._http_session = aiohttp.ClientSession() + return self._http_session async def close(self) -> None: if self._http_session and not self._http_session.closed: diff --git a/shared/alembic/versions/004_add_signal_detail_columns.py b/shared/alembic/versions/004_add_signal_detail_columns.py index 7a8a77b..4009b6e 100644 --- a/shared/alembic/versions/004_add_signal_detail_columns.py +++ b/shared/alembic/versions/004_add_signal_detail_columns.py @@ -12,7 +12,9 @@ down_revision = "003" def upgrade(): - op.add_column("signals", sa.Column("conviction", sa.Float, nullable=False, server_default="1.0")) + op.add_column( + "signals", sa.Column("conviction", sa.Float, nullable=False, server_default="1.0") + ) op.add_column("signals", sa.Column("stop_loss", sa.Numeric, nullable=True)) op.add_column("signals", sa.Column("take_profit", sa.Numeric, nullable=True)) diff --git a/shared/src/shared/events.py b/shared/src/shared/events.py index 61b85bd..37217a0 100644 --- a/shared/src/shared/events.py +++ b/shared/src/shared/events.py @@ -100,6 +100,4 @@ class Event: try: return cls.from_raw(data) except KeyError as exc: - raise ValueError( - f"Missing required field in {event_type} event data: {exc}" - ) from exc + raise ValueError(f"Missing required field in {event_type} event data: {exc}") from exc |
