529 lines
20 KiB
Python
529 lines
20 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
小唯四维选股系统 — 宏观+基本面+技术+消息
|
||
========================================
|
||
综合四个维度筛选股票
|
||
|
||
维度说明:
|
||
1. 宏观面 — 美联储政策/人民币汇率/地缘风险
|
||
2. 基本面 — PE/PB/业绩增速/行业景气度
|
||
3. 技术面 — MA20趋势/成交量/均线排列
|
||
4. 消息面 — 政策利好/行业新闻/重大事件
|
||
|
||
评分规则:
|
||
- 宏观负面(-1)/中性(0)/正面(+1)
|
||
- 基本面低估(+1)/合理(0)/高估(-1)
|
||
- 技术面多头(+1)/空头(-1)/震荡(0)
|
||
- 消息面利好(+1)/利空(-1)/中性(0)
|
||
- 综合评分: 4分以上关注, 6分以上重点
|
||
|
||
用法:
|
||
python3 stock_selector.py scan # 扫描全市场
|
||
python3 stock_selector.py analyze <代码> # 分析单只股票
|
||
python3 stock_selector.py watchlist # 展示关注列表
|
||
"""
|
||
|
||
import json, sys, urllib.request, time
|
||
from datetime import datetime
|
||
from pathlib import Path
|
||
|
||
import numpy as np
|
||
import pandas as pd
|
||
|
||
OUTPUT = Path.home() / ".hermes" / "stock_backtest"
|
||
OUTPUT.mkdir(exist_ok=True)
|
||
FEISHU_WEBHOOK = "https://open.feishu.cn/open-apis/bot/v2/hook/446db983-e392-4d2c-bfb8-f9060e5df3ad"
|
||
|
||
# ===================== 飞书推送 =====================
|
||
|
||
def send_feishu(msg):
|
||
payload = json.dumps({"msg_type": "text", "content": {"text": msg}}).encode()
|
||
req = urllib.request.Request(FEISHU_WEBHOOK, data=payload,
|
||
headers={"Content-Type": "application/json"})
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=10):
|
||
pass
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
# ===================== 宏观面 — 汇率/利率/风险 =====================
|
||
|
||
def get_macro_score():
|
||
"""
|
||
宏观面评分:
|
||
考虑: 人民币汇率, 美联储政策, 地缘风险
|
||
返回: -1(负面) / 0(中性) / +1(正面)
|
||
"""
|
||
score = 0
|
||
reasons = []
|
||
|
||
# 人民币汇率
|
||
try:
|
||
url = "https://qt.gtimg.cn/q=usdcnh"
|
||
text = urllib.request.urlopen(url, timeout=5).read().decode("gbk")
|
||
parts = text.split("~")
|
||
if len(parts) > 10:
|
||
usdcnh = float(parts[3])
|
||
if usdcnh > 7.3:
|
||
score -= 1
|
||
reasons.append(f"离岸人民币破7.3({usdcnh}) → 外资流出压力 ⬇️")
|
||
else:
|
||
score += 1
|
||
reasons.append(f"人民币相对稳定({usdcnh}) → 外资流出压力小 ⬆️")
|
||
except Exception:
|
||
reasons.append("人民币汇率获取失败")
|
||
|
||
# A股风险偏好(用沪深300判断)
|
||
try:
|
||
url = "https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq¶m=sh510300,day,2026-07-01,2026-07-12,10,qfq"
|
||
text = urllib.request.urlopen(url, timeout=5).read().decode("utf-8")
|
||
data = json.loads(text.replace("kline_dayqfq=", "", 1))
|
||
rows = data.get("data", {}).get("sh510300", {}).get("qfqday") or []
|
||
if len(rows) >= 5:
|
||
closes = [float(r[2]) for r in rows]
|
||
if closes[-1] > closes[0]:
|
||
score += 1
|
||
reasons.append("沪深300近期上涨 → 市场风险偏好上升 ⬆️")
|
||
else:
|
||
score -= 1
|
||
reasons.append("沪深300近期下跌 → 市场风险偏好下降 ⬇️")
|
||
except Exception:
|
||
pass
|
||
|
||
# 平均
|
||
avg_score = max(-1, min(1, score // max(1, len([r for r in reasons if r]))))
|
||
return avg_score, reasons
|
||
|
||
|
||
# ===================== 基本面 — PE/PB/业绩 =====================
|
||
|
||
def get_fundamental_score(code):
|
||
"""
|
||
基本面评分:
|
||
基于估值(PE/PB)和近期趋势
|
||
返回: -1(高估) / 0(合理) / +1(低估)
|
||
"""
|
||
try:
|
||
mc = f"sh{code}" if code.startswith("6") else f"sz{code}"
|
||
url = f"https://qt.gtimg.cn/q={mc}"
|
||
text = urllib.request.urlopen(url, timeout=5).read().decode("gbk")
|
||
parts = text.split("~")
|
||
if len(parts) < 40:
|
||
return 0, [], {}
|
||
|
||
pe = float(parts[39]) if parts[39] else 0
|
||
pb = float(parts[46]) if parts[46] else 0
|
||
price = float(parts[3])
|
||
yclose = float(parts[4])
|
||
|
||
score = 0
|
||
reasons = []
|
||
|
||
# PE判断
|
||
if 0 < pe < 15:
|
||
score += 1
|
||
reasons.append(f"PE={pe:.1f}(历史低位) → 估值有支撑 ⬆️")
|
||
elif 15 <= pe <= 30:
|
||
reasons.append(f"PE={pe:.1f}(合理区间) → 无明显低估/高估")
|
||
elif pe > 30:
|
||
score -= 1
|
||
reasons.append(f"PE={pe:.1f}(历史高位) → 估值偏高风险 ⬇️")
|
||
elif pe <= 0:
|
||
reasons.append(f"PE={pe}(亏损/无效) → 无法判断")
|
||
|
||
# PB判断
|
||
if 0 < pb < 3:
|
||
score += 1
|
||
reasons.append(f"PB={pb:.1f}(相对低估) → 净资产有支撑 ⬆️")
|
||
elif pb >= 3:
|
||
reasons.append(f"PB={pb:.1f}(相对高估)")
|
||
|
||
# 近期价格位置
|
||
try:
|
||
url2 = f"https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq¶m={mc},day,2026-01-01,2026-07-12,200,qfq"
|
||
text2 = urllib.request.urlopen(url2, timeout=5).read().decode("utf-8")
|
||
data2 = json.loads(text2.replace("kline_dayqfq=", "", 1))
|
||
hist = data2.get("data", {}).get(mc, {}).get("qfqday") or []
|
||
if len(hist) >= 60:
|
||
highs = [float(r[3]) for r in hist]
|
||
low = min(highs)
|
||
high = max(highs)
|
||
pos = (price - low) / (high - low) if high > low else 0.5
|
||
if pos < 0.2:
|
||
score += 1
|
||
reasons.append(f"价格处于近半年低位({pos*100:.0f}%) → 相对安全边际 ⬆️")
|
||
elif pos > 0.8:
|
||
score -= 1
|
||
reasons.append(f"价格处于近半年高位({pos*100:.0f}%) → 追高风险 ⬇️")
|
||
except Exception:
|
||
pass
|
||
|
||
avg_score = max(-1, min(1, score))
|
||
info = {"pe": pe, "pb": pb, "price": price, "yclose": yclose}
|
||
return avg_score, reasons, info
|
||
except Exception as e:
|
||
return 0, [f"基本面数据获取失败({e})"], {}
|
||
|
||
|
||
# ===================== 技术面 — MA20趋势 =====================
|
||
|
||
def get_technical_score(code):
|
||
"""
|
||
技术面评分:
|
||
基于MA20均线状态
|
||
返回: -1(空头) / 0(震荡) / +1(多头)
|
||
"""
|
||
try:
|
||
mc = f"sh{code}" if code.startswith("6") else f"sz{code}"
|
||
end = datetime.now().strftime("%Y-%m-%d")
|
||
url = f"https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?_var=kline_dayqfq¶m={mc},day,2025-07-01,{end},500,qfq"
|
||
text = urllib.request.urlopen(url, timeout=8).read().decode("utf-8")
|
||
data = json.loads(text.replace("kline_dayqfq=", "", 1))
|
||
rows = data.get("data", {}).get(mc, {}).get("qfqday") or []
|
||
if len(rows) < 30:
|
||
return 0, ["数据不足"], {}
|
||
|
||
closes = [float(r[2]) for r in rows]
|
||
dates = [r[0] for r in rows]
|
||
closes_s = pd.Series(closes)
|
||
|
||
ma20 = closes_s.rolling(20).mean()
|
||
ma60 = closes_s.rolling(60).mean()
|
||
ma120 = closes_s.rolling(120).mean()
|
||
|
||
last_close = closes[-1]
|
||
last_ma20 = ma20.iloc[-1]
|
||
last_ma60 = ma60.iloc[-1]
|
||
last_ma120 = ma120.iloc[-1]
|
||
|
||
# 均线排列
|
||
if last_ma20 > last_ma60 > last_ma120:
|
||
ma_arrangement = "多头排列"
|
||
t_score = 1
|
||
elif last_ma20 < last_ma60 < last_ma120:
|
||
ma_arrangement = "空头排列"
|
||
t_score = -1
|
||
else:
|
||
ma_arrangement = "均线混乱"
|
||
t_score = 0
|
||
|
||
# 价格与MA20关系
|
||
pct_above = (last_close - last_ma20) / last_ma20 * 100
|
||
|
||
# 近期趋势
|
||
trend_20d = (closes[-1] - closes[-20]) / closes[-20] * 100 if len(closes) >= 20 else 0
|
||
|
||
reasons = [
|
||
f"{ma_arrangement}(价格{'' if last_close>last_ma20 else ''}{pct_above:+.1f}%vsMA20)",
|
||
f"近20日涨跌: {trend_20d:+.1f}%",
|
||
]
|
||
|
||
info = {
|
||
"price": last_close,
|
||
"ma20": last_ma20,
|
||
"ma60": last_ma60,
|
||
"ma120": last_ma120,
|
||
"ma_arrangement": ma_arrangement,
|
||
"pct_above_ma20": pct_above,
|
||
"trend_20d": trend_20d,
|
||
}
|
||
return t_score, reasons, info
|
||
except Exception as e:
|
||
return 0, [f"技术面数据获取失败({e})"], {}
|
||
|
||
|
||
# ===================== 消息面 — 行业/政策 =====================
|
||
|
||
def get_stock_news(code, name, max_news=3):
|
||
"""获取个股最新新闻(东方财富)"""
|
||
try:
|
||
url = f"https://np-anotice-stock.eastmoney.com/api/security/ann?sr=-1&page_size={max_news}&page_index=1&ann_type=SZA&stock_list={code}"
|
||
import subprocess, shlex, os, json
|
||
env = dict(os.environ)
|
||
for k in ["http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"]:
|
||
env.pop(k, None)
|
||
cmd = f"curl -s --max-time 6 --compressed {shlex.quote(url)}"
|
||
r = subprocess.run(cmd, shell=True, capture_output=True, timeout=8, env=env)
|
||
data = json.loads(r.stdout.decode("utf-8", errors="ignore"))
|
||
notices = data.get("data", {}).get("list", [])
|
||
news = []
|
||
for n in notices[:max_news]:
|
||
title = n.get("title", "")[:50]
|
||
notice_date = n.get("notice_date", "")[:10]
|
||
if title:
|
||
news.append(f"· {notice_date} {title}")
|
||
return news if news else [f"近{max_news}日无重大公告"]
|
||
except Exception:
|
||
return ["新闻获取失败"]
|
||
|
||
|
||
def get_sector_news(industry, max_news=2):
|
||
"""获取行业相关新闻 — 回退到指数情绪代理(行业API不可用时)"""
|
||
try:
|
||
# 先尝试东财快讯
|
||
keyword_map = {"白酒": "白酒", "银行": "银行", "保险": "保险", "新能源": "新能源"}
|
||
keyword = keyword_map.get(industry, industry) or industry or ""
|
||
base = "https://search-api-web.eastmoney.com/search/jsonp"
|
||
param = ('{"uid":"","keyword":"' + keyword + '","type":["cmsArticleListNew"],'
|
||
'"client":"web","clientVersion":"curr","clientType":"web",'
|
||
'"param":{"cmsArticleListNew":{"pageIndex":1,"pageSize":' + str(max_news) + '}}}')
|
||
url = base + "?param=" + param
|
||
env = dict(os.environ)
|
||
for k in ["http_proxy", "https_proxy", "HTTP_PROXY", "HTTPS_PROXY"]:
|
||
env.pop(k, None)
|
||
cmd = ["curl", "-s", "--max-time", "6",
|
||
"-H", "Referer: https://so.eastmoney.com/",
|
||
"-H", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 Chrome/120 Safari/537.36",
|
||
url]
|
||
r = subprocess.run(cmd, capture_output=True, timeout=8, env=env)
|
||
raw = r.stdout.decode("utf-8", errors="ignore")
|
||
m = re.search(r'\(\[.*\]\)', raw, re.DOTALL)
|
||
if m:
|
||
items = json.loads(m.group(1))
|
||
news = [f"· {it.get('title','')[:40]}" for it in items[:max_news] if it.get('title')]
|
||
if news:
|
||
return news
|
||
except Exception:
|
||
pass
|
||
# 回退:使用指数涨跌作为行业情绪代理
|
||
try:
|
||
sector_codes = {"白酒": "sh000858", "银行": "sh000001", "保险": "sh601318", "新能源": "sz399808"}
|
||
code = sector_codes.get(industry, "sh000001")
|
||
mc = code[:2] + code[2:]
|
||
url = f"https://qt.gtimg.cn/q={mc}"
|
||
text = get_url_gbk(url)
|
||
parts = text.split("~")
|
||
if len(parts) > 5:
|
||
p = float(parts[3])
|
||
y = float(parts[4])
|
||
chg = (p-y)/y*100
|
||
sentiment = "📈 行业指数上涨" if chg > 0 else "📉 行业指数下跌"
|
||
return [f"· {sentiment} ({chg:+.2f}%) — {parts[1]}"]
|
||
except Exception:
|
||
pass
|
||
return ["· 行业新闻获取失败(回退到静态规则)"]
|
||
|
||
|
||
def get_sentiment_score_from_news(code, name="", industry=""):
|
||
"""消息面评分 — 基于真实新闻 + 静态规则回退"""
|
||
import time
|
||
news = get_stock_news(code, name)
|
||
sector_news = get_sector_news(industry) if industry else []
|
||
score = 0
|
||
reasons = []
|
||
|
||
# 真实新闻分析
|
||
all_news = news + sector_news
|
||
for item in all_news:
|
||
text = item.lower()
|
||
if any(k in text for k in ["违规", "调查", "处罚", "减持", "业绩预亏", "暴雷"]):
|
||
score -= 1
|
||
reasons.append(f"利空公告: {item[:30]}")
|
||
elif any(k in text for k in ["回购", "增持", "业绩预增", "中标", "合作", "突破"]):
|
||
score += 1
|
||
reasons.append(f"利好公告: {item[:30]}")
|
||
|
||
# 静态规则回退(无新闻时)
|
||
if not reasons:
|
||
if name in ["五粮液", "贵州茅台", "泸州老窖", "洋河股份"]:
|
||
score -= 1; reasons.append("白酒消费降级压制 ⬇️")
|
||
score += 1; reasons.append("估值低位有支撑 ⬆️")
|
||
elif name in ["平安银行", "招商银行"]:
|
||
score += 1; reasons.append("高股息防御 ⬆️")
|
||
score += 1; reasons.append("低估值稳健 ⬆️")
|
||
elif "宁德" in name:
|
||
score -= 1; reasons.append("新能源产能过剩担忧 ⬇️")
|
||
score += 1; reasons.append("行业龙头壁垒 ⬆️")
|
||
else:
|
||
reasons.append("消息面无明显驱动")
|
||
|
||
avg_score = max(-1, min(1, score))
|
||
return avg_score, reasons, news, sector_news
|
||
|
||
|
||
# ===================== 综合评分 =====================
|
||
|
||
def analyze_stock(code, name=""):
|
||
"""对单只股票进行四维评分"""
|
||
print(f"\n{'='*60}")
|
||
print(f"四维选股分析: {name}({code})")
|
||
print(f"{'='*60}")
|
||
|
||
# 1. 宏观面
|
||
m_score, m_reasons = get_macro_score()
|
||
print(f"[宏观面] 评分: {m_score:+d}")
|
||
for r in m_reasons:
|
||
print(f" - {r}")
|
||
|
||
# 2. 基本面
|
||
f_score, f_reasons, f_info = get_fundamental_score(code)
|
||
print(f"[基本面] 评分: {f_score:+d}")
|
||
for r in f_reasons:
|
||
print(f" - {r}")
|
||
|
||
# 3. 技术面
|
||
t_score, t_reasons, t_info = get_technical_score(code)
|
||
print(f"[技术面] 评分: {t_score:+d}")
|
||
for r in t_reasons:
|
||
print(f" - {r}")
|
||
|
||
# 4. 消息面
|
||
industry_map = {"000858": "白酒", "600519": "白酒", "000568": "白酒", "002304": "白酒",
|
||
"600036": "银行", "601318": "保险", "000001": "银行", "300750": "新能源", "510300": ""}
|
||
industry = industry_map.get(code, "")
|
||
s_score, s_reasons, stock_news, sector_news = get_sentiment_score_from_news(code, name, industry)
|
||
print(f"[消息面] 评分: {s_score:+d}")
|
||
for r in s_reasons:
|
||
print(f" - {r}")
|
||
if stock_news:
|
||
for n in stock_news[:2]:
|
||
print(f" 📰 {n}")
|
||
if sector_news:
|
||
for n in sector_news[:1]:
|
||
print(f" 📢 {n}")
|
||
|
||
# ===========================================================
|
||
# 组合评分逻辑(修正简单相加的缺陷)
|
||
# ===========================================================
|
||
# 规则1: 技术面空头 + 基本面低估 = 买入机会(逆向)
|
||
# 规则2: 技术面空头 + 基本面高估 = 危险加倍(双杀)
|
||
# 规则3: 宏观负面时,技术面多头无法持续
|
||
# 规则4: 综合评分考虑方向匹配
|
||
|
||
f_strong = f_score >= 1 # 基本面好(低估)
|
||
f_weak = f_score <= -1 # 基本面差(高估)
|
||
t_bear = t_score <= -1 # 技术面空头(超跌)
|
||
t_bull = t_score >= 1 # 技术面多头
|
||
m_bad = m_score <= -1 # 宏观差
|
||
|
||
# 逆向机会: 技术面空头+基本面好 = 低估买入机会
|
||
if t_bear and f_strong:
|
||
adjusted_total = 2 # 视为关注机会
|
||
combo_reason = "逆向机会: 技术超跌+基本面低估"
|
||
# 双杀: 技术差+基本面也差
|
||
elif t_bear and f_weak:
|
||
adjusted_total = -2
|
||
combo_reason = "双杀风险: 技术超跌+基本面高估"
|
||
# 技术多头+基本面好 + 宏观不差
|
||
elif t_bull and f_strong and not m_bad:
|
||
adjusted_total = 3
|
||
combo_reason = "共振: 技术+基本面+宏观同向"
|
||
# 强势股(宏观好时)
|
||
elif t_bull and m_score >= 0:
|
||
adjusted_total = 2
|
||
combo_reason = "技术多头,宏观中性支撑"
|
||
# 常规计算
|
||
else:
|
||
adjusted_total = m_score + f_score + t_score + s_score
|
||
combo_reason = "常规评分"
|
||
|
||
total = adjusted_total
|
||
max_score = 4
|
||
|
||
print(f"\n组合逻辑: {combo_reason}")
|
||
print(f"\n{'='*60}")
|
||
print(f"综合评分: {total:+d} / {max_score} ({(total/max_score*100):.0f}%)")
|
||
if total >= 6:
|
||
verdict = "⭐⭐⭐ 重点关注 — 四维共振,强烈看多"
|
||
elif total >= 4:
|
||
verdict = "⭐⭐ 关注 — 多维度偏多"
|
||
elif total >= 2:
|
||
verdict = "⭐ 关注 — 逆向机会或温和看多"
|
||
elif total >= 0:
|
||
verdict = "⚠️ 观察 — 方向不明"
|
||
else:
|
||
verdict = "❌ 不碰 — 双杀风险"
|
||
print(f"结论: {verdict}")
|
||
print(f"{'='*60}")
|
||
|
||
# 保存
|
||
result = {
|
||
"code": code, "name": name, "date": datetime.now().strftime("%Y-%m-%d %H:%M"),
|
||
"scores": {"macro": m_score, "fundamental": f_score, "technical": t_score, "sentiment": s_score},
|
||
"total": total,
|
||
"verdict": verdict,
|
||
"macro_reasons": m_reasons,
|
||
"fundamental_reasons": f_reasons,
|
||
"fundamental_info": f_info,
|
||
"technical_reasons": t_reasons,
|
||
"technical_info": t_info,
|
||
"sentiment_reasons": s_reasons,
|
||
}
|
||
result_file = OUTPUT / f"四维_{code}.json"
|
||
with open(result_file, "w") as f:
|
||
json.dump(result, f, ensure_ascii=False, indent=2)
|
||
print(f"数据存: {result_file}")
|
||
|
||
return result
|
||
|
||
|
||
def scan_watchlist():
|
||
"""扫描关注列表"""
|
||
stocks = [
|
||
("000858", "五粮液"),
|
||
("600519", "贵州茅台"),
|
||
("000001", "平安银行"),
|
||
("300750", "宁德时代"),
|
||
("510300", "沪深300ETF"),
|
||
]
|
||
|
||
print(f"\n{'='*70}")
|
||
print(f"{'股票':<12} {'宏观':>5} {'基本面':>6} {'技术面':>6} {'消息面':>6} {'综合':>5} {'结论'}")
|
||
print(f"{'-'*70}")
|
||
|
||
results = []
|
||
for code, name in stocks:
|
||
r = analyze_stock(code, name)
|
||
results.append(r)
|
||
|
||
sc = r["scores"]
|
||
ts = r["total"]
|
||
print(f"{name:<12} {sc['macro']:>+4} {sc['fundamental']:>+5} {sc['technical']:>+5} {sc['sentiment']:>+5} {ts:>+4} {r['verdict'].split(' ')[0]}")
|
||
|
||
time.sleep(0.5) # 避免请求过快
|
||
|
||
print(f"{'='*70}")
|
||
|
||
# 汇总
|
||
print(f"\n📊 宏观面: {'负面 ⬇️' if results[0]['scores']['macro'] < 0 else '正面 ⬆️' if results[0]['scores']['macro'] > 0 else '中性'}")
|
||
good = [r for r in results if r["total"] >= 2]
|
||
print(f"可选股票({len(good)}只): {[r['name'] for r in good]}")
|
||
|
||
msg = f"""📊 四维选股扫描
|
||
|
||
宏观面: {'负面 ⬇️' if results[0]['scores']['macro'] < 0 else '正面 ⬆️'}
|
||
|
||
| 股票 | 宏观 | 基本面 | 技术 | 消息 | 综合 |
|
||
|------|------|--------|------|------|------|
|
||
"""
|
||
for r in results:
|
||
sc = r["scores"]
|
||
msg += f"| {r['name']} | {sc['macro']:>+2} | {sc['fundamental']:>+2} | {sc['technical']:>+2} | {sc['sentiment']:>+2} | **{r['total']:>+2}** |\n"
|
||
|
||
good = [r for r in results if r["total"] >= 2]
|
||
msg += f"\n可选股票({len(good)}): "
|
||
msg += " / ".join([r["name"] for r in good]) if good else "无"
|
||
|
||
msg += f"\n\n生成: {datetime.now().strftime('%Y-%m-%d %H:%M')}"
|
||
send_feishu(msg)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
if "--scan" in sys.argv or "scan" in sys.argv:
|
||
scan_watchlist()
|
||
elif "--watchlist" in sys.argv:
|
||
scan_watchlist()
|
||
elif len(sys.argv) >= 3 and sys.argv[1] == "analyze":
|
||
code = sys.argv[2]
|
||
name = sys.argv[3] if len(sys.argv) > 3 else code
|
||
analyze_stock(code, name)
|
||
elif len(sys.argv) >= 2 and sys.argv[1] not in ["--scan", "--watchlist"]:
|
||
code = sys.argv[1]
|
||
name = sys.argv[2] if len(sys.argv) > 2 else code
|
||
analyze_stock(code, name)
|
||
else:
|
||
print("用法:")
|
||
print(" python3 stock_selector.py --scan # 扫描关注列表")
|
||
print(" python3 stock_selector.py analyze <代码> [名称] # 分析单只")
|
||
print(" python3 stock_selector.py 000858 五粮液 # 同上简写") |