add etf
This commit is contained in:
67
py-client/strategy/etf/boot.py
Normal file
67
py-client/strategy/etf/boot.py
Normal file
@@ -0,0 +1,67 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user