1. 这节究竟解决什么实际问题?¶
1.3 金融市场基础 的具体任务是:读懂交易所、订单、OHLCV 和资产类别,避免把行情字段当成经济事实。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
会使用百分比和基础代数;建议先完成环境配置。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
示例优先使用仓库固定的日频复权收盘价;它不是实时行情,也不代表完整市场。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 1.3 金融市场基础 的输入、计算步骤、输出和局限;尤其能说明:读懂交易所、订单、OHLCV 和资产类别,避免把行情字段当成经济事实。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
价格表示资产在特定时点的水平,收益率表示两个时点之间的相对变化。多数跨资产比较使用收益率。
所有公式都先说明分子、分母、单位与时间尺度;年化前必须确认数据频率。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
NumPy/Pandas 帮你算数和对齐索引;统计库帮你估计参数,但不替你解释经济含义。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“读懂交易所、订单、OHLCV 和资产类别,避免把行情字段当成经济事实。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
把百分比和小数混用、忘记 dropna、把日频波动率直接叫作年化波动率、把相关性当因果。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
概念演示也要避免事后选择样本或只展示支持结论的时间段。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/01_financial_concepts/03_market_basics_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/01_financial_concepts/03_market_basics_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:Investments(Bodie, Kane, Marcus)。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
下一节是 04_probability_and_simulation。本节的概念会成为理解它的输入,而不是孤立的名词。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC', 'STHeiti', 'sans-serif']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
1. 主要资产类别¶
金融市场里有各种「标的」可以交易。对初学者来说,最常接触的是股票和 ETF;更进阶的是期货和期权。
先知道有哪些选择,以及它们的核心差别:
| 资产 | 通俗理解 | 特点 | 示例 |
|---|---|---|---|
| 股票 (Stock) | 买了公司的一小块所有权 | 高风险高收益,日内可多次交易 | AAPL(苹果), 贵州茅台 |
| ETF | 一篮子股票打包成一只基金 | 分散化、低费率,比买单只股票风险低 | SPY(标普500), 510050 |
| 期货 (Futures) | 约定以某价格在未来某天买/卖东西 | 有杠杆(风险倍增)、有到期日、可做空 | ES(标普500期货), IF |
| 期权 (Options) | 花少量钱买一个「选择权」,但不是义务 | 收益不对称(最多亏权利金),灵活对冲 | SPY Put(看跌期权) |
| 债券 (Bonds) | 借给政府/公司钱,收固定利息 | 风险低,收益稳定,与股票相关性低 | 国债, TLT |
| 外汇 (FX) | 两种货币之间互换 | 24小时交易、流动性极高 | EUR/USD(欧元兑美元) |
| 加密货币 | 去中心化数字资产 | 极高波动率、永不休市(7×24h) | BTC, ETH |
初学者建议: 先从股票和 ETF 开始,规则最清晰、监管最完善。期货和期权有杠杆,搞懂之前不要碰。
import yfinance as yf
import mplfinance as mpf
import pandas as pd
ticker = 'AAPL'
# 下载数据时直接禁用多级索引
df = yf.download(ticker, period='60d', progress=False, multi_level_index=False)
if isinstance(df.columns, pd.MultiIndex):
# 找到包含 'Open', 'High' 等关键词的那一层级
df.columns = df.columns.get_level_values(0 if 'Open' in df.columns.get_level_values(0) else 1)
print("当前列名:", df.columns.tolist())
# 确保索引是日期格式(mplfinance 的硬性要求)
df.index = pd.to_datetime(df.index)
df_plot = df.tail(30)
# (红涨绿跌)
mc = mpf.make_marketcolors(up='red', down='green', edge='inherit', wick='inherit', volume='inherit')
my_style = mpf.make_mpf_style(base_mpf_style='binance', marketcolors=mc)
mpf.plot(df_plot,
type='candle',
style=my_style,
title=f'\n{ticker} Analysis',
ylabel='Price (USD)',
volume=True,
mav=(5, 10),
figsize=(12, 8))
当前列名: ['Close', 'High', 'Low', 'Open', 'Volume']
# 默认使用固定样本,保证课程与 CI 不依赖实时接口。
USE_OFFLINE_DATA = True
if USE_OFFLINE_DATA:
from pathlib import Path
import sys
docs_root = next(parent for parent in [Path.cwd(), *Path.cwd().parents]
if (parent / 'utils' / 'offline_data.py').exists())
sys.path.insert(0, str(docs_root))
from utils.offline_data import configure_yfinance_offline
configure_yfinance_offline(yf)
else:
print('在线下载为扩展步骤;请核对数据字段、时区和下载日期。')
3. 订单类型¶
为什么下单方式很重要?¶
想象你在农贸市场买苹果:
- 市价单(Market Order):优先立即成交,不指定成交价格;在流动性不足或价格快速变化时可能产生较大滑点
- 限价单(Limit Order):「老板,苹果 3.5 元/斤我就买,贵了我不买」——价格有保障,但可能买不到(价格没跌到那里)
- 止损单(Stop Order):价格达到触发条件后转换为市价单或限价单;触发价不等于最终成交价
| 订单类型 | 通俗理解 | 优点 | 缺点 |
|---|---|---|---|
| 市价单 (Market Order) | 按当前可得对手方报价成交 | 提高立即成交概率 | 不保证成交价格或完整成交,高波动时滑点较大 |
| 限价单 (Limit Order) | 「这个价格成交,否则不买」 | 控制价格,不会多付 | 可能等不到,错过交易机会 |
| 止损单 (Stop Order) | 「跌破某价格就触发,马上卖」 | 自动保护,控制最大损失 | 触发后变市价单,极端行情滑点大 |
| 止损限价单 | 止损触发后变限价单 | 结合两者 | 极端行情可能根本无法成交 |
量化交易的实践: 回测时通常用收盘价成交(理想化),实盘时要考虑滑点(Slippage),实际成交价会比预想的差一点。
# 模拟交易成本对策略的影响
np.random.seed(42)
n_trades = 200
# 假设每笔交易有 55% 的胜率,盈亏比 1:1
win_rate = 0.55
win_amount = 0.01 # 每次盈利 1%
loss_amount = -0.01 # 每次亏损 1%
outcomes = np.random.choice([win_amount, loss_amount],
size=n_trades, p=[win_rate, 1 - win_rate])
# 不同手续费率
commission_rates = [0, 0.001, 0.002, 0.005]
labels = ['无成本(理想回测)', '0.1%(低佣金)', '0.2%(常见水平)', '0.5%(偏高,会亏钱)']
fig, ax = plt.subplots(figsize=(12, 5))
for rate, label in zip(commission_rates, labels):
net = outcomes - rate # 每笔扣除手续费
cum = (1 + net).cumprod() * 10000
ax.plot(cum, label=label, linewidth=1.5)
ax.axhline(10000, color='gray', linestyle='--', alpha=0.5)
ax.set_title(f'200笔交易 × 胜率{win_rate:.0%} × 盈亏比1:1:交易成本的侵蚀', fontsize=13)
ax.set_xlabel('交易次数')
ax.set_ylabel('资产价值')
ax.legend()
ax.grid(alpha=0.3)
plt.tight_layout()
plt.show()
print(' 结论:在单笔毛收益不变时,交易次数增加会提高累计手续费。')
print(' 回测应使用与市场、资产和交易规模一致的手续费假设,并报告成本敏感性。')
结论:在单笔毛收益不变时,交易次数增加会提高累计手续费。 回测应使用与市场、资产和交易规模一致的手续费假设,并报告成本敏感性。
练习¶
- 观察 K 线图中的长上影线和长下影线分别意味着什么样的多空博弈?
- 将上方模拟中的
win_rate改为 0.50,观察不同手续费下的结果。在其他条件不变且期望毛收益为零时,正交易成本会使期望净收益为负。 - 思考:如果一个策略每天交易 10 次,手续费 0.1%,需要多高的日胜率才能覆盖成本?(提示:每天成本 = 10 × 0.2% = 2%,每笔需要净盈利 0.2% 才能回本)
下一模块 → ../02_data/01_data_sources.ipynb