# mt5_quotes_to_local_loop.py
# MT5のBid/Askを 30秒ごとに C:\fx_http\quotes.json へ保存（FTPなし）
import os
import json
import time
import traceback
from datetime import datetime, timezone, timedelta

import MetaTrader5 as mt5

# =========================
# 設定
# =========================
INTERVAL_SEC = 30
ROUND_DIGITS = 5

MT5_TERMINAL_PATH = r"C:\Program Files\Gaitame Finest MetaTrader 5 Terminal\terminal64.exe"

OUT_DIR = r"C:\fx_http"
OUT_JSON = os.path.join(OUT_DIR, "quotes.json")

SYMBOLS = [
    "USDJPY-",
    "GBPJPY-",
    "GBPUSD-",
    "EURUSD-",
    "AUDUSD-",
    "USDCAD-",
]

# =========================
def now_jst():
    return datetime.now(timezone(timedelta(hours=9)))

def now_jst_str():
    return now_jst().strftime("%Y-%m-%d %H:%M:%S")

def safe_float(x, default=None):
    try:
        return float(x)
    except Exception:
        return default

def write_json_atomic(path: str, data: dict):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    tmp = path + ".tmp"
    with open(tmp, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    os.replace(tmp, path)

def mt5_init():
    if not mt5.initialize(path=MT5_TERMINAL_PATH):
        raise RuntimeError(f"MT5 init failed: {mt5.last_error()}")

def fetch_quotes():
    rows = []
    for sym in SYMBOLS:
        row = {"symbol": sym}

        info = mt5.symbol_info(sym)
        if info is None:
            row["error"] = "symbol_info_none"
            rows.append(row)
            continue

        if not info.visible:
            mt5.symbol_select(sym, True)

        tick = mt5.symbol_info_tick(sym)
        if tick is None:
            row["error"] = "tick_none"
            rows.append(row)
            continue

        bid = safe_float(getattr(tick, "bid", None))
        ask = safe_float(getattr(tick, "ask", None))

        row.update({
            "bid": round(bid, ROUND_DIGITS) if bid is not None else None,
            "ask": round(ask, ROUND_DIGITS) if ask is not None else None,
            "time": int(getattr(tick, "time", 0) or 0),
            "time_msc": int(getattr(tick, "time_msc", 0) or 0),
        })
        rows.append(row)

    payload = {
        "updated_at": now_jst_str(),
        "updated_at_unix": int(now_jst().timestamp()),
        "quotes": rows,
    }
    return payload

def main():
    print("[START]", now_jst_str())
    mt5_init()
    print("[MT5] initialized")
    print(f"[INFO] interval={INTERVAL_SEC}s -> {OUT_JSON}")

    while True:
        try:
            payload = fetch_quotes()
            write_json_atomic(OUT_JSON, payload)
            print("[OK]", payload["updated_at"], "written")
            time.sleep(INTERVAL_SEC)
        except KeyboardInterrupt:
            print("\n[STOP] KeyboardInterrupt")
            break
        except Exception as e:
            print("[ERROR]", now_jst_str(), repr(e))
            print(traceback.format_exc())
            time.sleep(10)

    try:
        mt5.shutdown()
    except Exception:
        pass

if __name__ == "__main__":
    main()
