Loading...
Loading...
Compare original and translation side by side
!`python3 -c "import yfinance, pandas, numpy; print(f'yfinance={yfinance.__version__} pandas={pandas.__version__} numpy={numpy.__version__}')" 2>/dev/null || echo "DEPS_MISSING"`DEPS_MISSINGimport subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "yfinance", "pandas", "numpy"])!`python3 -c "import yfinance, pandas, numpy; print(f'yfinance={yfinance.__version__} pandas={pandas.__version__} numpy={numpy.__version__}')" 2>/dev/null || echo "DEPS_MISSING"`DEPS_MISSINGimport subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "yfinance", "pandas", "numpy"])| User Request | Route To | Examples |
|---|---|---|
| General liquidity check, "how liquid is X" | Sub-Skill A: Liquidity Dashboard | "how liquid is AAPL", "liquidity analysis for TSLA", "is this stock liquid enough" |
| Bid-ask spread, trading costs, effective spread | Sub-Skill B: Spread Analysis | "bid-ask spread for AMD", "what's the spread on NVDA options", "trading cost estimate" |
| Volume, ADTV, dollar volume, volume profile | Sub-Skill C: Volume Analysis | "volume analysis MSFT", "average daily volume", "volume profile for SPY" |
| Order book depth, market depth, level 2 | Sub-Skill D: Order Book Depth | "order book depth for AAPL", "market depth", "show me the book" |
| Market impact, slippage, execution cost for large orders | Sub-Skill E: Market Impact | "how much would 50k shares move the price", "slippage estimate", "market impact of $1M order" |
| Turnover ratio, trading activity relative to float | Sub-Skill F: Turnover Ratio | "turnover ratio for GME", "float turnover", "how actively traded is this" |
| Compare liquidity across multiple stocks | Sub-Skill A (multi-ticker mode) | "compare liquidity AAPL vs TSLA", "which is more liquid AMD or INTC" |
| 用户请求 | 路由到 | 示例 |
|---|---|---|
| 通用流动性检查、"X的流动性如何" | 子技能A:流动性看板 | "AAPL的流动性如何", "TSLA的流动性分析", "这只股票的流动性是否足够" |
| 买卖价差、交易成本、有效价差 | 子技能B:价差分析 | "AMD的买卖价差", "NVDA期权的价差是多少", "交易成本预估" |
| 成交量、ADTV、美元成交量、成交量分布 | 子技能C:成交量分析 | "MSFT成交量分析", "平均每日成交量", "SPY的成交量分布" |
| 订单簿深度、市场深度、Level 2数据 | 子技能D:订单簿深度 | "AAPL的订单簿深度", "市场深度", "展示订单簿" |
| 市场影响、滑点、大额订单执行成本 | 子技能E:市场影响 | "5万股会使价格变动多少", "滑点预估", "100万美元订单的市场影响" |
| 换手率、相对于流通盘的交易活跃度 | 子技能F:换手率分析 | "GME的换手率", "流通盘换手率", "这只股票的交易活跃度如何" |
| 多只股票的流动性对比 | 子技能A(多标的模式) | "对比AAPL和TSLA的流动性", "AMD和INTC哪个流动性更好" |
| Parameter | Default |
|---|---|
| Lookback period | |
| Data interval | |
| Market impact model | Square-root model |
| Intraday interval (when needed) | |
| 参数 | 默认值 |
|---|---|
| 回溯周期 | |
| 数据间隔 | |
| 市场影响模型 | 平方根模型 |
| 日内数据间隔(如需) | |
import yfinance as yf
import pandas as pd
import numpy as np
def liquidity_dashboard(ticker_symbol, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
info = ticker.info
hist = ticker.history(period=period)
if hist.empty:
return None
# --- Spread metrics (from current quote) ---
bid = info.get("bid", None)
ask = info.get("ask", None)
current_price = info.get("currentPrice") or info.get("regularMarketPrice") or hist["Close"].iloc[-1]
spread = None
spread_pct = None
if bid and ask and bid > 0 and ask > 0:
spread = round(ask - bid, 4)
midpoint = (ask + bid) / 2
spread_pct = round((spread / midpoint) * 100, 4)
# --- Volume metrics ---
avg_volume = hist["Volume"].mean()
median_volume = hist["Volume"].median()
avg_dollar_volume = (hist["Close"] * hist["Volume"]).mean()
volume_std = hist["Volume"].std()
volume_cv = volume_std / avg_volume if avg_volume > 0 else None # coefficient of variation
# --- Turnover ratio ---
shares_outstanding = info.get("sharesOutstanding", None)
float_shares = info.get("floatShares", None)
base_shares = float_shares or shares_outstanding
turnover_ratio = round(avg_volume / base_shares, 6) if base_shares else None
# --- Amihud illiquidity ratio ---
# Average of |daily return| / daily dollar volume
returns = hist["Close"].pct_change().dropna()
dollar_volume = (hist["Close"] * hist["Volume"]).iloc[1:] # align with returns
amihud_values = returns.abs() / dollar_volume
amihud = amihud_values[amihud_values.replace([np.inf, -np.inf], np.nan).notna()].mean()
# --- Market impact estimate (square-root model) ---
# For a hypothetical order of 1% of ADV
adv = avg_volume
order_size = adv * 0.01
daily_volatility = returns.std()
sigma = daily_volatility
participation_rate = order_size / adv if adv > 0 else 0
impact_bps = sigma * np.sqrt(participation_rate) * 10000 # in basis points
return {
"ticker": ticker_symbol,
"current_price": round(current_price, 2),
"bid": bid,
"ask": ask,
"spread": spread,
"spread_pct": spread_pct,
"avg_daily_volume": int(avg_volume),
"median_daily_volume": int(median_volume),
"avg_dollar_volume": round(avg_dollar_volume, 0),
"volume_cv": round(volume_cv, 3) if volume_cv else None,
"shares_outstanding": shares_outstanding,
"float_shares": float_shares,
"turnover_ratio": turnover_ratio,
"amihud_illiquidity": round(amihud * 1e9, 4) if not np.isnan(amihud) else None,
"daily_volatility": round(daily_volatility * 100, 2),
"impact_1pct_adv_bps": round(impact_bps, 2),
"observations": len(hist),
}import yfinance as yf
import pandas as pd
import numpy as np
def liquidity_dashboard(ticker_symbol, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
info = ticker.info
hist = ticker.history(period=period)
if hist.empty:
return None
# --- 价差指标(来自当前报价) ---
bid = info.get("bid", None)
ask = info.get("ask", None)
current_price = info.get("currentPrice") or info.get("regularMarketPrice") or hist["Close"].iloc[-1]
spread = None
spread_pct = None
if bid and ask and bid > 0 and ask > 0:
spread = round(ask - bid, 4)
midpoint = (ask + bid) / 2
spread_pct = round((spread / midpoint) * 100, 4)
# --- 成交量指标 ---
avg_volume = hist["Volume"].mean()
median_volume = hist["Volume"].median()
avg_dollar_volume = (hist["Close"] * hist["Volume"]).mean()
volume_std = hist["Volume"].std()
volume_cv = volume_std / avg_volume if avg_volume > 0 else None # 变异系数
# --- 换手率 ---
shares_outstanding = info.get("sharesOutstanding", None)
float_shares = info.get("floatShares", None)
base_shares = float_shares or shares_outstanding
turnover_ratio = round(avg_volume / base_shares, 6) if base_shares else None
# --- Amihud非流动性比率 ---
# 日均 |收益率| / 日美元成交量的平均值
returns = hist["Close"].pct_change().dropna()
dollar_volume = (hist["Close"] * hist["Volume"]).iloc[1:] # 与收益率对齐
amihud_values = returns.abs() / dollar_volume
amihud = amihud_values[amihud_values.replace([np.inf, -np.inf], np.nan).notna()].mean()
# --- 市场影响预估(平方根模型) ---
# 假设订单占日均成交量的1%
adv = avg_volume
order_size = adv * 0.01
daily_volatility = returns.std()
sigma = daily_volatility
participation_rate = order_size / adv if adv > 0 else 0
impact_bps = sigma * np.sqrt(participation_rate) * 10000 # 基点为单位
return {
"ticker": ticker_symbol,
"current_price": round(current_price, 2),
"bid": bid,
"ask": ask,
"spread": spread,
"spread_pct": spread_pct,
"avg_daily_volume": int(avg_volume),
"median_daily_volume": int(median_volume),
"avg_dollar_volume": round(avg_dollar_volume, 0),
"volume_cv": round(volume_cv, 3) if volume_cv else None,
"shares_outstanding": shares_outstanding,
"float_shares": float_shares,
"turnover_ratio": turnover_ratio,
"amihud_illiquidity": round(amihud * 1e9, 4) if not np.isnan(amihud) else None,
"daily_volatility": round(daily_volatility * 100, 2),
"impact_1pct_adv_bps": round(impact_bps, 2),
"observations": len(hist),
}| Grade | Avg Dollar Volume | Spread (%) | Amihud (×10⁹) |
|---|---|---|---|
| Very High | > $500M/day | < 0.03% | < 0.01 |
| High | $50M–$500M/day | 0.03–0.10% | 0.01–0.1 |
| Moderate | $5M–$50M/day | 0.10–0.50% | 0.1–1.0 |
| Low | $500K–$5M/day | 0.50–2.00% | 1.0–10 |
| Very Low | < $500K/day | > 2.00% | > 10 |
| 评级 | 平均美元成交量 | 价差(%) | Amihud(×10⁹) |
|---|---|---|---|
| 极高 | > 5亿美元/天 | < 0.03% | < 0.01 |
| 高 | 5000万–5亿美元/天 | 0.03–0.10% | 0.01–0.1 |
| 中等 | 500万–5000万美元/天 | 0.10–0.50% | 0.1–1.0 |
| 低 | 50万–500万美元/天 | 0.50–2.00% | 1.0–10 |
| 极低 | < 50万美元/天 | > 2.00% | > 10 |
import yfinance as yf
def spread_analysis(ticker_symbol):
ticker = yf.Ticker(ticker_symbol)
info = ticker.info
bid = info.get("bid", 0)
ask = info.get("ask", 0)
bid_size = info.get("bidSize", None)
ask_size = info.get("askSize", None)
current_price = info.get("currentPrice") or info.get("regularMarketPrice", 0)
result = {"bid": bid, "ask": ask, "bid_size": bid_size, "ask_size": ask_size}
if bid > 0 and ask > 0:
midpoint = (bid + ask) / 2
result["absolute_spread"] = round(ask - bid, 4)
result["relative_spread_pct"] = round((ask - bid) / midpoint * 100, 4)
result["relative_spread_bps"] = round((ask - bid) / midpoint * 10000, 2)
return resultimport yfinance as yf
def spread_analysis(ticker_symbol):
ticker = yf.Ticker(ticker_symbol)
info = ticker.info
bid = info.get("bid", 0)
ask = info.get("ask", 0)
bid_size = info.get("bidSize", None)
ask_size = info.get("askSize", None)
current_price = info.get("currentPrice") or info.get("regularMarketPrice", 0)
result = {"bid": bid, "ask": ask, "bid_size": bid_size, "ask_size": ask_size}
if bid > 0 and ask > 0:
midpoint = (bid + ask) / 2
result["absolute_spread"] = round(ask - bid, 4)
result["relative_spread_pct"] = round((ask - bid) / midpoint * 100, 4)
result["relative_spread_bps"] = round((ask - bid) / midpoint * 10000, 2)
return resultdef options_spread_analysis(ticker_symbol):
ticker = yf.Ticker(ticker_symbol)
expirations = ticker.options
if not expirations:
return None
# Use nearest expiration
chain = ticker.option_chain(expirations[0])
for label, df in [("Calls", chain.calls), ("Puts", chain.puts)]:
atm = df[df["inTheMoney"]].tail(3).append(df[~df["inTheMoney"]].head(3))
atm = pd.concat([df[df["inTheMoney"]].tail(3), df[~df["inTheMoney"]].head(3)])
atm["spread"] = atm["ask"] - atm["bid"]
atm["spread_pct"] = (atm["spread"] / ((atm["ask"] + atm["bid"]) / 2) * 100).round(2)
return chaindef options_spread_analysis(ticker_symbol):
ticker = yf.Ticker(ticker_symbol)
expirations = ticker.options
if not expirations:
return None
# 使用最近到期的期权
chain = ticker.option_chain(expirations[0])
for label, df in [("Calls", chain.calls), ("Puts", chain.puts)]:
atm = df[df["inTheMoney"]].tail(3).append(df[~df["inTheMoney"]].head(3))
atm = pd.concat([df[df["inTheMoney"]].tail(3), df[~df["inTheMoney"]].head(3)])
atm["spread"] = atm["ask"] - atm["bid"]
atm["spread_pct"] = (atm["spread"] / ((atm["ask"] + atm["bid"]) / 2) * 100).round(2)
return chainimport yfinance as yf
import pandas as pd
import numpy as np
def volume_analysis(ticker_symbol, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
hist = ticker.history(period=period)
if hist.empty:
return None
vol = hist["Volume"]
close = hist["Close"]
dollar_vol = vol * close
# Relative volume (today vs average)
rvol = vol.iloc[-1] / vol.mean() if vol.mean() > 0 else None
# Volume trend (linear regression slope over the period)
x = np.arange(len(vol))
slope, _ = np.polyfit(x, vol.values, 1) if len(vol) > 1 else (0, 0)
trend_pct = (slope * len(vol)) / vol.mean() * 100 # % change over period
# Volume profile by day of week
hist_copy = hist.copy()
hist_copy["DayOfWeek"] = hist_copy.index.dayofweek
day_names = {0: "Mon", 1: "Tue", 2: "Wed", 3: "Thu", 4: "Fri"}
vol_by_day = hist_copy.groupby("DayOfWeek")["Volume"].mean()
vol_by_day.index = vol_by_day.index.map(day_names)
# High/low volume days
high_vol_days = hist.nlargest(5, "Volume")[["Close", "Volume"]]
low_vol_days = hist.nsmallest(5, "Volume")[["Close", "Volume"]]
return {
"avg_volume": int(vol.mean()),
"median_volume": int(vol.median()),
"avg_dollar_volume": round(dollar_vol.mean(), 0),
"current_volume": int(vol.iloc[-1]),
"relative_volume": round(rvol, 2) if rvol else None,
"volume_trend_pct": round(trend_pct, 1),
"volume_by_day": vol_by_day.to_dict(),
"high_vol_days": high_vol_days,
"low_vol_days": low_vol_days,
"max_volume": int(vol.max()),
"min_volume": int(vol.min()),
}import yfinance as yf
import pandas as pd
import numpy as np
def volume_analysis(ticker_symbol, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
hist = ticker.history(period=period)
if hist.empty:
return None
vol = hist["Volume"]
close = hist["Close"]
dollar_vol = vol * close
# 相对成交量(当日 vs 平均值)
rvol = vol.iloc[-1] / vol.mean() if vol.mean() > 0 else None
# 成交量趋势(周期内线性回归斜率)
x = np.arange(len(vol))
slope, _ = np.polyfit(x, vol.values, 1) if len(vol) > 1 else (0, 0)
trend_pct = (slope * len(vol)) / vol.mean() * 100 # 周期内变动百分比
# 按周几划分的成交量分布
hist_copy = hist.copy()
hist_copy["DayOfWeek"] = hist_copy.index.dayofweek
day_names = {0: "周一", 1: "周二", 2: "周三", 3: "周四", 4: "周五"}
vol_by_day = hist_copy.groupby("DayOfWeek")["Volume"].mean()
vol_by_day.index = vol_by_day.index.map(day_names)
# 高/低成交量日
high_vol_days = hist.nlargest(5, "Volume")[["Close", "Volume"]]
low_vol_days = hist.nsmallest(5, "Volume")[["Close", "Volume"]]
return {
"avg_volume": int(vol.mean()),
"median_volume": int(vol.median()),
"avg_dollar_volume": round(dollar_vol.mean(), 0),
"current_volume": int(vol.iloc[-1]),
"relative_volume": round(rvol, 2) if rvol else None,
"volume_trend_pct": round(trend_pct, 1),
"volume_by_day": vol_by_day.to_dict(),
"high_vol_days": high_vol_days,
"low_vol_days": low_vol_days,
"max_volume": int(vol.max()),
"min_volume": int(vol.min()),
}import yfinance as yf
import pandas as pd
import numpy as np
def order_book_proxy(ticker_symbol):
ticker = yf.Ticker(ticker_symbol)
info = ticker.info
# Top of book
top_of_book = {
"bid": info.get("bid"),
"ask": info.get("ask"),
"bid_size": info.get("bidSize"),
"ask_size": info.get("askSize"),
}
# Intraday volume distribution (5-min bars, last 5 days)
intraday = ticker.history(period="5d", interval="5m")
if not intraday.empty:
intraday_copy = intraday.copy()
intraday_copy["time"] = intraday_copy.index.time
vol_by_time = intraday_copy.groupby("time")["Volume"].mean()
# Normalize to percentage of daily volume
total = vol_by_time.sum()
vol_pct = (vol_by_time / total * 100).round(2) if total > 0 else vol_by_time
# Options open interest as depth proxy
expirations = ticker.options
if expirations:
chain = ticker.option_chain(expirations[0])
total_call_oi = chain.calls["openInterest"].sum()
total_put_oi = chain.puts["openInterest"].sum()
total_call_volume = chain.calls["volume"].sum()
total_put_volume = chain.puts["volume"].sum()
return top_of_book, vol_pct if not intraday.empty else Noneimport yfinance as yf
import pandas as pd
import numpy as np
def order_book_proxy(ticker_symbol):
ticker = yf.Ticker(ticker_symbol)
info = ticker.info
# 订单簿顶层
top_of_book = {
"bid": info.get("bid"),
"ask": info.get("ask"),
"bid_size": info.get("bidSize"),
"ask_size": info.get("askSize"),
}
# 日内成交量分布(5分钟K线,最近5天)
intraday = ticker.history(period="5d", interval="5m")
if not intraday.empty:
intraday_copy = intraday.copy()
intraday_copy["time"] = intraday_copy.index.time
vol_by_time = intraday_copy.groupby("time")["Volume"].mean()
# 归一化为日成交量百分比
total = vol_by_time.sum()
vol_pct = (vol_by_time / total * 100).round(2) if total > 0 else vol_by_time
# 期权未平仓合约作为深度代理
expirations = ticker.options
if expirations:
chain = ticker.option_chain(expirations[0])
total_call_oi = chain.calls["openInterest"].sum()
total_put_oi = chain.puts["openInterest"].sum()
total_call_volume = chain.calls["volume"].sum()
total_put_volume = chain.puts["volume"].sum()
return top_of_book, vol_pct if not intraday.empty else Noneimport yfinance as yf
import numpy as np
def market_impact(ticker_symbol, order_shares=None, order_dollars=None, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
hist = ticker.history(period=period)
info = ticker.info
if hist.empty:
return None
current_price = info.get("currentPrice") or hist["Close"].iloc[-1]
avg_volume = hist["Volume"].mean()
daily_volatility = hist["Close"].pct_change().dropna().std()
# Determine order size in shares
if order_dollars and not order_shares:
order_shares = order_dollars / current_price
elif not order_shares:
# Default: estimate for various sizes
order_shares = avg_volume * 0.01 # 1% of ADV
participation_rate = order_shares / avg_volume if avg_volume > 0 else 0
pct_adv = (order_shares / avg_volume * 100) if avg_volume > 0 else 0
# Square-root impact model
impact_pct = daily_volatility * np.sqrt(participation_rate) * 100
impact_bps = impact_pct * 100
impact_dollars = impact_pct / 100 * current_price * order_shares
# Generate impact curve for multiple order sizes
sizes = [0.001, 0.005, 0.01, 0.02, 0.05, 0.10, 0.20, 0.50] # as fraction of ADV
curve = []
for s in sizes:
q = avg_volume * s
imp = daily_volatility * np.sqrt(s) * 100
curve.append({
"pct_adv": round(s * 100, 1),
"shares": int(q),
"dollars": round(q * current_price, 0),
"impact_bps": round(imp * 100, 1),
"impact_dollars_per_share": round(imp / 100 * current_price, 4),
})
return {
"ticker": ticker_symbol,
"current_price": round(current_price, 2),
"avg_daily_volume": int(avg_volume),
"daily_volatility_pct": round(daily_volatility * 100, 2),
"order_shares": int(order_shares),
"order_dollars": round(order_shares * current_price, 0),
"pct_of_adv": round(pct_adv, 2),
"estimated_impact_bps": round(impact_bps, 1),
"estimated_impact_pct": round(impact_pct, 4),
"estimated_impact_total_dollars": round(impact_dollars, 2),
"impact_curve": curve,
}import yfinance as yf
import numpy as np
def market_impact(ticker_symbol, order_shares=None, order_dollars=None, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
hist = ticker.history(period=period)
info = ticker.info
if hist.empty:
return None
current_price = info.get("currentPrice") or hist["Close"].iloc[-1]
avg_volume = hist["Volume"].mean()
daily_volatility = hist["Close"].pct_change().dropna().std()
# 确定订单股数
if order_dollars and not order_shares:
order_shares = order_dollars / current_price
elif not order_shares:
# 默认:预估多种订单规模的影响
order_shares = avg_volume * 0.01 # 日均成交量的1%
participation_rate = order_shares / avg_volume if avg_volume > 0 else 0
pct_adv = (order_shares / avg_volume * 100) if avg_volume > 0 else 0
# 平方根影响模型
impact_pct = daily_volatility * np.sqrt(participation_rate) * 100
impact_bps = impact_pct * 100
impact_dollars = impact_pct / 100 * current_price * order_shares
# 生成多种订单规模的影响曲线
sizes = [0.001, 0.005, 0.01, 0.02, 0.05, 0.10, 0.20, 0.50] # 占日均成交量的比例
curve = []
for s in sizes:
q = avg_volume * s
imp = daily_volatility * np.sqrt(s) * 100
curve.append({
"pct_adv": round(s * 100, 1),
"shares": int(q),
"dollars": round(q * current_price, 0),
"impact_bps": round(imp * 100, 1),
"impact_dollars_per_share": round(imp / 100 * current_price, 4),
})
return {
"ticker": ticker_symbol,
"current_price": round(current_price, 2),
"avg_daily_volume": int(avg_volume),
"daily_volatility_pct": round(daily_volatility * 100, 2),
"order_shares": int(order_shares),
"order_dollars": round(order_shares * current_price, 0),
"pct_of_adv": round(pct_adv, 2),
"estimated_impact_bps": round(impact_bps, 1),
"estimated_impact_pct": round(impact_pct, 4),
"estimated_impact_total_dollars": round(impact_dollars, 2),
"impact_curve": curve,
}import yfinance as yf
import pandas as pd
import numpy as np
def turnover_analysis(ticker_symbol, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
hist = ticker.history(period=period)
info = ticker.info
if hist.empty:
return None
avg_volume = hist["Volume"].mean()
shares_outstanding = info.get("sharesOutstanding")
float_shares = info.get("floatShares")
result = {
"avg_daily_volume": int(avg_volume),
"shares_outstanding": shares_outstanding,
"float_shares": float_shares,
}
if shares_outstanding:
daily_turnover = avg_volume / shares_outstanding
result["daily_turnover_ratio"] = round(daily_turnover, 6)
result["annualized_turnover"] = round(daily_turnover * 252, 2)
result["days_to_trade_float"] = round(
(float_shares or shares_outstanding) / avg_volume, 1
) if avg_volume > 0 else None
if float_shares:
float_turnover = avg_volume / float_shares
result["float_turnover_daily"] = round(float_turnover, 6)
result["float_turnover_annualized"] = round(float_turnover * 252, 2)
# Turnover trend
vol = hist["Volume"]
base = float_shares or shares_outstanding
if base:
hist_copy = hist.copy()
hist_copy["turnover"] = hist_copy["Volume"] / base
recent_turnover = hist_copy["turnover"].tail(20).mean()
older_turnover = hist_copy["turnover"].head(20).mean()
if older_turnover > 0:
result["turnover_trend_pct"] = round(
(recent_turnover - older_turnover) / older_turnover * 100, 1
)
return resultimport yfinance as yf
import pandas as pd
import numpy as np
def turnover_analysis(ticker_symbol, period="3mo"):
ticker = yf.Ticker(ticker_symbol)
hist = ticker.history(period=period)
info = ticker.info
if hist.empty:
return None
avg_volume = hist["Volume"].mean()
shares_outstanding = info.get("sharesOutstanding")
float_shares = info.get("floatShares")
result = {
"avg_daily_volume": int(avg_volume),
"shares_outstanding": shares_outstanding,
"float_shares": float_shares,
}
if shares_outstanding:
daily_turnover = avg_volume / shares_outstanding
result["daily_turnover_ratio"] = round(daily_turnover, 6)
result["annualized_turnover"] = round(daily_turnover * 252, 2)
result["days_to_trade_float"] = round(
(float_shares or shares_outstanding) / avg_volume, 1
) if avg_volume > 0 else None
if float_shares:
float_turnover = avg_volume / float_shares
result["float_turnover_daily"] = round(float_turnover, 6)
result["float_turnover_annualized"] = round(float_turnover * 252, 2)
# 换手率趋势
vol = hist["Volume"]
base = float_shares or shares_outstanding
if base:
hist_copy = hist.copy()
hist_copy["turnover"] = hist_copy["Volume"] / base
recent_turnover = hist_copy["turnover"].tail(20).mean()
older_turnover = hist_copy["turnover"].head(20).mean()
if older_turnover > 0:
result["turnover_trend_pct"] = round(
(recent_turnover - older_turnover) / older_turnover * 100, 1
)
return result| Turnover (Annualized) | Interpretation |
|---|---|
| > 500% | Extremely active — likely speculative or momentum-driven |
| 100–500% | Actively traded |
| 30–100% | Moderate activity |
| < 30% | Thinly traded — likely institutional buy-and-hold or neglected |
| 换手率(年化) | 解读 |
|---|---|
| > 500% | 极度活跃——大概率是投机或者动量驱动 |
| 100–500% | 交易活跃 |
| 30–100% | 活跃度中等 |
| < 30% | 交投清淡——大概率是机构长期持有或者被市场忽略 |
references/liquidity_reference.mdreferences/liquidity_reference.md