Files
The-Ouroboros/src/logging_config.py
agentson b26ff0c1b8
Some checks failed
CI / test (pull_request) Has been cancelled
feat: implement timezone-based global market auto-selection
Implement comprehensive multi-market trading system with automatic
market selection based on timezone and trading hours.

## New Features
- Market schedule module with 10 global markets (KR, US, JP, HK, CN, VN)
- Overseas broker for KIS API international stock trading
- Automatic market detection based on current time and timezone
- Next market open waiting logic when all markets closed
- ConnectionError retry with exponential backoff (max 3 attempts)

## Architecture Changes
- Market-aware trading cycle with domestic/overseas broker routing
- Market context in AI prompts for better decision making
- Database schema extended with market and exchange_code columns
- Config setting ENABLED_MARKETS for market selection

## Testing
- 19 new tests for market schedule (timezone, DST, lunch breaks)
- All 54 tests passing
- Lint fixes with ruff

## Files Added
- src/markets/schedule.py - Market schedule and timezone logic
- src/broker/overseas.py - KIS overseas stock API client
- tests/test_market_schedule.py - Market schedule test suite

## Files Modified
- src/main.py - Multi-market main loop with retry logic
- src/config.py - ENABLED_MARKETS setting
- src/db.py - market/exchange_code columns with migration
- src/brain/gemini_client.py - Dynamic market context in prompts

Resolves #5

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-04 09:29:25 +09:00

42 lines
1.4 KiB
Python

"""JSON-formatted structured logging for machine readability."""
from __future__ import annotations
import json
import logging
import sys
from datetime import UTC, datetime
from typing import Any
class JSONFormatter(logging.Formatter):
"""Emit log records as single-line JSON objects."""
def format(self, record: logging.LogRecord) -> str:
log_entry: dict[str, Any] = {
"timestamp": datetime.now(UTC).isoformat(),
"level": record.levelname,
"logger": record.name,
"message": record.getMessage(),
}
if record.exc_info and record.exc_info[1]:
log_entry["exception"] = self.formatException(record.exc_info)
# Merge any extra fields attached to the record
for key in ("stock_code", "action", "confidence", "pnl_pct", "order_amount"):
value = getattr(record, key, None)
if value is not None:
log_entry[key] = value
return json.dumps(log_entry, ensure_ascii=False)
def setup_logging(level: int = logging.INFO) -> None:
"""Configure the root logger with JSON output to stdout."""
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JSONFormatter())
root = logging.getLogger()
root.setLevel(level)
# Avoid duplicate handlers on repeated calls
root.handlers.clear()
root.addHandler(handler)