stock-liquidity

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Stock Liquidity Analysis Skill

股票流动性分析技能

Analyzes stock liquidity across multiple dimensions — bid-ask spreads, volume patterns, order book depth, estimated market impact, and turnover ratios — using data from Yahoo Finance via yfinance.
Liquidity matters because it determines the real cost of trading. The quoted price is not what you actually pay — spreads, slippage, and market impact all eat into returns, especially for larger positions or less liquid names.
Important: This is for research and educational purposes only. Not financial advice. yfinance is not affiliated with Yahoo, Inc.

通过yfinance调用Yahoo Finance的数据,从多个维度分析股票流动性:买卖价差、成交量模式、订单簿深度、预估市场影响和换手率。
流动性的重要性在于它决定了交易的实际成本,你实际支付的成本并不等于报价——价差、滑点和市场影响都会侵蚀收益,对于大额持仓或者流动性较差的标的尤其明显。
重要提示:本内容仅用于研究和教育目的,不构成投资建议。yfinance与Yahoo, Inc.无附属关系。

Step 1: Ensure Dependencies Are Available

步骤1:确认依赖可用

Current environment status:
!`python3 -c "import yfinance, pandas, numpy; print(f'yfinance={yfinance.__version__} pandas={pandas.__version__} numpy={numpy.__version__}')" 2>/dev/null || echo "DEPS_MISSING"`
If
DEPS_MISSING
, install required packages:
python
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "yfinance", "pandas", "numpy"])
If already installed, skip and proceed.

当前环境状态:
!`python3 -c "import yfinance, pandas, numpy; print(f'yfinance={yfinance.__version__} pandas={pandas.__version__} numpy={numpy.__version__}')" 2>/dev/null || echo "DEPS_MISSING"`
如果输出
DEPS_MISSING
,则安装所需包:
python
import subprocess, sys
subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "yfinance", "pandas", "numpy"])
如果已安装则跳过,直接进入下一步。

Step 2: Route to the Correct Sub-Skill

步骤2:路由到对应的子技能

Classify the user's request and jump to the matching section. If the user asks for a general liquidity assessment without specifying a particular metric, run Sub-Skill A (Liquidity Dashboard) which computes all key metrics together.
User RequestRoute ToExamples
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 spreadSub-Skill B: Spread Analysis"bid-ask spread for AMD", "what's the spread on NVDA options", "trading cost estimate"
Volume, ADTV, dollar volume, volume profileSub-Skill C: Volume Analysis"volume analysis MSFT", "average daily volume", "volume profile for SPY"
Order book depth, market depth, level 2Sub-Skill D: Order Book Depth"order book depth for AAPL", "market depth", "show me the book"
Market impact, slippage, execution cost for large ordersSub-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 floatSub-Skill F: Turnover Ratio"turnover ratio for GME", "float turnover", "how actively traded is this"
Compare liquidity across multiple stocksSub-Skill A (multi-ticker mode)"compare liquidity AAPL vs TSLA", "which is more liquid AMD or INTC"
对用户请求进行分类,跳转到匹配的模块。如果用户要求进行通用流动性评估,未指定具体指标,则运行子技能A(流动性看板),一次性计算所有核心指标。
用户请求路由到示例
通用流动性检查、"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哪个流动性更好"

Defaults

默认参数

ParameterDefault
Lookback period
3mo
(3 months)
Data interval
1d
(daily)
Market impact modelSquare-root model
Intraday interval (when needed)
5m

参数默认值
回溯周期
3mo
(3个月)
数据间隔
1d
(每日)
市场影响模型平方根模型
日内数据间隔(如需)
5m

Sub-Skill A: Liquidity Dashboard

子技能A:流动性看板

Goal: Produce a comprehensive liquidity snapshot combining all key metrics for one or more tickers.
目标:生成包含单只或多只标的所有核心指标的综合流动性快照。

A1: Fetch data and compute all metrics

A1:获取数据并计算所有指标

python
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),
    }
python
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),
    }

A2: Interpret and present

A2:解读与展示

Present as a summary card. For the Amihud illiquidity ratio, multiply by 1e9 for readability (standard convention).
Liquidity grade (use these rough thresholds for US equities):
GradeAvg Dollar VolumeSpread (%)Amihud (×10⁹)
Very High> $500M/day< 0.03%< 0.01
High$50M–$500M/day0.03–0.10%0.01–0.1
Moderate$5M–$50M/day0.10–0.50%0.1–1.0
Low$500K–$5M/day0.50–2.00%1.0–10
Very Low< $500K/day> 2.00%> 10
When comparing multiple tickers, show a side-by-side table and highlight which is more liquid and why.

以摘要卡片的形式呈现。对于Amihud非流动性比率,按照行业惯例乘以1e9提升可读性。
流动性评级(针对美股的大致阈值):
评级平均美元成交量价差(%)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
对比多只标的时,展示并列表格,标注哪只流动性更好及原因。

