Files
Claude Code eee8b02af2 feat(data): add MOEX (Moscow Exchange) equities data source for analysis/backtest
Adds a read-only MOEX data source backed by the public MOEX ISS HTTP API
(https://iss.moex.com/iss). Targets the TQBR equities board so common Russian
tickers (SBER, GAZP, LKOH, ...) work out of the box.

What's new
- app/data_sources/moex.py: MOEXDataSource with get_kline / get_ticker
  - QuantDinger timeframes mapped to ISS intervals; 5m/15m/30m/4H are
    resampled from the nearest finer native interval (1m or 60m)
  - ISS naive timestamps treated as Europe/Moscow (UTC+3) and converted to
    Unix UTC seconds
  - Pagination via ISS start parameter
  - Symbol normalization handles SBER, SBER.ME, GAZP:MOEX, etc.
- app/data_sources/factory.py: registers MOEX market + aliases
- app/routes/market.py: MOEX in /market/types between HKStock and Crypto
- app/services/symbol_name.py: resolves MOEX names from ISS securities endpoint
- app/services/strategy.py: explicit guard - MOEX cannot be used as a live
  trading market_category (create / batch / update paths)
- tests/test_moex_data_source.py: 11 offline unit tests (mocked HTTP)
- scripts/verify_moex.py: live ISS smoke-test script

Limitations
- Analysis & backtesting only. No live order placement on MOEX.
- TQBR board only by default. Other boards can be enabled by constructing
  MOEXDataSource(board=...) explicitly.
- Sub-hour non-native timeframes (5m/15m/30m) and 4H are resampled
  client-side; large limits at fine timeframes pull more data.
2026-04-28 13:40:39 +00:00

57 lines
1.9 KiB
Python
Executable File

#!/usr/bin/env python3
"""Live verification script for the MOEX data source.
Hits the public MOEX ISS API. Run from the backend_api_python directory:
python scripts/verify_moex.py
python scripts/verify_moex.py SBER 1D 30
python scripts/verify_moex.py GAZP 1H 50
Prints a summary of fetched candles and the latest ticker. Useful for smoke-
testing connectivity in environments where pytest can run only offline tests.
"""
from __future__ import annotations
import os
import sys
from datetime import datetime, timezone
# Make `app` importable when the script is run directly
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
# Minimal env so app.config doesn't blow up
os.environ.setdefault("SECRET_KEY", "verify-moex-script")
os.environ.setdefault("ADMIN_USER", "verify")
os.environ.setdefault("ADMIN_PASSWORD", "verifypass")
from app.data_sources.factory import DataSourceFactory # noqa: E402
def main() -> int:
symbol = sys.argv[1] if len(sys.argv) > 1 else "SBER"
timeframe = sys.argv[2] if len(sys.argv) > 2 else "1D"
limit = int(sys.argv[3]) if len(sys.argv) > 3 else 10
print(f"== MOEX verification == symbol={symbol} timeframe={timeframe} limit={limit}")
src = DataSourceFactory.get_source("MOEX")
print(f"data source: {src.name}")
klines = src.get_kline(symbol, timeframe, limit)
print(f"candles returned: {len(klines)}")
for k in klines[-5:]:
utc = datetime.fromtimestamp(k['time'], tz=timezone.utc).isoformat()
print(f" {utc} O={k['open']:>8} H={k['high']:>8} L={k['low']:>8} C={k['close']:>8} V={k['volume']}")
print()
ticker = src.get_ticker(symbol)
print(f"ticker: {ticker}")
if not klines:
print("WARNING: no candles returned (network blocked? bad symbol? non-trading window?)")
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())