fix: 资格判定用当前激活温区(state)优先,best_regime回退——择优激活的v_lurk_v3不再被choppy误判
This commit is contained in:
@@ -0,0 +1,189 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""b_td1_v3_scanner.py — B组超跌·原池优选 实盘扫描器(2026-08-17 择优激活落地)
|
||||||
|
|
||||||
|
由果及因(老莫指正):b_td1 原池(5000信号)信号太密集,用 score 每日top5截断 → 信号572/成交316/比1.8
|
||||||
|
实盘对齐回测:
|
||||||
|
池(b_td1 原池):dist_lo20 > 5(距20日低点>5%)——news3/mcap_q/pe_q 为回测外部因子,
|
||||||
|
实盘用可获取近似:mcap_q<0.3(市值分位,从 stock_daily 市值算)pe_q<0.3 暂缺则放宽
|
||||||
|
score(池内超跌评分):bias60深度 + rsi + sec_ret20 + ret5
|
||||||
|
每日 top5(score 降序)
|
||||||
|
出场建议:tp15% / sl8% / max35日(原版模拟验证参数)
|
||||||
|
|
||||||
|
数据源:腾讯前复权日K(与 mr_scanner 同源,零偏差)+ 市值分位从 stock_daily
|
||||||
|
输出:candidates 表(sector='b_td1_v3')
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python3 b_td1_v3_scanner.py # 完整扫描
|
||||||
|
python3 b_td1_v3_scanner.py --force # 忽略门控
|
||||||
|
python3 b_td1_v3_scanner.py --top N # 输出前 N 只(默认 5)
|
||||||
|
"""
|
||||||
|
import sys, json, sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from indicators import calc_ma, calc_rsi
|
||||||
|
from market_data import fetch_tx_klines, get_stock_pool
|
||||||
|
|
||||||
|
DB_PATH = Path("/home/hmo/MoFin/data/mofin.db")
|
||||||
|
|
||||||
|
TOP_N = 5
|
||||||
|
EXIT_CFG = {"tp_pct": 0.15, "sl_pct": 0.08, "max_hold_days": 35}
|
||||||
|
|
||||||
|
|
||||||
|
def load_regime():
|
||||||
|
"""当前温区(平滑优先)"""
|
||||||
|
try:
|
||||||
|
from regime_gate import get_current_regime
|
||||||
|
rg = get_current_regime()
|
||||||
|
if rg and rg.get("regime") != "unknown":
|
||||||
|
return rg.get("regime")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(str(DB_PATH), timeout=5)
|
||||||
|
r = conn.execute("SELECT regime FROM market_regime WHERE market='a' ORDER BY date DESC LIMIT 1").fetchone()
|
||||||
|
conn.close()
|
||||||
|
return r[0] if r else "unknown"
|
||||||
|
except Exception:
|
||||||
|
return "unknown"
|
||||||
|
|
||||||
|
|
||||||
|
def mcap_quantile(code):
|
||||||
|
"""从 stock_daily 算市值分位(mcap_q 近似)"""
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(str(DB_PATH), timeout=5)
|
||||||
|
row = conn.execute(
|
||||||
|
"SELECT amount, close FROM stock_daily WHERE code=? ORDER BY date DESC LIMIT 1",
|
||||||
|
(code,)).fetchone()
|
||||||
|
if not row or not row[0]:
|
||||||
|
conn.close()
|
||||||
|
return 0.3 # 缺省给中值(放宽)
|
||||||
|
# 全市场今日成交额分位
|
||||||
|
rows = conn.execute(
|
||||||
|
"SELECT amount FROM stock_daily WHERE date=(SELECT MAX(date) FROM stock_daily) AND amount IS NOT NULL"
|
||||||
|
).fetchall()
|
||||||
|
conn.close()
|
||||||
|
amounts = sorted([r[0] for r in rows if r[0]])
|
||||||
|
if not amounts:
|
||||||
|
return 0.3
|
||||||
|
import bisect
|
||||||
|
pos = bisect.bisect_left(amounts, row[0])
|
||||||
|
return round(pos / max(len(amounts), 1), 2)
|
||||||
|
except Exception:
|
||||||
|
return 0.3
|
||||||
|
|
||||||
|
|
||||||
|
def score_of(bias60, rsi, sec_ret20, ret5):
|
||||||
|
"""池内超跌评分(与回测 b_td1_v3_gen 一致)"""
|
||||||
|
sc = 0
|
||||||
|
if bias60 is not None:
|
||||||
|
sc += 40 if bias60 < -30 else 32 if bias60 < -20 else 20 if bias60 < -10 else 8
|
||||||
|
if rsi is not None:
|
||||||
|
sc += 30 if rsi < 30 else 24 if rsi < 40 else 14 if rsi < 50 else 6
|
||||||
|
if sec_ret20 is not None:
|
||||||
|
sc += 20 if sec_ret20 < -20 else 14 if sec_ret20 < -10 else 8 if sec_ret20 < 0 else 3
|
||||||
|
if ret5 is not None:
|
||||||
|
sc += 10 if ret5 < -25 else 7 if ret5 < -15 else 4 if ret5 < -8 else 1
|
||||||
|
return sc
|
||||||
|
|
||||||
|
|
||||||
|
def sec_ret20_approx(code):
|
||||||
|
"""行业20日涨幅近似:用该股所在板块指数或简化为大盘对照"""
|
||||||
|
# 实盘简化:返回 None(score 该分项给0),避免复杂行业数据依赖
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def check_b_td1(klines, code):
|
||||||
|
"""b_td1_v3 筛选:池条件 + score"""
|
||||||
|
if not klines or len(klines) < 60:
|
||||||
|
return None
|
||||||
|
closes = [k["close"] for k in klines]
|
||||||
|
lows = [k["low"] for k in klines]
|
||||||
|
i = len(klines) - 1
|
||||||
|
close = closes[i]
|
||||||
|
if close <= 0:
|
||||||
|
return None
|
||||||
|
ma60 = calc_ma(closes, 60)
|
||||||
|
m60 = ma60[i]
|
||||||
|
if not m60 or m60 <= 0:
|
||||||
|
return None
|
||||||
|
bias60 = (close - m60) / m60 * 100
|
||||||
|
# 池条件:dist_lo20 > 5(距20日低点>5%)
|
||||||
|
lo20 = min(lows[max(0, i - 19):i + 1])
|
||||||
|
dist_lo20 = (close - lo20) / lo20 * 100 if lo20 > 0 else 0
|
||||||
|
if dist_lo20 <= 5:
|
||||||
|
return None
|
||||||
|
rsi = calc_rsi(closes)
|
||||||
|
rsi_v = rsi[i] if i < len(rsi) else None
|
||||||
|
prev5 = closes[i - 5] if i >= 5 else 0
|
||||||
|
ret5 = (close - prev5) / prev5 * 100 if prev5 > 0 else 0
|
||||||
|
# 市值分位
|
||||||
|
mcap_q = mcap_quantile(code)
|
||||||
|
if mcap_q >= 0.3:
|
||||||
|
return None # 池条件:小市值
|
||||||
|
# score
|
||||||
|
sec20 = sec_ret20_approx(code)
|
||||||
|
sc = score_of(bias60, rsi_v, sec20, ret5)
|
||||||
|
return {
|
||||||
|
"price": close, "bias60": round(bias60, 2), "rsi": round(rsi_v, 2) if rsi_v else None,
|
||||||
|
"ret5": round(ret5, 2), "dist_lo20": round(dist_lo20, 2), "mcap_q": mcap_q,
|
||||||
|
"score": sc, "target": round(close * (1 + EXIT_CFG["tp_pct"]), 2),
|
||||||
|
"stop_loss": round(close * (1 - EXIT_CFG["sl_pct"]), 2),
|
||||||
|
"date": klines[i]["date"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import argparse
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--force", action="store_true")
|
||||||
|
ap.add_argument("--top", type=int, default=TOP_N)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
regime = load_regime()
|
||||||
|
print(f"[b_td1_v3] {datetime.now().strftime('%H:%M')} 扫描开始 温区={regime}", flush=True)
|
||||||
|
# 温区门控:trend_down/choppy 才扫(超跌池主战场)
|
||||||
|
if not args.force and regime not in ("trend_down", "choppy"):
|
||||||
|
print(f" 温区 {regime} 非超跌池主战场,跳过", flush=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
all_stocks, existing = get_stock_pool()
|
||||||
|
print(f" 股票池 {len(all_stocks)} 只", flush=True)
|
||||||
|
hits = []
|
||||||
|
for code, name in all_stocks:
|
||||||
|
try:
|
||||||
|
klines = fetch_tx_klines(code, datalen=120)
|
||||||
|
sig = check_b_td1(klines, code)
|
||||||
|
if sig:
|
||||||
|
hits.append((code, name, sig))
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
# score 降序 top-N
|
||||||
|
hits.sort(key=lambda x: -x[2]["score"])
|
||||||
|
hits = hits[: args.top]
|
||||||
|
print(f" 命中 {len(hits)} 只(score降序前{args.top})", flush=True)
|
||||||
|
|
||||||
|
conn = sqlite3.connect(str(DB_PATH), timeout=10)
|
||||||
|
inserted = 0
|
||||||
|
for code, name, sig in hits:
|
||||||
|
reasons = (f"dist_lo20={sig['dist_lo20']}% bias60={sig['bias60']}% "
|
||||||
|
f"rsi={sig['rsi']} ret5={sig['ret5']}% mcap_q={sig['mcap_q']} score={sig['score']}")
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO candidates (code, name, sector, reason, entry_range, stop_loss, target, created_at) "
|
||||||
|
"VALUES (?,?,?,?,?,?,?,datetime('now','localtime')) "
|
||||||
|
"ON CONFLICT(code) DO UPDATE SET "
|
||||||
|
"name=excluded.name, sector=excluded.sector, reason=excluded.reason, "
|
||||||
|
"entry_range=excluded.entry_range, stop_loss=excluded.stop_loss, target=excluded.target",
|
||||||
|
(code, name, "b_td1_v3", reasons,
|
||||||
|
f"{sig['price']*0.98:.2f}~{sig['price']:.2f}", sig["stop_loss"], sig["target"]))
|
||||||
|
inserted += 1
|
||||||
|
print(f" 🟢 {code} {name} 价{sig['price']} score={sig['score']} {reasons}", flush=True)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print(f" ✅ 新增 {inserted} 只 b_td1_v3 候选", flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""s2_panic_v2_scanner.py — S2恐慌买alpha升级 实盘扫描器(2026-08-17 择优激活落地)
|
||||||
|
|
||||||
|
由果及因(72941恐慌日信号验证):恐慌日买强势——小市值(mcap_q<0.4)+高RSI(rsi>=35)+行业抗跌(sec_ret20>=-10)
|
||||||
|
→ 胜率60.8% vs 基线30.7%;每日top8截断(信号/成交比1.8)
|
||||||
|
基于原 s2_scanner 改:原=恐慌日买超跌(bias60<-6.8+r5f<-10+dist_lo20>=10),数据证明无alpha(甚至负alpha)
|
||||||
|
→ v2 改为恐慌日买强势(alpha组合),评分同 s2_panic_v2_gen
|
||||||
|
|
||||||
|
入场:
|
||||||
|
市场门控:大盘 RSI14 < 25(极端恐慌日)
|
||||||
|
个股 alpha 组合:
|
||||||
|
mcap_q < 0.4(小市值)
|
||||||
|
rsi >= 35(相对强势)
|
||||||
|
sec_ret20 >= -10(行业抗跌)
|
||||||
|
score:mcap分(40) + rsi分(30) + sec分(20) + news分(10)
|
||||||
|
出场建议:tp30% / sl12% / max60日(s2 原出场)
|
||||||
|
输出:candidates 表(sector='s2_panic_v2')
|
||||||
|
|
||||||
|
用法:
|
||||||
|
python3 s2_panic_v2_scanner.py # 完整扫描(大盘RSI<25门控)
|
||||||
|
python3 s2_panic_v2_scanner.py --force # 忽略门控
|
||||||
|
python3 s2_panic_v2_scanner.py --top N # 输出前 N 只(默认 8)
|
||||||
|
"""
|
||||||
|
import sys, sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent))
|
||||||
|
from indicators import calc_ma, calc_rsi
|
||||||
|
from market_data import fetch_tx_klines, get_stock_pool
|
||||||
|
|
||||||
|
DB_PATH = Path("/home/hmo/MoFin/data/mofin.db")
|
||||||
|
TOP_N = 8
|
||||||
|
EXIT_CFG = {"tp_pct": 0.30, "sl_pct": 0.12, "max_hold_days": 60}
|
||||||
|
|
||||||
|
# 大盘 RSI 门控(与 s2_scanner 一致)
|
||||||
|
MKT_RSI_MAX = 25
|
||||||
|
|
||||||
|
|
||||||
|
def load_mkt_rsi():
|
||||||
|
"""大盘 RSI14(stock_daily sh000001)"""
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(str(DB_PATH), timeout=5)
|
||||||
|
rows = conn.execute(
|
||||||
|
"SELECT date, close FROM stock_daily WHERE code='sh000001' ORDER BY date DESC LIMIT 40").fetchall()
|
||||||
|
conn.close()
|
||||||
|
if len(rows) < 20:
|
||||||
|
return None, None
|
||||||
|
rows = list(reversed(rows))
|
||||||
|
closes = [r[1] for r in rows]
|
||||||
|
rsi = calc_rsi(closes)
|
||||||
|
return rows[-1][0], rsi[-1]
|
||||||
|
except Exception:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def mcap_quantile(code):
|
||||||
|
try:
|
||||||
|
conn = sqlite3.connect(str(DB_PATH), timeout=5)
|
||||||
|
row = conn.execute(
|
||||||
|
"SELECT amount FROM stock_daily WHERE code=? ORDER BY date DESC LIMIT 1", (code,)).fetchone()
|
||||||
|
if not row or not row[0]:
|
||||||
|
conn.close()
|
||||||
|
return 0.3
|
||||||
|
rows = conn.execute(
|
||||||
|
"SELECT amount FROM stock_daily WHERE date=(SELECT MAX(date) FROM stock_daily) AND amount IS NOT NULL"
|
||||||
|
).fetchall()
|
||||||
|
conn.close()
|
||||||
|
amounts = sorted([r[0] for r in rows if r[0]])
|
||||||
|
if not amounts:
|
||||||
|
return 0.3
|
||||||
|
import bisect
|
||||||
|
return round(bisect.bisect_left(amounts, row[0]) / max(len(amounts), 1), 2)
|
||||||
|
except Exception:
|
||||||
|
return 0.3
|
||||||
|
|
||||||
|
|
||||||
|
def alpha_score(mcap_q, rsi, sec_ret20, news3=0):
|
||||||
|
sc = 0
|
||||||
|
if mcap_q is not None:
|
||||||
|
sc += 40 if mcap_q < 0.2 else 32 if mcap_q < 0.4 else 24 if mcap_q < 0.6 else 16 if mcap_q < 0.8 else 8
|
||||||
|
if rsi is not None:
|
||||||
|
sc += 30 if rsi >= 45 else 22 if rsi >= 35 else 12 if rsi >= 25 else 6
|
||||||
|
if sec_ret20 is not None:
|
||||||
|
sc += 20 if sec_ret20 >= 0 else 16 if sec_ret20 >= -10 else 8 if sec_ret20 >= -20 else 3
|
||||||
|
if news3:
|
||||||
|
sc += 10 if news3 >= 2 else 7 if news3 >= 1 else 2
|
||||||
|
return sc
|
||||||
|
|
||||||
|
|
||||||
|
def check_s2v2(klines, code):
|
||||||
|
"""s2_panic_v2 筛选:恐慌日 + 强势alpha组合"""
|
||||||
|
if not klines or len(klines) < 70:
|
||||||
|
return None
|
||||||
|
closes = [k["close"] for k in klines]
|
||||||
|
i = len(klines) - 1
|
||||||
|
close = closes[i]
|
||||||
|
if close <= 0:
|
||||||
|
return None
|
||||||
|
rsi = calc_rsi(closes)
|
||||||
|
rsi_v = rsi[i] if i < len(rsi) else None
|
||||||
|
mcap_q = mcap_quantile(code)
|
||||||
|
# alpha 组合:小市值 + 强势 + (行业抗跌实盘近似简化:跳过 sec_ret20 门控)
|
||||||
|
if mcap_q >= 0.4:
|
||||||
|
return None
|
||||||
|
if rsi_v is None or rsi_v < 35:
|
||||||
|
return None
|
||||||
|
sc = alpha_score(mcap_q, rsi_v, None)
|
||||||
|
return {
|
||||||
|
"price": close, "rsi": round(rsi_v, 2), "mcap_q": mcap_q, "score": sc,
|
||||||
|
"target": round(close * (1 + EXIT_CFG["tp_pct"]), 2),
|
||||||
|
"stop_loss": round(close * (1 - EXIT_CFG["sl_pct"]), 2),
|
||||||
|
"date": klines[i]["date"],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
import argparse
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--force", action="store_true")
|
||||||
|
ap.add_argument("--top", type=int, default=TOP_N)
|
||||||
|
args = ap.parse_args()
|
||||||
|
|
||||||
|
mkt_date, mkt_rsi = load_mkt_rsi()
|
||||||
|
print(f"[s2_panic_v2] {datetime.now().strftime('%H:%M')} 扫描开始 大盘RSI={mkt_rsi}", flush=True)
|
||||||
|
if mkt_rsi is None:
|
||||||
|
print(" 大盘RSI获取失败,跳过", flush=True)
|
||||||
|
return
|
||||||
|
if not args.force and mkt_rsi >= MKT_RSI_MAX:
|
||||||
|
print(f" 大盘RSI={mkt_rsi:.0f} >= {MKT_RSI_MAX},非恐慌日,跳过", flush=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
all_stocks, existing = get_stock_pool()
|
||||||
|
print(f" 股票池 {len(all_stocks)} 只", flush=True)
|
||||||
|
hits = []
|
||||||
|
for code, name in all_stocks:
|
||||||
|
try:
|
||||||
|
klines = fetch_tx_klines(code, datalen=120)
|
||||||
|
sig = check_s2v2(klines, code)
|
||||||
|
if sig:
|
||||||
|
hits.append((code, name, sig))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
hits.sort(key=lambda x: -x[2]["score"])
|
||||||
|
hits = hits[: args.top]
|
||||||
|
|
||||||
|
conn = sqlite3.connect(str(DB_PATH), timeout=10)
|
||||||
|
inserted = 0
|
||||||
|
for code, name, sig in hits:
|
||||||
|
reasons = (f"rsi={sig['rsi']} mcap_q={sig['mcap_q']} score={sig['score']}")
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO candidates (code, name, sector, reason, entry_range, stop_loss, target, created_at) "
|
||||||
|
"VALUES (?,?,?,?,?,?,?,datetime('now','localtime')) "
|
||||||
|
"ON CONFLICT(code) DO UPDATE SET "
|
||||||
|
"name=excluded.name, sector=excluded.sector, reason=excluded.reason, "
|
||||||
|
"entry_range=excluded.entry_range, stop_loss=excluded.stop_loss, target=excluded.target",
|
||||||
|
(code, name, "s2_panic_v2", reasons,
|
||||||
|
f"{sig['price']*0.98:.2f}~{sig['price']:.2f}", sig["stop_loss"], sig["target"]))
|
||||||
|
inserted += 1
|
||||||
|
print(f" 🟢 {code} {name} 价{sig['price']} score={sig['score']} {reasons}", flush=True)
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print(f" ✅ 新增 {inserted} 只 s2_panic_v2 候选", flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -55,6 +55,8 @@ STRATEGY_SCANNER = {
|
|||||||
"v8.1": "leader_scanner.py",
|
"v8.1": "leader_scanner.py",
|
||||||
"v_next5": "leader_scanner.py",
|
"v_next5": "leader_scanner.py",
|
||||||
"v_combo": "leader_scanner.py",
|
"v_combo": "leader_scanner.py",
|
||||||
|
"b_td1_v3": "b_td1_v3_scanner.py",
|
||||||
|
"s2_panic_v2": "s2_panic_v2_scanner.py",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -824,9 +824,18 @@ def api_research_strategies():
|
|||||||
'bench_10y': _qbench_for_mkt.get('10y'), 'bench_2y': _qbench_for_mkt.get('2y'),
|
'bench_10y': _qbench_for_mkt.get('10y'), 'bench_2y': _qbench_for_mkt.get('2y'),
|
||||||
'bench_1y': _qbench_for_mkt.get('1y'),
|
'bench_1y': _qbench_for_mkt.get('1y'),
|
||||||
}
|
}
|
||||||
# ── 2026-08-16 资格判定用【策略自身适应温区 best_regime】的 qualification ──
|
# ── 2026-08-17 资格判定用【当前激活温区 weights.state】优先,best_regime 回退 ──
|
||||||
# (bug修复:原用当前市场温区,趋势市策略在当前温区(如trend_down)无资格数据→误判不合格)
|
# (2026-08-16 原用 best_regime:择优激活按当前温区选策略(如trend_down激活v_lurk_v3),
|
||||||
_qual_rg = s.get('best_regime') or _weights.get('state') or ''
|
# 但best_regime(choppy)判定会误判——v_lurk_v3在trend_down达标却被判不合格)
|
||||||
|
# 当前温区有资格数据 → 用当前温区;否则回退策略自身适应温区
|
||||||
|
_state_rg = _weights.get('state') or ''
|
||||||
|
_state_q = (s.get('qualification') or {}).get(_state_rg)
|
||||||
|
_best_rg = s.get('best_regime') or ''
|
||||||
|
_best_q = (s.get('qualification') or {}).get(_best_rg)
|
||||||
|
if _state_q and (_state_q.get('cagr_10y') is not None):
|
||||||
|
_qual_rg = _state_rg
|
||||||
|
else:
|
||||||
|
_qual_rg = _best_rg
|
||||||
_cur_regime_q = (s.get('qualification') or {}).get(_qual_rg)
|
_cur_regime_q = (s.get('qualification') or {}).get(_qual_rg)
|
||||||
_l_ok = bool(_cur_regime_q and _cur_regime_q.get('long_ok'))
|
_l_ok = bool(_cur_regime_q and _cur_regime_q.get('long_ok'))
|
||||||
_m_ok = bool(_cur_regime_q and _cur_regime_q.get('mid_ok'))
|
_m_ok = bool(_cur_regime_q and _cur_regime_q.get('mid_ok'))
|
||||||
|
|||||||
Reference in New Issue
Block a user