Files
big-qmt/py-client/strategy/etf/boot.py
2026-09-17 00:56:05 +08:00

68 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""ETF 策略入口:每 30 秒运行,日线指标当天缓存,失败标的单独重试。"""
from datetime import datetime, timedelta
import hashlib
import logging as log
from pathlib import Path
import time
import httpx
import config
from libs.calc import trading_time
from libs.snapshot import cache_portfolio
from sdk import Client
from .config import load
from .data import daily_bars
from .engine import Engine
from .indicators import calculate
from .state import Store
def StartETF() -> None:
cfg = load(config.account_config.etf_config_path or None)
account = str(config.account_config.account_id).strip()
if not account:
raise ValueError('ETF 策略缺少账户编号')
key = hashlib.sha256(account.encode('utf-8')).hexdigest()
store = Store(Path(config.global_config.qmt_data_dir) / 'etf' / key / 'state.json', account)
# 独立 HTTP 连接池读取外部日线,不向外部接口发送 QMT 认证信息。
with Client(config.global_config.qmt_base_url, config.global_config.qmt_token, config.HTTP_TIMEOUT) as client, \
httpx.Client(timeout=config.HTTP_TIMEOUT) as history_client:
engine = Engine(client, cfg, store, config.account_config.min_cash_ratio,
config.account_config.excluded_codes)
log.info('[ETF启动] 标的=%s 每次=%d手 每只上限=%d手 状态=%s',
cfg.codes, cfg.buy_hands, cfg.max_hands, store.path)
log.info('[ETF启动] 管理配置白名单内已有持仓,卖出以券商可用份额为限')
indicators, retry_at = {}, {}
cached_day = None
while True:
now = datetime.now()
if now.hour >= 15:
log.info('[ETF结束] 已到 15:00')
return
if trading_time(now):
try:
if cached_day != now.date():
indicators, retry_at, cached_day = {}, {}, now.date()
for code in cfg.codes:
if code in indicators or now < retry_at.get(code, datetime.min):
continue
try:
rows = daily_bars(history_client, code, now.date())
indicators[code] = calculate(rows, now.date(), cfg)
log.info('[ETF指标] %s %s', code, indicators[code])
except Exception:
retry_at[code] = now + timedelta(minutes=5)
log.exception('[ETF日线] %s 获取或计算失败5分钟后重试', code)
portfolio = client.portfolio()
ticks = client.full_tick(list(cfg.codes))
engine.run(portfolio, ticks, indicators, datetime.now())
try:
cache_portfolio(account, portfolio.assets, list(portfolio.positions.values()), client.deals())
except Exception:
log.exception('[ETF采集] 成交快照读取失败')
except Exception:
log.exception('[ETF异常] 本轮失败,下一轮继续')
time.sleep(30 - datetime.now().second % 30)