diff --git a/multi_timeframe.py b/multi_timeframe.py index 43c9b799..b3607f93 100644 --- a/multi_timeframe.py +++ b/multi_timeframe.py @@ -223,8 +223,18 @@ def calc_moving_averages(klines: list, windows: list = [5, 10, 20, 60]) -> dict: # 确保按时间正序(旧的在前) closes = [k["close"] for k in klines] - # 检查是否倒序(最新的在前) - if len(closes) >= 2 and closes[0] > closes[-1]: + # 使用日期判断顺序(不能用价格:下跌趋势下closes[0]>closes[-1]也会触发反转) + is_reversed = False + if len(klines) >= 2: + d0 = klines[0].get("date", "") + d1 = klines[-1].get("date", "") + if d0 and d1: + from datetime import datetime + try: + is_reversed = datetime.strptime(d0, "%Y-%m-%d") > datetime.strptime(d1, "%Y-%m-%d") + except: + is_reversed = (closes[0] > closes[-1] * 1.5) if len(closes) >= 2 else False + if is_reversed: closes = list(reversed(closes)) result = {} @@ -314,8 +324,18 @@ def assess_trend(klines: list) -> dict: return {"trend": "unknown", "strength": 0, "description": "数据不足"} closes = [k["close"] for k in klines] - # 确保正序 - if len(closes) >= 2 and closes[0] > closes[-1]: + # 确保正序(使用日期不用价格,避免下跌趋势中错误反转) + is_reversed = False + if len(klines) >= 2: + d0 = klines[0].get("date", "") + d1 = klines[-1].get("date", "") + if d0 and d1: + from datetime import datetime + try: + is_reversed = datetime.strptime(d0, "%Y-%m-%d") > datetime.strptime(d1, "%Y-%m-%d") + except: + is_reversed = (closes[0] > closes[-1] * 1.5) if len(closes) >= 2 else False + if is_reversed: closes = list(reversed(closes)) n = len(closes) diff --git a/scripts/multi_timeframe.py b/scripts/multi_timeframe.py index 514bc1fa..b3607f93 100644 --- a/scripts/multi_timeframe.py +++ b/scripts/multi_timeframe.py @@ -17,7 +17,9 @@ import urllib.error from datetime import datetime, date, timedelta from typing import Optional -DATA_DIR = "/home/hmo/MoFin/data" +from mofin_db import get_conn + +DATA_DIR = "/home/hmo/web-dashboard/data" HISTORY_PATH = os.path.join(DATA_DIR, "price_history.json") # multi_tf_cache.json 已迁移到 DB (mtf_cache 表) @@ -91,8 +93,7 @@ def _load_mtf_cache(): if _MTF_CACHE_DATA is not None: return _MTF_CACHE_DATA try: - import sqlite3 - db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db') + db = get_conn() rows = db.execute("SELECT code, cache_json FROM mtf_cache").fetchall() _MTF_CACHE_DATA = {} for code, json_str in rows: @@ -112,8 +113,7 @@ def _save_mtf_cache(): if _MTF_CACHE_DATA is None: return try: - import sqlite3 - db = sqlite3.connect('/home/hmo/MoFin/data/mofin.db') + db = get_conn() for code, data in _MTF_CACHE_DATA.items(): db.execute( "INSERT OR REPLACE INTO mtf_cache (code, cache_json, updated_at) VALUES (?,?,datetime('now','localtime'))", @@ -222,8 +222,8 @@ def calc_moving_averages(klines: list, windows: list = [5, 10, 20, 60]) -> dict: return {f"ma{w}": None for w in windows} # 确保按时间正序(旧的在前) - # 使用日期判断顺序(不能用价格:下跌趋势下closes[0]>closes[-1]也会触发反转) closes = [k["close"] for k in klines] + # 使用日期判断顺序(不能用价格:下跌趋势下closes[0]>closes[-1]也会触发反转) is_reversed = False if len(klines) >= 2: d0 = klines[0].get("date", "") @@ -233,9 +233,7 @@ def calc_moving_averages(klines: list, windows: list = [5, 10, 20, 60]) -> dict: try: is_reversed = datetime.strptime(d0, "%Y-%m-%d") > datetime.strptime(d1, "%Y-%m-%d") except: - # 日期格式不对时的fallback: 仅当首价显著高于末价才判定倒序(50%阈值) is_reversed = (closes[0] > closes[-1] * 1.5) if len(closes) >= 2 else False - if is_reversed: closes = list(reversed(closes)) @@ -326,8 +324,18 @@ def assess_trend(klines: list) -> dict: return {"trend": "unknown", "strength": 0, "description": "数据不足"} closes = [k["close"] for k in klines] - # 确保正序 - if len(closes) >= 2 and closes[0] > closes[-1]: + # 确保正序(使用日期不用价格,避免下跌趋势中错误反转) + is_reversed = False + if len(klines) >= 2: + d0 = klines[0].get("date", "") + d1 = klines[-1].get("date", "") + if d0 and d1: + from datetime import datetime + try: + is_reversed = datetime.strptime(d0, "%Y-%m-%d") > datetime.strptime(d1, "%Y-%m-%d") + except: + is_reversed = (closes[0] > closes[-1] * 1.5) if len(closes) >= 2 else False + if is_reversed: closes = list(reversed(closes)) n = len(closes) diff --git a/technical_analysis.py b/technical_analysis.py index 27426959..5a6ddcae 100644 --- a/technical_analysis.py +++ b/technical_analysis.py @@ -55,14 +55,7 @@ def _market_prefix(code): def get_quote(code): - """获取行情数据。DB 优先(price_monitor 维护),腾讯 API fallback""" - # DB 优先 - try: - from mofin_db import get_price_from_db - p, chg = get_price_from_db(code) - if p: return {"code": code, "price": p, "change_pct": chg or 0} - except: pass - # Fallback: 腾讯 API + """获取行情数据。先拿DB的价格和涨跌幅,再调腾讯API拿HLC全量数据""" import time _cache = get_quote.__dict__.get("_cache", {}) now = time.time() @@ -70,6 +63,18 @@ def get_quote(code): if cached and (now - cached["ts"]) < 60: return cached["data"] + # 先从DB拿基础价格(快速,不阻塞) + db_price = None + db_chg = None + try: + from mofin_db import get_price_from_db + p, chg = get_price_from_db(code) + if p: + db_price, db_chg = p, chg + except: + pass + + # 腾讯API获取全量HLC数据 raw = str(code).split("_")[0] prefix = _market_prefix(code) url = f"http://qt.gtimg.cn/q={prefix}{raw}" @@ -77,6 +82,8 @@ def get_quote(code): r = urllib.request.urlopen(url, timeout=5) fields = r.read().decode("gbk").split('"')[1].split("~") except Exception as e: + if db_price: + return {"code": code, "price": db_price, "change_pct": db_chg or 0} return {"code": code, "error": str(e)} def get(i): @@ -465,7 +472,7 @@ def analyze_volume_deep(code): import sqlite3 from pathlib import Path - DATA_DIR = Path(__file__).parent / "data" + DATA_DIR = Path(__file__).parent.parent / "data" try: conn = sqlite3.connect(str(DATA_DIR / "mofin.db")) row = conn.execute("SELECT cache_json FROM mtf_cache WHERE code=?", (code,)).fetchone()