feat: 测温机制定稿——三态骨架+温度(rsi)连续维度(数据验证) 回填market_regime 10年(2016-2026 2319条); 温度分档temp_band(panic/fear/neutral/greed/euphoria); strategy_router v2三态选策略+温度乘数; 废弃四态thermometer; 策略-适用温度表(v_weak=choppy×fear, v_oversold=trend_down×panic, v_next4=trend_up 63%)
This commit is contained in:
@@ -1,176 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""market_thermometer.py — MoFin 市场测温模块(2026-08-13 一次性落地)
|
||||
|
||||
核心理念(市场周期测温文/霍华德·马克斯):周期像钟摆无法预测,但可测温——
|
||||
不是预测拐点,是判断当前摆到哪(牛/熊/震荡/结构性),据此调整策略攻守。
|
||||
|
||||
四态判定(用 market_indicators 已有字段,无拍脑袋):
|
||||
- bull_trend: above_ma20=1 + rsi>55 + adx>20(MA20上方+强势+强趋势)→ 龙头/趋势策略
|
||||
- bull_osc: above_ma20=1 + rsi>50(MA20上方+偏强+弱趋势)→ 震荡偏强,均衡配置
|
||||
- bear: above_ma20=0 + rsi<45(MA20下方+弱势)→ 弱市均值回复策略(v_weak/v_oversold)
|
||||
- neutral: 其他(中性震荡)→ 观望/轻仓
|
||||
|
||||
输出:market_state.json(当前状态+历史分态统计),供策略动态切换
|
||||
"""
|
||||
import sqlite3
|
||||
import json
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
|
||||
DB_PATH = "/home/hmo/MoFin/data/mofin.db"
|
||||
OUT_PATH = "/home/hmo/MoFin/data/market_state.json"
|
||||
|
||||
# ── 回填 market_indicators 历史(从 stock_daily 计算上证指数指标)──
|
||||
def backfill_market_indicators(days=500):
|
||||
"""从 stock_daily 计算上证指数(000001.SH)的 mkt_* 指标,回填到 market_indicators"""
|
||||
c = sqlite3.connect(DB_PATH)
|
||||
# 上证指数代码(MoFin 约定:000001 = 上证指数)
|
||||
rows = c.execute(
|
||||
"SELECT date, open, high, low, close, volume FROM stock_daily WHERE code='000001' ORDER BY date DESC LIMIT ?",
|
||||
(days,)
|
||||
).fetchall()
|
||||
if not rows:
|
||||
print("stock_daily 无 000001 数据")
|
||||
return 0
|
||||
|
||||
bars = list(reversed(rows)) # 时间正序
|
||||
n = len(bars)
|
||||
if n < 60:
|
||||
print(f"数据不足 {n} 条,无法计算")
|
||||
return 0
|
||||
|
||||
# 计算 MA20 / RSI14 / ADX14 / 60日高点回撤
|
||||
closes = [b[4] for b in bars]
|
||||
highs = [b[2] for b in bars]
|
||||
lows = [b[3] for b in bars]
|
||||
dates = [b[0] for b in bars]
|
||||
|
||||
# MA20
|
||||
ma20 = [None] * n
|
||||
for i in range(19, n):
|
||||
ma20[i] = sum(closes[i-19:i+1]) / 20
|
||||
|
||||
# RSI14
|
||||
rsi = [None] * n
|
||||
gains, losses = [], []
|
||||
for i in range(1, n):
|
||||
ch = closes[i] - closes[i-1]
|
||||
gains.append(max(ch, 0))
|
||||
losses.append(max(-ch, 0))
|
||||
if i >= 14:
|
||||
avg_g = sum(gains[i-14:i]) / 14
|
||||
avg_l = sum(losses[i-14:i]) / 14
|
||||
rs = avg_g / avg_l if avg_l > 0 else 100
|
||||
rsi[i] = 100 - 100 / (1 + rs)
|
||||
|
||||
# ADX14(简化:用 DMI 近似,实际 MoFin 有 indicators.calc_adx,这里用简化版)
|
||||
adx = [None] * n
|
||||
tr_list = [0.0]
|
||||
pdm, ndm = [0.0], [0.0]
|
||||
for i in range(1, n):
|
||||
h, l, pc = highs[i], lows[i], closes[i-1]
|
||||
tr = max(h - l, abs(h - pc), abs(l - pc))
|
||||
tr_list.append(tr)
|
||||
up_move = highs[i] - highs[i-1]
|
||||
down_move = lows[i-1] - lows[i]
|
||||
pdm.append(up_move if up_move > down_move and up_move > 0 else 0)
|
||||
ndm.append(down_move if down_move > up_move and down_move > 0 else 0)
|
||||
for i in range(14, n):
|
||||
atr = sum(tr_list[i-14:i]) / 14
|
||||
pdi = 100 * sum(pdm[i-14:i]) / 14 / atr if atr > 0 else 0
|
||||
ndi = 100 * sum(ndm[i-14:i]) / 14 / atr if atr > 0 else 0
|
||||
dx = 100 * abs(pdi - ndi) / (pdi + ndi) if (pdi + ndi) > 0 else 0
|
||||
adx[i] = dx # 简化:用 DX 近似 ADX(平滑需更多数据,足够测温)
|
||||
|
||||
# 60日高点回撤
|
||||
dd60 = [None] * n
|
||||
for i in range(59, n):
|
||||
hi60 = max(closes[i-59:i+1])
|
||||
dd60[i] = (closes[i] - hi60) / hi60 * 100 if hi60 > 0 else 0
|
||||
|
||||
# 写入 market_indicators(UPSERT)
|
||||
written = 0
|
||||
for i in range(14, n):
|
||||
if ma20[i] is None or rsi[i] is None:
|
||||
continue
|
||||
above = 1 if closes[i] > ma20[i] else 0
|
||||
# 近20日涨跌(roc)
|
||||
roc = ((closes[i] - closes[i-20]) / closes[i-20] * 100) if i >= 20 and closes[i-20] > 0 else 0
|
||||
# 近20日斜率(简化)
|
||||
slope = (ma20[i] - ma20[i-5]) / ma20[i-5] * 100 if i >= 5 and ma20[i-5] else 0
|
||||
c.execute(
|
||||
"""INSERT OR REPLACE INTO market_indicators
|
||||
(date, mkt_rsi, mkt_dd60, mkt_adx, mkt_above_ma20, mkt_down_days, mkt_slope, mkt_roc, updated_at)
|
||||
VALUES (?,?,?,?,?,?,?,?,?)""",
|
||||
(dates[i], rsi[i], dd60[i], adx[i], above, 0, slope, roc, datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
|
||||
)
|
||||
written += 1
|
||||
c.commit()
|
||||
c.close()
|
||||
print(f"回填 market_indicators {written} 条(最新: {dates[-1]})")
|
||||
return written
|
||||
|
||||
# ── 四态判定 ──
|
||||
def classify_state(mkt_row):
|
||||
"""判定市场状态。输入:market_indicators 行 dict"""
|
||||
rsi = mkt_row.get("mkt_rsi", 50)
|
||||
adx = mkt_row.get("mkt_adx", 20)
|
||||
above = mkt_row.get("mkt_above_ma20", 0)
|
||||
dd60 = mkt_row.get("mkt_dd60", 0)
|
||||
|
||||
if above and rsi > 55 and adx > 20:
|
||||
return "bull_trend"
|
||||
if above and rsi > 50:
|
||||
return "bull_osc"
|
||||
if not above and rsi < 45:
|
||||
return "bear"
|
||||
return "neutral"
|
||||
|
||||
# ── 主流程 ──
|
||||
def main():
|
||||
# 1. 回填历史(500 日 ≈ 2 年)
|
||||
backfill_market_indicators(500)
|
||||
|
||||
# 2. 当前状态
|
||||
c = sqlite3.connect(DB_PATH)
|
||||
row = c.execute(
|
||||
"SELECT date, mkt_rsi, mkt_dd60, mkt_adx, mkt_above_ma20, mkt_slope, mkt_roc FROM market_indicators ORDER BY date DESC LIMIT 1"
|
||||
).fetchone()
|
||||
c.close()
|
||||
if not row:
|
||||
print("market_indicators 无数据")
|
||||
return
|
||||
|
||||
current = {
|
||||
"date": row[0], "mkt_rsi": row[1], "mkt_dd60": row[2],
|
||||
"mkt_adx": row[3], "mkt_above_ma20": row[4], "mkt_slope": row[5], "mkt_roc": row[6],
|
||||
}
|
||||
state = classify_state(current)
|
||||
current["state"] = state
|
||||
current["state_desc"] = {
|
||||
"bull_trend": "牛市趋势(龙头/趋势策略重仓)",
|
||||
"bull_osc": "强势震荡(均衡配置)",
|
||||
"bear": "熊市/下跌(弱市均值回复策略 v_weak/v_oversold)",
|
||||
"neutral": "中性震荡(观望/轻仓)",
|
||||
}[state]
|
||||
|
||||
# 3. 历史分态统计(近 250 交易日 ≈ 1 年)
|
||||
c = sqlite3.connect(DB_PATH)
|
||||
rows = c.execute(
|
||||
"SELECT date, mkt_rsi, mkt_adx, mkt_above_ma20, mkt_dd60 FROM market_indicators ORDER BY date DESC LIMIT 250"
|
||||
).fetchall()
|
||||
c.close()
|
||||
from collections import Counter
|
||||
hist = Counter(classify_state({"mkt_rsi": r[1], "mkt_adx": r[2], "mkt_above_ma20": r[3], "mkt_dd60": r[4]}) for r in rows)
|
||||
current["hist_1y"] = dict(hist)
|
||||
current["updated_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 4. 写入 market_state.json
|
||||
Path(OUT_PATH).write_text(json.dumps(current, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"market_state.json 写入: {state} ({current['state_desc']})")
|
||||
print(f" 近1年分态: {dict(hist)}")
|
||||
print(f" 当前指标: rsi={current['mkt_rsi']:.1f} adx={current['mkt_adx']:.1f} above={current['mkt_above_ma20']} dd60={current['mkt_dd60']:.1f}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,74 +1,137 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""strategy_router.py — MoFin 策略动态路由(2026-08-13 一次性落地)
|
||||
"""strategy_router.py v2 — MoFin 策略动态路由(三态 + 温度维度,2026-08-13 重写)
|
||||
|
||||
核心:读 market_state.json 的当前市场状态,决定各策略权重/开关
|
||||
- bear: v_weak/v_oversold 降权 50%(信号质量差,28%胜率)
|
||||
- bull_osc: v_weak/v_oversold 正常(60%胜率,当前状态)
|
||||
- bull_trend: v_weak/v_oversold 降权 50%,启用龙头策略(待研究)
|
||||
- neutral: 轻仓观望
|
||||
结论(数据验证):三态骨架 + 温度(rsi)连续维度。
|
||||
- 三态(trend_up/choppy/trend_down)→ 选策略类型(market_regime.py 判定)
|
||||
- 温度(rsi 档位)→ 仓位乘数(temp_band.py 判定,连续量)
|
||||
|
||||
输出:strategy_weights.json 供扫描器/重评脚本读取
|
||||
策略-适用温度映射(数据来源:2026-08-13 分市场状态检验,见 docs/decisions/2026-08-13-事项五):
|
||||
v_weak → choppy × [fear, neutral] (震荡市+恐慌最佳 71%)
|
||||
v_oversold → trend_down × [panic, fear] (下跌市+恐慌最佳 64-86%)
|
||||
v_next4 → trend_up × [greed, euphoria](趋势市 63%)
|
||||
v8.1 → trend_up (趋势市 57%)
|
||||
s2_panic → trend_down × [panic] (极端恐慌 86%)
|
||||
v_lurk_v3 → [choppy, trend_down] × [fear, neutral](77%/71%)
|
||||
|
||||
输出:strategy_weights.json(各策略 weight 由 状态×温度 决定)
|
||||
"""
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
MARKET_STATE = Path("/home/hmo/MoFin/data/market_state.json")
|
||||
_SCRIPT_DIR = Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(_SCRIPT_DIR))
|
||||
sys.path.insert(0, "/home/hmo/MoFin")
|
||||
|
||||
OUT = Path("/home/hmo/MoFin/data/strategy_weights.json")
|
||||
|
||||
def load_market_state():
|
||||
if MARKET_STATE.exists():
|
||||
return json.loads(MARKET_STATE.read_text(encoding="utf-8"))
|
||||
return {"state": "neutral", "date": ""}
|
||||
|
||||
def route(state):
|
||||
"""市场状态 → 策略权重。返回 dict"""
|
||||
# 基准权重(等权,程飞:不确定时等权最稳健)
|
||||
base = {"v_weak": 1.0, "v_oversold": 1.0, "leader": 0.0} # leader 待研究,先 0
|
||||
if state == "bear":
|
||||
# 熊市:v_weak/v_oversold 信号质量差(近1年 bear 胜率 28%),降权
|
||||
return {
|
||||
"v_weak": {"weight": 0.5, "action": "降权50%", "reason": "bear 胜率28%,信号质量差"},
|
||||
"v_oversold": {"weight": 0.5, "action": "降权50%", "reason": "bear 胜率28%"},
|
||||
"leader": {"weight": 0.0, "action": "停用", "reason": "bear 不适用龙头"},
|
||||
"state": state,
|
||||
def load_regime():
|
||||
"""读取最新 market_regime(三态)"""
|
||||
try:
|
||||
from market_regime import load_market_regime
|
||||
return load_market_regime()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def get_temp():
|
||||
"""读取市场温度(rsi 档位)"""
|
||||
try:
|
||||
from temp_band import get_market_temp, temp_multiplier
|
||||
return get_market_temp()
|
||||
except Exception as e:
|
||||
print(f" [router] 温度获取失败: {e}", file=sys.stderr)
|
||||
return {"band": "unknown", "rsi": None}
|
||||
|
||||
|
||||
# 策略-适用温度映射(基础数据,数据驱动固化)
|
||||
# weight = 状态匹配度 × 温度乘数
|
||||
STRATEGY_TEMP = {
|
||||
"v_weak": {
|
||||
"family": "mr",
|
||||
"regimes": ["choppy"],
|
||||
"desc": "震荡市超跌反弹(choppy+恐慌71%最佳)",
|
||||
},
|
||||
"v_oversold": {
|
||||
"family": "mr",
|
||||
"regimes": ["trend_down"],
|
||||
"desc": "下跌市超跌(trend_down+恐慌64-86%)",
|
||||
},
|
||||
"v_next4": {
|
||||
"family": "trend",
|
||||
"regimes": ["trend_up"],
|
||||
"desc": "趋势市追涨(trend_up 63%)",
|
||||
},
|
||||
"v8.1": {
|
||||
"family": "trend",
|
||||
"regimes": ["trend_up"],
|
||||
"desc": "波段先出再进(trend_up 57%)",
|
||||
},
|
||||
"s2_panic": {
|
||||
"family": "mr",
|
||||
"regimes": ["trend_down"],
|
||||
"desc": "极端恐慌买超跌(panic 86%)",
|
||||
},
|
||||
"v_lurk_v3": {
|
||||
"family": "mr",
|
||||
"regimes": ["choppy", "trend_down"],
|
||||
"desc": "潜伏型(choppy/trend_down 71-77%)",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def route(regime, temp):
|
||||
"""三态 + 温度 → 各策略权重"""
|
||||
current_regime = regime.get("regime", "unknown") if regime else "unknown"
|
||||
band = temp.get("band", "unknown")
|
||||
family = temp.get("family", "mr")
|
||||
|
||||
weights = {}
|
||||
for name, cfg in STRATEGY_TEMP.items():
|
||||
# 状态匹配度:适用温区包含当前状态 → 1.0,否则 0.3(保留观察)
|
||||
matched = current_regime in cfg["regimes"]
|
||||
base = 1.0 if matched else 0.3
|
||||
# 温度乘数(按策略家族)
|
||||
try:
|
||||
from temp_band import temp_multiplier
|
||||
mult = temp_multiplier(band, cfg["family"])
|
||||
except Exception:
|
||||
mult = 0.8
|
||||
weights[name] = {
|
||||
"weight": round(base * mult, 2),
|
||||
"regime": current_regime,
|
||||
"temp_band": band,
|
||||
"family": cfg["family"],
|
||||
"matched": matched,
|
||||
"desc": cfg["desc"],
|
||||
}
|
||||
if state == "bull_osc":
|
||||
# 强势震荡:v_weak/v_oversold 最有效(60%胜率),正常
|
||||
return {
|
||||
"v_weak": {"weight": 1.0, "action": "正常", "reason": "bull_osc 胜率60%,最有效"},
|
||||
"v_oversold": {"weight": 1.0, "action": "正常", "reason": "bull_osc 适用"},
|
||||
"leader": {"weight": 0.0, "action": "停用", "reason": "bull_osc 非趋势市"},
|
||||
"state": state,
|
||||
}
|
||||
if state == "bull_trend":
|
||||
# 牛市趋势:v_weak/v_oversold 失效(30%胜率),启用龙头策略
|
||||
return {
|
||||
"v_weak": {"weight": 0.5, "action": "降权50%", "reason": "bull_trend 胜率30%"},
|
||||
"v_oversold": {"weight": 0.5, "action": "降权50%", "reason": "bull_trend 失效"},
|
||||
"leader": {"weight": 1.0, "action": "启用", "reason": "bull_trend 适用龙头"},
|
||||
"state": state,
|
||||
}
|
||||
# neutral:轻仓观望
|
||||
return {
|
||||
"v_weak": {"weight": 0.5, "action": "轻仓", "reason": "neutral 观望"},
|
||||
"v_oversold": {"weight": 0.5, "action": "轻仓", "reason": "neutral 观望"},
|
||||
"leader": {"weight": 0.0, "action": "停用", "reason": "neutral 观望"},
|
||||
"state": state,
|
||||
}
|
||||
return weights
|
||||
|
||||
|
||||
def main():
|
||||
ms = load_market_state()
|
||||
state = ms.get("state", "neutral")
|
||||
weights = route(state)
|
||||
weights["market_state"] = ms
|
||||
weights["updated_at"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
OUT.write_text(json.dumps(weights, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"strategy_weights.json 写入: {state}")
|
||||
for k, v in weights.items():
|
||||
if isinstance(v, dict) and "weight" in v:
|
||||
print(f" {k}: weight={v['weight']} ({v['action']}) - {v['reason']}")
|
||||
regime = load_regime()
|
||||
temp = get_temp()
|
||||
if not regime:
|
||||
print(" [router] market_regime 无数据", file=sys.stderr)
|
||||
regime = {"regime": "unknown"}
|
||||
|
||||
weights = route(regime, temp)
|
||||
out = {
|
||||
"state": regime.get("regime", "unknown"),
|
||||
"temp_band": temp.get("band", "unknown"),
|
||||
"temp_rsi": temp.get("rsi"),
|
||||
"weights": weights,
|
||||
"updated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
"note": "三态(trend_up/choppy/trend_down)选策略类型 + 温度(rsi)乘数决定仓位",
|
||||
}
|
||||
OUT.write_text(json.dumps(out, ensure_ascii=False, indent=1), encoding="utf-8")
|
||||
print(f"strategy_weights.json: 状态={out['state']} 温度={out['temp_band']}(rsi={out['temp_rsi']})")
|
||||
for name, w in weights.items():
|
||||
print(f" {name:<12} weight={w['weight']:<5} regime={w['regime']:<10} band={w['temp_band']:<8} {'✓' if w['matched'] else '观察'}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""temp_band.py — 市场温度分档模块(2026-08-13)
|
||||
|
||||
结论(数据验证):三态骨架 + 温度(rsi)连续维度。
|
||||
- 三态(trend_up/choppy/trend_down)= market_regime.py 已有(策略类型分类)
|
||||
- 温度(rsi 分档)= 本模块(力度维度,仓位乘数)
|
||||
|
||||
数据依据(2026-08-13 分市场状态检验):
|
||||
v_weak choppy: 恐慌<45 -> 71%胜率 / 中性45-60 -> 46% / 亢奋>60 -> 37%
|
||||
v_oversold trend_down: 恐慌<35 -> 64% / 偏弱35-50 -> 53% / 中性>50 -> 0%
|
||||
s2_panic trend_down 恐慌<35: 86%胜率 +16.6%
|
||||
|
||||
档位:panic(<35) / fear(35-45) / neutral(45-60) / greed(60-70) / euphoria(>70)
|
||||
用法:
|
||||
from temp_band import get_market_temp, temp_multiplier
|
||||
t = get_market_temp() # {rsi, band, regime}
|
||||
mult = temp_multiplier(t["band"], strategy_family="mr")
|
||||
"""
|
||||
import sqlite3
|
||||
from pathlib import Path
|
||||
|
||||
DB = Path("/home/hmo/MoFin/data/mofin.db")
|
||||
INDEX = "sh000001"
|
||||
|
||||
|
||||
def calc_rsi(series, n=14):
|
||||
result = [None] * len(series)
|
||||
if len(series) < n + 1:
|
||||
return result
|
||||
gains, losses = [], []
|
||||
for i in range(1, len(series)):
|
||||
ch = series[i] - series[i - 1]
|
||||
gains.append(max(ch, 0))
|
||||
losses.append(max(-ch, 0))
|
||||
if i >= n:
|
||||
avg_g = sum(gains[i - n:i]) / n
|
||||
avg_l = sum(losses[i - n:i]) / n
|
||||
rs = avg_g / avg_l if avg_l > 0 else 100
|
||||
result[i] = 100 - 100 / (1 + rs)
|
||||
return result
|
||||
|
||||
|
||||
def temp_band(rsi):
|
||||
"""连续 rsi -> 温度档位"""
|
||||
if rsi is None:
|
||||
return "unknown"
|
||||
if rsi < 35:
|
||||
return "panic"
|
||||
if rsi < 45:
|
||||
return "fear"
|
||||
if rsi < 60:
|
||||
return "neutral"
|
||||
if rsi < 70:
|
||||
return "greed"
|
||||
return "euphoria"
|
||||
|
||||
|
||||
def temp_multiplier(band, strategy_family="mr"):
|
||||
"""温度 -> 仓位乘数。mr=均值回复(恐慌重仓),trend=趋势(恐慌回避)"""
|
||||
if strategy_family == "trend":
|
||||
return {
|
||||
"panic": 0.0, "fear": 0.0, "neutral": 0.5,
|
||||
"greed": 1.0, "euphoria": 0.5, "unknown": 0.5,
|
||||
}.get(band, 0.5)
|
||||
return {
|
||||
"panic": 1.5, "fear": 1.0, "neutral": 0.8,
|
||||
"greed": 0.5, "euphoria": 0.3, "unknown": 0.5,
|
||||
}.get(band, 0.5)
|
||||
|
||||
|
||||
def get_market_temp(db_path=None):
|
||||
"""读取最新市场温度(rsi + 档位 + regime)。"""
|
||||
db = db_path or DB
|
||||
conn = sqlite3.connect(str(db), timeout=5)
|
||||
try:
|
||||
# 上证最近 30 日收盘算 rsi
|
||||
rows = conn.execute(
|
||||
"SELECT date, close FROM stock_daily WHERE code=? ORDER BY date DESC LIMIT 30",
|
||||
(INDEX,)
|
||||
).fetchall()
|
||||
# 最新 regime
|
||||
reg = conn.execute(
|
||||
"SELECT date, regime FROM market_regime ORDER BY date DESC LIMIT 1"
|
||||
).fetchone()
|
||||
finally:
|
||||
conn.close()
|
||||
if not rows or len(rows) < 15:
|
||||
return {"rsi": None, "band": "unknown", "regime": "unknown", "date": ""}
|
||||
rows = list(reversed(rows))
|
||||
closes = [r[1] for r in rows]
|
||||
dates = [r[0] for r in rows]
|
||||
rsi_series = calc_rsi(closes)
|
||||
rsi_now = rsi_series[-1] if rsi_series else None
|
||||
band = temp_band(rsi_now)
|
||||
regime = reg[1] if reg else "unknown"
|
||||
reg_date = reg[0] if reg else ""
|
||||
return {
|
||||
"rsi": round(rsi_now, 1) if rsi_now is not None else None,
|
||||
"band": band,
|
||||
"regime": regime,
|
||||
"date": dates[-1],
|
||||
"regime_date": reg_date,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
t = get_market_temp()
|
||||
print(f"温度: rsi={t['rsi']} band={t['band']} regime={t['regime']} ({t['date']})")
|
||||
print(f" mr 乘数: {temp_multiplier(t['band'], 'mr')}")
|
||||
print(f" trend 乘数: {temp_multiplier(t['band'], 'trend')}")
|
||||
Reference in New Issue
Block a user