修复web前端,增加openai

This commit is contained in:
dekun
2026-05-23 11:53:36 +08:00
parent 4439bedcb7
commit ada9478713
20 changed files with 335 additions and 355 deletions
+24 -78
View File
@@ -34,6 +34,7 @@ import sys
if _REPO_ROOT not in sys.path:
sys.path.insert(0, _REPO_ROOT)
from ai_client import ai_generate, ai_review, ai_short_advice
from hub_auth import request_allowed as hub_request_allowed
from history_window_lib import (
PRESET_CUSTOM,
@@ -196,8 +197,6 @@ BREAKEVEN_RR_TRIGGER = float(os.getenv("BREAKEVEN_RR_TRIGGER", "1.0"))
BREAKEVEN_OFFSET_PCT = float(os.getenv("BREAKEVEN_OFFSET_PCT", "0.02"))
BREAKEVEN_STEP_R = float(os.getenv("BREAKEVEN_STEP_R", "1.0"))
DEFAULT_TRADE_STYLE = (os.getenv("DEFAULT_TRADE_STYLE", "trend") or "trend").strip().lower()
OLLAMA_API = os.getenv("OLLAMA_API", "http://127.0.0.1:11434/api/generate")
AI_MODEL = os.getenv("AI_MODEL", "huihui_ai/deepseek-r1-abliterated:latest")
GATE_SOCKS_PROXY = (os.getenv("GATE_SOCKS_PROXY") or "").strip()
GATE_HTTP_PROXY = (os.getenv("GATE_HTTP_PROXY") or "").strip()
@@ -516,61 +515,6 @@ def _journal_row_lines_for_ai(idx, row, *, include_hold_duration=True):
return "\n".join(lines) + "\n"
def ai_review(trades_text, period_title, image_paths=None):
prompt = f"""
你是一位专业交易教练下面是用户的{period_title}交易记录请做简洁可执行的复盘中文
硬性规则 必须遵守
- 你只能根据交易记录**明确出现的字段**陈述事实禁止编造是否触发止损是否扛单亏损是否扩大图上具体结构/进出场点位等记录里**没有**的信息
- 平仓/离场只是交易员自述摘要不是客观成交明细若记录未写明代币是否打到止损价是否软件平仓等不要断言执行路径可用在记录有限前提下一种可能是或简短写执行路径记录不足无法判断
- 提前离场类结论必须优先依据记录中的提前离场记录字段若该段全为或未出现有效内容不得写道明显扛单拒不止损未执行硬止损
- 实际RR为负只说明结果相对于预期RR不利不等同于风控失灵止损纪律崩溃除非记录里另有依据
- 禁止用语人身攻击夸张定性致命伤灾难语气克制对事不对人
- 若有截图且你能辨认再结合图讨论看不清或无明确定位则明确说无法从图确认不得虚构 K 线故事
输出结构
1. 总体盈亏结构紧扣笔数盈亏数字与 RR少形容词
2. 心态与执行每笔 110 + 一句依据依据必须对应记录字段
3. 行为标签提前离场 / 乱开仓 / 扛单等仅在有字段或自述支撑时点名否则写记录未勾选或未描述不作强加
4. 改进建议最多 3 每条具体可执行
5. 图表若有且可读结合价格行为简述否则一两句说明无法看图分析
交易记录
{trades_text}
""".strip()
payload = {"model": AI_MODEL, "prompt": prompt, "stream": False, "options": {"temperature": 0.2}}
images = []
for p in image_paths or []:
b64 = _read_image_base64(p)
if b64:
images.append(b64)
if images:
payload["images"] = images
try:
r = requests.post(OLLAMA_API, json=payload, timeout=AI_TIMEOUT_SECONDS)
return r.json().get("response", "AI 生成失败")
except Exception as e:
return f"AI 调用失败:{str(e)}"
def ai_short_advice(prompt_text):
prompt = f"""
你是交易风控助理请用中文给出**最多 3 **提醒要求
- 每条不超过 25 个字
- 语气克制具体可执行
- 不要输出 Markdown不要编号前缀以外的废话
场景
{prompt_text}
""".strip()
payload = {"model": AI_MODEL, "prompt": prompt, "stream": False, "options": {"temperature": 0.2}}
try:
r = requests.post(OLLAMA_API, json=payload, timeout=AI_TIMEOUT_SECONDS)
return (r.json().get("response") or "").strip()
except Exception:
return ""
def _load_font(size):
if not ImageFont:
return None
@@ -1072,16 +1016,10 @@ JSON 字段:
"note": ""
}
""".strip()
payload = {
"model": AI_MODEL,
"prompt": prompt,
"images": [image_b64],
"stream": False,
"options": {"temperature": 0.1},
}
try:
r = requests.post(OLLAMA_API, json=payload, timeout=AI_TIMEOUT_SECONDS)
raw = r.json().get("response", "")
raw = ai_generate(prompt, images_b64=[image_b64], temperature=0.1)
if raw.startswith("AI 调用失败"):
return {}
data = _extract_json_object(raw) or {}
if not isinstance(data, dict):
data = {}
@@ -5291,8 +5229,9 @@ def render_main_page(page="trade"):
preview_expires_ms = None
trend_preview_expired = False
trend_preview_id_arg = ""
if page == "strategy_trend":
if page in ("strategy", "strategy_trend", "strategy_roll"):
_trend_cleanup_stale_previews(conn)
if page in ("strategy", "strategy_trend"):
trend_preview_id_arg = (request.args.get("preview_id") or "").strip()
if trend_preview_id_arg:
pr = conn.execute(
@@ -5313,7 +5252,7 @@ def render_main_page(page="trade"):
elif pr:
trend_preview_expired = True
strategy_extra = {}
if page == "strategy_roll":
if page in ("strategy", "strategy_trend", "strategy_roll"):
from strategy_ui import fetch_roll_page_data
strategy_extra = fetch_roll_page_data(
@@ -6205,17 +6144,17 @@ def preview_trend_pullback():
if not okp:
conn.close()
flash(reasonp)
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
ok_live, reason_live = ensure_exchange_live_ready()
if not ok_live:
conn.close()
flash(reason_live)
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
payload, err = parse_and_compute_trend_pullback_plan(request.form)
if err:
conn.close()
flash(err)
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
pid = str(uuid.uuid4())
exp_ms = int(time.time() * 1000) + int(TREND_PULLBACK_PREVIEW_TTL_SECONDS) * 1000
created = app_now_str()
@@ -6263,7 +6202,7 @@ def execute_trend_pullback():
pid = (request.form.get("preview_id") or "").strip()
if not pid:
flash("缺少预览 ID")
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
conn = get_db()
_trend_cleanup_stale_previews(conn)
pr = conn.execute("SELECT * FROM trend_pullback_previews WHERE id=?", (pid,)).fetchone()
@@ -6271,7 +6210,7 @@ def execute_trend_pullback():
if not pr or int(pr["expires_at_ms"] or 0) < now_ms:
conn.close()
flash("预览已过期或不存在,请重新生成预览")
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
okp, reasonp = precheck_trend_pullback_start(conn)
if not okp:
conn.close()
@@ -6294,7 +6233,7 @@ def execute_trend_pullback():
flash(
f"当前可用余额与预览快照偏差 {drift_pct:.2f}%,超过允许 {TREND_PREVIEW_MAX_BALANCE_DRIFT_PCT}% ,请重新生成预览"
)
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
symbol = pr["symbol"]
exchange_symbol = pr["exchange_symbol"]
direction = pr["direction"] or "long"
@@ -6411,7 +6350,7 @@ def trend_pullback_breakeven(pid):
raise ValueError
except ValueError:
flash("保本偏移% 格式无效")
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
conn = get_db()
row = conn.execute(
"SELECT * FROM trend_pullback_plans WHERE id=? AND status='active'", (pid,)
@@ -6419,7 +6358,7 @@ def trend_pullback_breakeven(pid):
if not row:
conn.close()
flash("未找到运行中的趋势回调计划")
return redirect(url_for("strategy_trend_page"))
return redirect(url_for("strategy_trading_page"))
ok, err = apply_trend_pullback_manual_breakeven(conn, row, offset_pct=offset_pct)
conn.commit()
conn.close()
@@ -7381,16 +7320,23 @@ except Exception as _hub_err:
print(f"[hub_bridge] gate_bot: {_hub_err}")
@app.route("/strategy")
@login_required
def strategy_trading_page():
return render_main_page("strategy")
@app.route("/strategy/trend")
@login_required
def strategy_trend_page():
return render_main_page("strategy_trend")
qs = request.query_string.decode()
return redirect(f"/strategy?{qs}" if qs else "/strategy")
@app.route("/strategy/roll")
@login_required
def strategy_roll_page():
return render_main_page("strategy_roll")
return redirect("/strategy")
from strategy_register import install_strategy_trading