基础指标的学习

Published

2025-10-11

import os
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from trade_utils import get_daily, get_etf_name_map
# 设置渲染器为 Jupyter 默认
import plotly.io as pio
pio.renderers.default = 'plotly_mimetype+notebook'

symbol = '513100.SH'
name_map = get_etf_name_map()
display_name = name_map.get(symbol, symbol)

# 获取日线数据
daily = get_daily([symbol])
# 确保按日期排序
daily = daily.sort_values('date').reset_index(drop=True)

# 只保留本标的(如 get_daily 返回多标的时)
if 'code' in daily.columns:
    daily = daily[daily['code'] == symbol].copy()

# 转换日期为 datetime
if not np.issubdtype(daily['date'].dtype, np.datetime64):
    daily['date'] = pd.to_datetime(daily['date'])

# 统一列名(lower case assumed)
close = daily['close']
high = daily['high']
low = daily['low']
volume = daily['volume']

1 数据概览与收盘价/成交量

daily.head(3)
code date open high low close volume
0 513100.SH 2013-05-14 0.990 0.999 0.989 0.997 877116
1 513100.SH 2013-05-15 0.997 0.999 0.994 0.999 265570
2 513100.SH 2013-05-16 0.999 1.000 0.996 0.997 36379
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(
    go.Scatter(x=daily['date'], y=close, name='Close', line=dict(color='#1f77b4')),
    secondary_y=False
)
fig.add_trace(
    go.Bar(x=daily['date'], y=volume, name='Volume', marker_color='rgba(200,200,200,0.6)') ,
    secondary_y=True
)
fig.update_yaxes(title_text='Price', secondary_y=False)
fig.update_yaxes(title_text='Volume', secondary_y=True)
fig.update_layout(title=f'{display_name}{symbol}) 收盘价与成交量')
fig

2 日收益率与累计收益

  • 定义
  • 日收益率 ret_t = close_t / close_{t-1} - 1
  • 对数收益 logret_t = ln(close_t) - ln(close_{t-1})
  • 累计收益 cumret_t = Π(1+ret_i) - 1
daily['ret'] = close.pct_change()
daily['logret'] = np.log(close).diff()
daily['cumret'] = (1 + daily['ret']).cumprod() - 1

fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.05)
fig.add_trace(go.Scatter(x=daily['date'], y=daily['ret'], name='Daily Return', line=dict(color='#9467bd')), row=1, col=1)
fig.add_trace(go.Scatter(x=daily['date'], y=daily['cumret'], name='Cumulative Return', line=dict(color='#2ca02c')), row=2, col=1)
fig.update_layout(title=f'{display_name} 日收益与累计收益')
fig

3 波动率(年化)

  • 定义
  • 用对数收益标准差近似波动率,年化:vol_annual = std(logret) * sqrt(252)
  • 也可按滚动窗口计算滚动年化波动
trading_days = 252
vol_annual = daily['logret'].std(skipna=True) * np.sqrt(trading_days)
daily['vol20'] = daily['logret'].rolling(20).std() * np.sqrt(trading_days)
vol_annual
np.float64(0.5175767400767213)
fig = px.line(daily, x='date', y='vol20', title=f'{display_name} 20日滚动年化波动率')
fig

4 移动均线(MA)与指数移动均线(EMA)

  • 定义
  • 简单移动均线:MA_n = mean(close_{t-n+1..t})
  • 指数移动均线:EMA_n = EMA_{n, t-1} + α(close_t - EMA_{n, t-1})α=2/(n+1)
daily['ma20'] = close.rolling(20).mean()
daily['ma60'] = close.rolling(60).mean()
daily['ema12'] = close.ewm(span=12, adjust=False).mean()
daily['ema26'] = close.ewm(span=26, adjust=False).mean()

fig = go.Figure()
fig.add_trace(go.Scatter(x=daily['date'], y=close, name='Close', line=dict(color='#1f77b4')))
fig.add_trace(go.Scatter(x=daily['date'], y=daily['ma20'], name='MA20', line=dict(color='#ff7f0e')))
fig.add_trace(go.Scatter(x=daily['date'], y=daily['ma60'], name='MA60', line=dict(color='#2ca02c')))
fig.update_layout(title=f'{display_name} 收盘价与MA')
fig

5 布林带(Bollinger Bands)

  • 定义
  • 中轨:MB = MA_n
  • 上轨:UB = MB + k * std_n
  • 下轨:LB = MB - k * std_n,通常 n=20, k=2
