xiaowei-system/scripts/stock_contradiction_workflo...

234 lines
8.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
小唯股票投研 — 矛盾分析工作流
===============================
用马克思主义矛盾分析法指导每周投资复盘。
原理:
1. 实践是检验真理的唯一标准 → 模拟持仓验证判断
2. 对立统一 → 市场里的多空矛盾
3. 量变质变 → MA20金叉/死叉是质变信号
4. 否定之否定 → 每周复盘迭代策略
用法:
python3 stock_contradiction_workflow.py report # 输出本周矛盾分析
python3 stock_contradiction_workflow.py check # 检查持仓状态MA20信号
python3 stock_contradiction_workflow.py record # 记录本周分析到历史
"""
import json
import os
import sys
from datetime import datetime
# ============ 配置 ============
HISTORY_FILE = os.path.expanduser("~/.hermes/stock_backtest/contradiction_history.json")
OUTPUT_DIR = os.path.expanduser("~/.hermes/stock_backtest/")
# ============ 四维评分标准 ============
# 宏观面:-1=系统性压力 / 0=中性 / +1=友好
# 基本面:-1=差 / 0=中性 / +1=强
# 技术面:-1=下跌趋势 / 0=震荡 / +1=上升趋势
# 消息面:-1=利空 / 0=中性 / +1=利好
def load_history():
"""加载历史矛盾分析记录"""
if os.path.exists(HISTORY_FILE):
with open(HISTORY_FILE) as f:
return json.load(f)
return {"weeks": [], "positions": {}}
def save_history(history):
"""保存历史记录"""
os.makedirs(os.path.dirname(HISTORY_FILE), exist_ok=True)
with open(HISTORY_FILE, "w") as f:
json.dump(history, f, ensure_ascii=False, indent=2)
def get_ma20_signal(code):
"""
查 MA20 状态(用腾讯行情 API
返回:{"code": "000858", "name": "五粮液", "signal": "golden_cross"|"dead_cross"|"neutral", "price": xxx}
"""
import urllib.request
# 转换代码格式
if code.startswith("0") or code.startswith("3"):
market = "sz"
else:
market = "sh"
url = f"https://qt.gtimg.cn/q={market}{code}"
try:
with urllib.request.urlopen(url, timeout=5) as resp:
data = resp.read().decode("gbk")
parts = data.split("~")
if len(parts) < 33:
return None
name = parts[1]
price = float(parts[3]) # 现价
ma5 = float(parts[33]) # 5日均线
ma10 = float(parts[34]) # 10日均线
ma20 = float(parts[35]) # 20日均线
# 判断 MA20 信号
if ma5 > ma20 and price > ma20:
signal = "golden_cross" # 潜在金叉5日上穿20日
elif ma5 < ma20 and price < ma20:
signal = "dead_cross" # 死叉5日下穿20日
else:
signal = "neutral"
return {
"code": code,
"name": name,
"signal": signal,
"price": price,
"ma5": ma5,
"ma10": ma10,
"ma20": ma20
}
except Exception as e:
return {"code": code, "error": str(e)}
def analyze_week():
"""生成本周矛盾分析"""
history = load_history()
# 持仓股票
positions = history.get("positions", {
"000858": {"name": "五粮液", "status": "空仓等金叉", "avg_cost": None},
})
# 检查 MA20 信号
signals = {}
for code in ["000858", "600519", "000568", "002304", "601318", "600036"]:
result = get_ma20_signal(code)
if result:
signals[code] = result
# 矛盾分析框架
now = datetime.now()
week_num = now.isocalendar()[1]
# 核心矛盾(五粮液)
wuliangye = signals.get("000858", {})
contradiction = {
"week": f"{now.year}-W{week_num:02d}",
"date": now.strftime("%Y-%m-%d"),
"market_contradictions": [
{
"contradiction": "宏观经济下行 vs 消费刚需(白酒)",
"manifestation": "宏观面-1但五粮液基本面+1",
"current_phase": "矛盾积累阶段,等待技术面确认方向",
"action": "空仓等待,观察宏观拐点信号"
},
{
"contradiction": "估值回归 vs 趋势惯性",
"manifestation": "PE跌至历史低位但均线仍空头排列",
"current_phase": "量变质变的临界积累期",
"action": "等MA20金叉质变信号再入场"
}
],
"position_status": positions,
"ma20_signals": {code: {k: v for k, v in s.items() if k != "error"}
for code, s in signals.items()},
"weekly_verdict": {
"macro": -1, # 宏观承压
"fundamental": 1, # 基本面稳健
"technical": -1, # 技术面空头
"message": 0, # 消息面中性
"total": -1, # 综合-1
"decision": "空仓等待,继续积累数据"
}
}
return contradiction
def print_report(c):
"""输出矛盾分析报告"""
verdict = c["weekly_verdict"]
print("=" * 50)
print(f"📅 {c['date']} 矛盾分析报告")
print("=" * 50)
print("\n【四维评分】")
print(f" 宏观面: {verdict['macro']}(系统性压力)")
print(f" 基本面: {verdict['fundamental']}(稳健)")
print(f" 技术面: {verdict['technical']}(空头排列)")
print(f" 消息面: {verdict['message']}(中性)")
print(f" 综合评分: {verdict['total']} → 决策:{verdict['decision']}")
print("\n【本周核心矛盾】")
for i, m in enumerate(c["market_contradictions"], 1):
print(f"\n 矛盾{i}{m['contradiction']}")
print(f" 表现:{m['manifestation']}")
print(f" 阶段:{m['current_phase']}")
print(f" 行动:{m['action']}")
print("\n【MA20信号监控】")
if c["ma20_signals"]:
for code, sig in c["ma20_signals"].items():
if "error" in sig:
print(f" {code}: 查询失败")
else:
name = sig.get("name", code)
signal = sig.get("signal", "unknown")
emoji = {"golden_cross": "🟢金叉", "dead_cross": "🔴死叉", "neutral": "⚪中性"}.get(signal, signal)
price = sig.get("price", 0)
ma20 = sig.get("ma20", 0)
print(f" {name}{code}{emoji} 现价:{price:.2f} MA20:{ma20:.2f}")
else:
print(" 无数据")
print("\n【持仓状态】")
for code, pos in c["position_status"].items():
print(f" {pos['name']}{code}{pos['status']}")
print("\n" + "=" * 50)
print("【马克思主义分析框架】")
print(f" 本质:实践是检验真理的唯一标准")
print(f" 当前实践模拟持仓等MA20金叉验证判断")
print(f" 矛盾积累:宏观-1+基本面+1等待技术面确认方向")
print(f" 量变质变MA20金叉=质变临界点")
print("=" * 50)
def main():
cmd = sys.argv[1] if len(sys.argv) > 1 else "report"
if cmd == "report":
c = analyze_week()
print_report(c)
# 保存到历史
history = load_history()
history["weeks"].append(c)
if len(history["weeks"]) > 52: # 保留一年
history["weeks"] = history["weeks"][-52:]
save_history(history)
elif cmd == "check":
# 快速检查 MA20 信号
for code in ["000858", "600519", "000568", "002304"]:
sig = get_ma20_signal(code)
if sig and "error" not in sig:
emoji = {"golden_cross": "🟢", "dead_cross": "🔴", "neutral": ""}.get(sig["signal"], "")
print(f"{emoji} {sig['name']} | 现价:{sig['price']:.2f} MA20:{sig['ma20']:.2f}")
elif sig:
print(f"{code}: {sig.get('error', 'unknown')}")
elif cmd == "record":
c = analyze_week()
history = load_history()
if c not in history["weeks"]:
history["weeks"].append(c)
save_history(history)
print(f"✅ 已记录 {c['week']} 的矛盾分析")
else:
print(f"⚠️ {c['week']} 已记录,跳过")
if __name__ == "__main__":
main()