Sub-Skill B: Spread Analysis

子技能B:价差分析

Goal: Detailed bid-ask spread analysis including current spread, historical context from options data, and effective spread estimates.
目标:精细化的买卖价差分析,包括当前价差、期权数据的历史背景、有效价差预估。

B1: Current spread from quote

B1:从报价获取当前价差

python
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 result
python
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 result

B2: Options spread context

B2:期权价差参考

Options data from yfinance includes bid/ask for each strike, which gives a sense of derivatives liquidity:
python
def 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 chain
yfinance的期权数据包含每个行权价的买卖报价,可以体现衍生品的流动性:
python
def 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 chain

B3: Present results

B3:结果展示

Show:
  • Current quoted spread (absolute, relative %, basis points)
  • Bid/ask sizes if available
  • Near-the-money options spreads for context
  • How the spread compares to typical ranges for this market cap tier

展示内容包括:
  • 当前报价价差(绝对值、相对百分比、基点)
  • 买卖挂单量(如有)
  • 平值附近期权的价差作为参考
  • 当前价差与该市值梯队的常规区间对比

Sub-Skill C: Volume Analysis

子技能C:成交量分析

Goal: Analyze trading volume patterns — averages, trends, relative volume, and dollar volume.
目标:分析成交量模式:平均值、趋势、相对成交量和美元成交量。

C1: Compute volume metrics

C1:计算成交量指标

python
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

    # 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()),
    }
python
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()),
    }

C2: Present results

C2:结果展示

Show:
  • Average daily volume (shares and dollar) with median for comparison
  • Relative volume (RVOL) — today's volume vs. the average. RVOL > 1.5 is elevated; RVOL < 0.5 is unusually quiet
  • Volume trend — is trading activity increasing or declining?
  • Day-of-week pattern (if meaningful variation exists)
  • Top 5 highest-volume days with context (earnings? news?)

展示内容包括:
  • 平均每日成交量(股数和美元)及中位数以供对比
  • 相对成交量(RVOL):当日成交量 vs 平均值。RVOL>1.5说明成交量放大;RVOL<0.5说明交易异常清淡
  • 成交量趋势:交易活跃度正在上升还是下降?
  • 周内分布模式(如果存在显著差异)
  • 成交量最高的5个日期及相关背景(财报?新闻?)

Sub-Skill D: Order Book Depth

子技能D:订单簿深度

Goal: Estimate order book depth using available bid/ask data from the equity quote and options chain.
Yahoo Finance does not provide full Level 2 / order book data. Be upfront about this limitation. What we can do:
  1. Equity quote: bid, ask, bid size, ask size (top of book only)
  2. Options chain: bid/ask and open interest across strikes give a proxy for derivatives depth
  3. Intraday volume distribution: how volume is distributed within the day suggests how deep the continuous market is
目标:使用股票报价和期权链的可用买卖报价数据预估订单簿深度。
Yahoo Finance不提供完整的Level 2/订单簿数据,需提前说明这一限制。我们可以实现的内容:
  1. 股票报价:买价、卖价、买量、卖量(仅订单簿顶层)
  2. 期权链:不同行权价的买卖报价和未平仓合约可以作为衍生品深度的代理指标
  3. 日内成交量分布:成交量在日内的分布情况可以反映连续交易市场的深度

D1: Gather available depth data

D1:收集可用深度数据

python
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 None
python
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 = {
        "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 None

D2: Present results

D2:结果展示

Show:
  • Top of book: current bid/ask with sizes
  • Intraday volume shape: where volume concentrates (open/close vs. midday)
  • Options depth: total open interest and volume as a proxy for derivatives liquidity
  • Honest limitation: "Yahoo Finance provides top-of-book only. For full Level 2 depth, a direct market data feed (e.g., NYSE OpenBook, NASDAQ TotalView) is needed."

展示内容包括:
  • 订单簿顶层:当前买卖报价及挂单量
  • 日内成交量形态:成交量集中的时段(开盘/收盘 vs 午盘)
  • 期权深度:总未平仓合约和成交量作为衍生品流动性的代理指标
  • 如实说明限制:"Yahoo Finance仅提供订单簿顶层数据。如需完整Level 2深度,需要直接对接市场数据源(例如NYSE OpenBook、NASDAQ TotalView)。"

Sub-Skill E: Market Impact

子技能E:市场影响

Goal: Estimate how much a given order size would move the price, using the square-root market impact model.
The standard model in practice is: Impact (%) = σ × √(Q / V) where σ is daily volatility, Q is order size in shares, and V is average daily volume. This is a simplified version of the Almgren-Chriss framework used by institutional traders.
目标:使用平方根市场影响模型,预估给定订单规模对价格的影响程度。
行业常用的标准模型为:影响(%) = σ × √(Q / V),其中σ为日波动率,Q为订单股数,V为平均每日成交量。这是机构交易者使用的Almgren-Chriss框架的简化版本。

E1: Compute market impact estimate

E1:计算市场影响预估

python
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()

    # 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,
    }