n, k = 20, 2
mid = daily['ma20']
std = close.rolling(n).std()
daily['bb_upper'] = mid + k * std
daily['bb_lower'] = mid - k * std

fig = go.Figure()
fig.add_trace(go.Scatter(x=daily['date'], y=close, name='Close', line=dict(color='#1f77b4')))
fig.add_trace(go.Scatter(x=daily['date'], y=mid, name='MB(20)', line=dict(color='#ff7f0e')))
fig.add_trace(go.Scatter(x=daily['date'], y=daily['bb_upper'], name='UB', line=dict(color='rgba(255,127,14,0.6)')))
fig.add_trace(go.Scatter(x=daily['date'], y=daily['bb_lower'], name='LB', line=dict(color='rgba(255,127,14,0.6)')))
fig.update_layout(title=f'{display_name} 布林带(20,2)')
fig

6 RSI(相对强弱指标)

  • 定义(Wilder)
  • U_t = max(Δclose_t, 0)D_t = max(-Δclose_t, 0)
  • 平滑平均:AU = EMA(U, n), AD = EMA(D, n)RS = AU/AD
  • RSI = 100 - 100/(1+RS),常用 n=14
n = 14
delta = close.diff()
up = np.where(delta > 0, delta, 0.0)
down = np.where(delta < 0, -delta, 0.0)
au = pd.Series(up, index=close.index).ewm(alpha=1/n, adjust=False).mean()
ad = pd.Series(down, index=close.index).ewm(alpha=1/n, adjust=False).mean()
rs = au / (ad.replace(0, np.nan))
daily['rsi14'] = 100 - 100 / (1 + rs)

fig = go.Figure()
fig.add_trace(go.Scatter(x=daily['date'], y=daily['rsi14'], name='RSI14', line=dict(color='#d62728')))
fig.add_hrect(y0=30, y1=70, fillcolor='LightGray', opacity=0.2, line_width=0)
fig.update_layout(title=f'{display_name} RSI(14)')
fig

7 MACD

  • 定义
  • DIF = EMA12 - EMA26
  • DEA = EMA(DIF, 9)
  • MACD = 2 * (DIF - DEA)(有的实现不乘2)
dif = daily['ema12'] - daily['ema26']
dea = dif.ewm(span=9, adjust=False).mean()
macd = 2 * (dif - dea)
daily['dif'], daily['dea'], daily['macd'] = dif, dea, macd

fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.03,
                    row_heights=[0.6, 0.4])
fig.add_trace(go.Scatter(x=daily['date'], y=close, name='Close', line=dict(color='#1f77b4')), row=1, col=1)
fig.add_trace(go.Scatter(x=daily['date'], y=dif, name='DIF', line=dict(color='#ff7f0e')), row=2, col=1)
fig.add_trace(go.Scatter(x=daily['date'], y=dea, name='DEA', line=dict(color='#2ca02c')), row=2, col=1)
fig.add_trace(go.Bar(x=daily['date'], y=macd, name='MACD', marker_color=np.where(macd>=0, '#d62728', '#17becf')), row=2, col=1)
fig.update_layout(title=f'{display_name} MACD 指标')
fig

8 ATR(平均真实波幅)

  • 定义
  • TR_t = max(high_t - low_t, |high_t - close_{t-1}|, |low_t - close_{t-1}|)
  • ATR_n = MA(TR, n),常用 n=14
prev_close = close.shift(1)
tr = pd.concat([
    (high - low),
    (high - prev_close).abs(),
    (low - prev_close).abs()
], axis=1).max(axis=1)
daily['atr14'] = tr.rolling(14).mean()

fig = px.line(daily, x='date', y='atr14', title=f'{display_name} ATR(14)')
fig

9 成交量均线

  • 定义
  • VMA_n = mean(volume_{t-n+1..t})
daily['vma20'] = volume.rolling(20).mean()

fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(go.Bar(x=daily['date'], y=volume, name='Volume', marker_color='rgba(150,150,200,0.5)'), secondary_y=False)
fig.add_trace(go.Scatter(x=daily['date'], y=daily['vma20'], name='VMA20', line=dict(color='#ff7f0e')), secondary_y=False)
fig.update_layout(title=f'{display_name} 成交量与VMA20')
fig

10 结语

  • 本笔记要点
  • 了解了常见价格与量能类指标的定义与计算
  • 用 Plotly 对 513100.SH 进行了可视化
  • 指标参数并非固定,可按需要调整、组合与回测