quant-ai / fetch_stock.py
Ypeng12's picture
chore: rename branding from Quont to Quant and fix frontend build compilation errors
0ef2671
Raw
History Blame Contribute Delete
4.63 kB
import yfinance as yf
import pandas as pd
import numpy as np
def calculate_atr(df, period=14):
"""
计算 ATR (Average True Range) 真实波幅,用来衡量波动率。
ATR 越高,说明波动越大,适合日内交易。
"""
high_low = df['High'] - df['Low']
high_close = np.abs(df['High'] - df['Close'].shift())
low_close = np.abs(df['Low'] - df['Close'].shift())
ranges = pd.concat([high_low, high_close, low_close], axis=1)
true_range = np.max(ranges, axis=1)
atr = true_range.rolling(window=period).mean()
return atr
def scan_stocks(tickers):
"""
模拟交易员开盘前的扫描逻辑:
1. 相对成交量 (RVol) - 今天交易量是否是过去20天平均成交量的 1.5 倍以上?
2. 波动率 (ATR % of Price) - 股票的波动性是否够大?
3. 跳空幅度 (Gap %) - 开盘价相比昨天收盘价跳空了多少?
"""
print("=" * 60)
print(" QUANT.AI - PROFESSIONAL DAY TRADER SCANNER (盘前选股器)")
print("=" * 60)
print("正在从 Yahoo Finance 获取数据,请稍候...\n")
watchlist = []
for ticker in tickers:
try:
# 获取最近 30 天的日线数据
stock = yf.Ticker(ticker)
df = stock.history(period="30d")
if df.empty or len(df) < 20:
continue
# 最新一天的行情数据
latest_day = df.iloc[-1]
prev_day = df.iloc[-2]
# 1. 计算相对成交量 (Relative Volume - RVol)
avg_volume_20d = df['Volume'].iloc[-21:-1].mean()
latest_volume = latest_day['Volume']
rvol = latest_volume / avg_volume_20d if avg_volume_20d > 0 else 0
# 2. 计算 ATR (以百分比形式表示,方便横向对比不同股价的股票)
df['ATR'] = calculate_atr(df, period=14)
latest_atr = df['ATR'].iloc[-1]
atr_pct = (latest_atr / latest_day['Close']) * 100 if latest_day['Close'] > 0 else 0
# 3. 计算跳空幅度 (Gap %)
gap_pct = ((latest_day['Open'] - prev_day['Close']) / prev_day['Close']) * 100
watchlist.append({
'Ticker': ticker,
'Price': round(latest_day['Close'], 2),
'RVol': round(rvol, 2),
'ATR_%': round(atr_pct, 2),
'Gap_%': round(gap_pct, 2),
'Volume_Millions': round(latest_volume / 1_000_000, 2)
})
except Exception as e:
print(f"获取 {ticker} 数据失败: {str(e)}")
# 转为 DataFrame 方便处理和展示
results_df = pd.DataFrame(watchlist)
# 过滤与排序:
# 1. 过滤:波动率 ATR_% 必须大于 1.5%(波动太小的股我们不做日内)
# 2. 排序:按 RVol (相对成交量) 降序排列,成交量放大的股票才是热点
results_df = results_df.sort_values(by='RVol', ascending=False)
print("【候选股票扫描结果列表】:")
print(results_df.to_string(index=False))
print("\n" + "-" * 60)
# 选出今天最合适做 Day Trading 的股票
# 推荐标准:RVol > 1.2 且 ATR_% > 1.5%
recommended = results_df[(results_df['RVol'] > 1.2) & (results_df['ATR_%'] > 1.5)]
print("【AI 盘前交易推荐】:")
if not recommended.empty:
for idx, row in recommended.iterrows():
print(f"★ 推荐交易 {row['Ticker']}:")
print(f" - 理由:今日成交量放大到平时的 {row['RVol']} 倍 (RVol={row['RVol']}),波动率达 {row['ATR_%']}%。")
print(f" - 操作策略:根据日内K线走势进行突破或回调买入/做空,单笔最大止损控制在账户的 1%。")
else:
# 如果没有符合高波动和高成交量的股票,宁可观望
top_stock = results_df.iloc[0] if not results_df.empty else None
print("⚠ 警报:今天市场整体成交量萎缩或波动较低。")
print(" - 建议:【观望/不交易】。频繁交易将产生巨大的佣金损耗!")
if top_stock is not None:
print(f" - 若强行操作,可关注相对最活跃的 {top_stock['Ticker']} (RVol={top_stock['RVol']})。")
print("=" * 60)
if __name__ == "__main__":
# 我们扫描 TSLA(特斯拉), NVDA(英伟达), AAPL(苹果), MSFT(微软), AMD(超威半导体) 等大盘股
popular_stocks = ["TSLA", "NVDA", "AAPL", "MSFT", "AMD"]
scan_stocks(popular_stocks)