python
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,
    }

E2: Present results

E2:结果展示

Show:
  • The estimated impact for the user's specific order size
  • An impact curve table showing how cost scales with order size
  • Context: "This uses the square-root market impact model, a standard institutional estimate. Actual impact depends on execution strategy (VWAP, TWAP, etc.), time of day, and current market conditions."
  • If impact > 50 bps, flag that the order is large relative to liquidity and suggest the user consider algorithmic execution or splitting the order across days

展示内容包括:
  • 用户指定订单规模的预估影响
  • 影响曲线表格,展示成本随订单规模的变化情况
  • 参考说明:"本预估采用平方根市场影响模型,是机构常用的标准估算方法。实际影响取决于执行策略(VWAP、TWAP等)、交易时段和当前市场环境。"
  • 如果预估影响超过50个基点,提示该订单相对于流动性来说规模过大,建议用户考虑算法执行或者将订单拆分成多日执行

Sub-Skill F: Turnover Ratio

子技能F:换手率分析

Goal: Measure how actively a stock trades relative to its shares outstanding and free float.
目标:衡量股票相对于总股本和自由流通盘的交易活跃度。

F1: Compute turnover metrics

F1:计算换手率指标

python
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 result
python
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)

    # 换手率趋势
    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

F2: Present results

F2:结果展示

Show:
  • Daily and annualized turnover ratios (vs. outstanding and float)
  • "Days to trade the float" — how many days at average volume to turn over the entire free float
  • Turnover trend — is the stock becoming more or less actively traded?
  • Context:
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%交投清淡——大概率是机构长期持有或者被市场忽略

Step 3: Respond to the User

步骤3:响应用户

After running the appropriate sub-skill:
运行对应的子技能后:

Always include

始终包含以下内容

  • The lookback period used for historical metrics
  • The data timestamp — spreads and quotes are snapshots, not real-time
  • Any tickers that returned empty data (invalid symbol, delisted, etc.)
  • 历史指标使用的回溯周期
  • 数据时间戳——价差和报价是快照数据,非实时
  • 任何返回空数据的标的代码(无效代码、退市等)

Always caveat

始终包含以下提示

  • Yahoo Finance quote data has a 15-minute delay for most exchanges — spreads shown may not reflect the current live market
  • Full order book (Level 2) data is not available through Yahoo Finance
  • Market impact estimates are models, not guarantees — actual execution costs depend on strategy, timing, and market conditions
  • Liquidity can change rapidly — a stock that's liquid today may not be tomorrow (especially around events, halts, or during extended hours)
  • 大多数交易所的Yahoo Finance报价数据有15分钟延迟——展示的价差可能不反映当前实盘市场情况
  • Yahoo Finance不提供完整的订单簿(Level 2)数据
  • 市场影响预估是模型结果,不构成保证——实际执行成本取决于策略、时机和市场环境
  • 流动性可能快速变化——今天流动性好的股票明天可能变差(尤其是事件、停牌前后,或者盘外交易时段)

Practical guidance (mention when relevant)

实用指引(相关时提及)

  • Position sizing: If estimated impact exceeds 25 bps, the position may be too large for the stock's liquidity
  • Small/micro-cap warning: Stocks with < $1M daily dollar volume require careful execution
  • Spread costs compound: A 0.10% spread on a round-trip (buy + sell) costs 0.20% — this adds up for active strategies
  • Illiquidity premium: Less liquid stocks historically earn higher returns as compensation — but the transaction costs can eat this premium
Important: Never recommend specific trades. Present liquidity data and let the user make their own decisions.

  • 仓位规模:如果预估影响超过25个基点,说明仓位相对于股票的流动性来说可能过大
  • 小/微盘股提示:日均美元成交量低于100万美元的股票需要谨慎执行
  • 价差成本复利效应:一次买卖往返0.10%的价差成本合计为0.20%——对于高频策略来说成本累积非常快
  • 非流动性溢价:历史上流动性较差的股票收益率更高作为补偿——但交易成本可能侵蚀这部分溢价
重要提示:绝对不要推荐具体交易,仅展示流动性数据,由用户自行决策。

Reference Files

参考文件

  • references/liquidity_reference.md
    — Detailed formulas, extended code templates, metric interpretation guides, and academic references for all liquidity measures
Read the reference file when you need exact formulas, edge case handling, or deeper background on liquidity metrics.
  • references/liquidity_reference.md
    ——所有流动性指标的详细公式、扩展代码模板、指标解读指南和学术参考文献
需要精确公式、边缘场景处理或者更深入的流动性指标背景时可查阅参考文件。