乐趣区

【译】Python 金融:算法交易 (1)基础入门

本文翻译自 2018 年最热门的 Python 金融教程 Python For Finance: Algorithmic Trading。
这篇 Python 金融教程向您介绍算法交易等内容。技术已成为金融领域的一项资产:金融机构已不仅仅是单纯的金融机构了,它正向着技术公司演进。除了技术带来的创新速度和竞争优势以外,金融交易的速度、频率和大数据量,使得金融机构对技术的关注度日益提高,技术确已成为金融业的主要推动力。在最热门的金融编程语言中,有 R 语言、Python,还有 C ++、C# 和 Java。在本教程中,你将学习如何开始使用 Python 进行金融分析,其内容如下:
Python 金融入门必备基础知识:对于那些刚接触金融的人,首先要了解有关股票和交易策略的知识,什么是时间序列数据,以及如何设置工作空间。
常见的金融分析方法:介绍时间序列数据和常见的金融分析方法,比如使用 Python 包 Pandas 进行移动窗口、波动率计算等等。
简单的动量策略开发:首先逐步完成开发过程,然后开始编写简单的算法交易策略。
回溯测试策略:使用 Pandas、zipline 和 Quantopian 回溯测试制定的交易策略。
评估交易策略:优化策略使之获得更好的表现,并最终评估策略的性能和稳健性。
从这里下载本教程的 Jupyter notebook 代码。Python 金融入门在进入交易策略学习之前,最好先来了解基础知识。本教程的第一部分将着重于介绍入门所需的 Python 基础知识。但这并不意味着你将完全从零开始:你至少需要完成 DataCamp 的免费课程 Intro to Python for Data Science course,从而学会如何使用 Python 列表、包和 NumPy。此外,尽管不是必需,但仍希望你能了解 Pandas 的基本知识,这是众所周知的 Python 数据处理包。然后我建议你参加 DataCamp 的课程 Intro to Python for Finance,学习 Python 金融的基本知识。如果你还想要把新学的 Python 数据科学技能用于真实的金融数据,可以考虑参加 Importing and Managing Financial Data in Python 课程。股票和交易当一家公司想要扩张或承接新项目时,它可以发行股票来募集资金。股票代表在公司所有权中占的份额,并以金钱兑换的形式发放。股票可以买入和卖出:买卖双方交易现存的曾发行的股票。卖出股票的价格独立于公司业绩,股价反应的是供需关系。这意味着,每当一只股票因为成功、受欢迎等原因被认为是‘值得的’,那么它的股价就会上涨。请注意,股票和债券并不完全相同。债券是公司通过借贷的形式筹集资金,无论是作为银行贷款还是发行债券。正如你刚才读到的那样,买卖或者交易是我们谈论股票所不可避免的,但当然不仅限于此:交易是一种买卖资产的行为,可以是像股票、债券这样的金融证券,或者是如黄金、石油这样的有形资产。股票交易是这样一个过程:买入股票就是将现金转换成公司所有权的股份,反之,卖出股票就是将股份换回现金。这一切交易都希望能从中获取利润。现在,为了获得丰厚的利润,在市场上要么做多,要么做空:要么你认为股价会上涨并在将来的高价位上卖出股票,要么卖出你的股票,期望在低价位上买回而盈利。当你按照固定的计划在市场上做多或做空时,你就有了一个交易策略。开发交易策略需要经历若干阶段,就如同构建机器学习模型那样:首先制定一个策略,并以能在电脑上测试的形式实现它,然后进行初步测试或回溯测试,优化你的策略,最后评估策略的性能和稳健性。交易策略通常通过回溯测试来验证:根据策略制定的规则,使用历史数据,重构过去可能发生的交易。通过这种方式,你能够了解策略的有效性,并在将其应用于真实市场前,把它作为策略优化的起点。当然,这通通依赖于以下信念:任何在过去表现良好的策略,也会在将来取得好的成绩,同样,任何在过去表现差劲的策略,在将来也不可能有好的表现。时间序列数据时间序列是在连续的、等间距的时间点上取得的一系列数据点。在投资中,时间序列跟踪选定数据点(如股票价格)的变动,它是在特定的时间跨度内,等间隔地记录数据点。如果你还是疑惑它到底长什么样子,来看看下面的例子:

x 轴代表日期,y 轴代表价格。在上图中,“连续的、等间距的时间点”就是 x 轴上以 14 天为间隔的时间刻度:注意 3/7/2005 和它的下一个点 3/31/2005 之间的差值,以及 4/5/2005 和 4/19/2005 等等。然而,你常见的股票数据不仅仅只有时间和价格这两列,大部分时候有 5 列,包括时间、开盘价、最高价、最低价和收盘价。这意味着,如果时间间隔设置为天,你将会得到某只股票那一天的开盘价、收盘价,以及最高、最低价。现在,你具备了学习完本教程的基本概念。接下来这些概念马上又会出现,并在后续的学习中变得更深入。设置工作空间准备工作空间是一项简单的工作:基本上只需确保你的系统运行了 Python 和集成开发环境(IDE)。然而仍有些办法可以让你更容易地开始。以 Anaconda 为例,它是 Python 和 R 的高性能发行版本,包含了超过 100 个最受欢迎的 Python、R 和 Scala 数据科学包。此外,安装 Anaconda 将使你能通过 conda 轻松地安装超过 720 个包,这里 conda 是集成在 Anaconda 中的著名管理器,用于管理包、依赖项和环境。除此之外,Anaconda 还包含了 Jupyter Notebook 和 Spyder 集成开发环境。听起来不错,对吧?你可以从这里下载安装 Anaconda,同时不要忘了通过这篇教程 Jupyter Notebook Tutorial: The Definitive Guide 查看如何设置 Jupyter Notebook。当然,Anaconda 并不是你唯一的选择:你可以尝试收费的 Canopy Python 发行版本,或者是 Quant Platform。Quant Platform 相比于 Jupyter 或 Spyder IDE 更有优势,因为它向你提供了在浏览器中进行金融分析所需的一切。通过 Quant Platform,你可以访问基于图形用户界面的金融工程,进行基于 Python 的交互式金融分析,以及使用你自己的 Python 分析库。更重要的是,你还可以通过论坛与小伙伴们讨论问题和解决方案。Python 金融基础:Pandas 当你使用 Python 进行金融分析,会经常使用到数据处理包 Pandas。但当你深入时,也会涉及诸如 NumPy、SciPy、Matplotlib 这些包。现在,让我们只关注 Pandas 并使用它分析时间序列数据。本节将介绍如何使用 Pandas 导入、探索以及处理数据。最重要的是,你将了解如何对导入的数据进行常见的金融分析。将金融数据导入 Pythonpandas-datareader 包允许从谷歌、雅虎财经、世界银行等数据源中读取数据。如果你想知道此函数提供的最新的数据源列表,可参阅此文档。在本教程中,你将使用 pandas-datareader 包读取雅虎财经的数据。首先请确保安装了此包的最新版本,可通过 pip with pip install pandas-datareader 命令实现。提示:如果你想安装最新的开发版本,或者遇到任何问题,都可以在这里查看安装说明。
import pandas_datareader as pdr
import datetime
aapl = pdr.get_data_yahoo(‘AAPL’,
start=datetime.datetime(2006, 10, 1),
end=datetime.datetime(2012, 1, 1))

请注意雅虎 API 端口最近已更改,如果你已经开始使用该库,则需要安装一个临时的修复补丁,直到该补丁被合并到主干中才能使用 pandas-datareader 包从雅虎财经获取数据。在你开始之前,请确保查阅了此问题。无需担心,在本教程中数据已提前为你加载好,所以你在使用 Pandas 学习 Python 金融时不会遇到任何问题。即便 pandas-datareader 提供了向 Python 导入数据的许多选择,它也不是唯一能获取金融数据的包:比如 Quandl 库,就可以获取谷歌金融数据:
import quandl
aapl = quandl.get(“WIKI/AAPL”, start_date=”2006-10-01″, end_date=”2012-01-01″)

更多关于如何使用 Quandl 获取金融数据的信息,可参考此网页。最后,如果你已经在金融领域工作了一段时间,你可能知道最常用的数据处理工具是 Excel。在这种情况下,你需要了解如何将 Excel 集成到 Python 中。查看 DataCamp 的教程 Python Excel Tutorial: The Definitive Guide,获取更多相关知识。使用时间序列数据当你最终将数据导入工作空间后,首先要做的事情就是将手弄脏(指清洗、整理数据——译者注)。然而,现在要处理的是时间序列数据,这看起来可能不那么简单,因为索引是日期时间数据。即便如此也不用担心。让我们一步步来,首先使用函数来探索数据。如果你先前已经有 R 或者 Pandas 的编程经验,那么对于这些函数就不陌生了。没关系,你将发现这很容易!如上述代码块所示,使用 pandas_datareader 将数据导入工作空间。所得到的对象 aapl 是数据框,一个二维数据结构,其每一列可以是不同类型的数据。现在当你手头有了一个常规的数据框时,首先要做的事情之一是运行 head() 和 tail() 函数来查看开始和结束的几行数据。幸运的是,当你使用时间序列数据时,这一点并不会改变。提示:请务必使用 describe() 函数来查看数据的统计摘要信息。
# 查看 `aapl` 数据的前几行
print(aapl.head())

High Low Open Close Volume Adj Close
Date
2006-10-02 10.838572 10.614285 10.728572 10.694285 178159800 7.161565
2006-10-03 10.707143 10.455714 10.635715 10.582857 197677200 7.086947
2006-10-04 10.780000 10.451428 10.585714 10.768572 207270700 7.211311
2006-10-05 10.880000 10.590000 10.647142 10.690000 170970800 7.158698
2006-10-06 10.720000 10.544286 10.631429 10.602858 116739700 7.100338

# 查看 `aapl` 数据的最后几行
print(aapl.tail())

High Low Open Close Volume Adj Close
Date
2011-12-23 57.655716 57.070000 57.098572 57.618572 67349800 38.585011
2011-12-27 58.441429 57.574287 57.585712 58.075714 66269000 38.891148
2011-12-28 58.321430 57.334286 58.127144 57.520000 57165500 38.519012
2011-12-29 57.950001 57.215714 57.628571 57.874287 53994500 38.756252
2011-12-30 58.040001 57.641430 57.644287 57.857143 44915500 38.744774

# 查看 `aapl` 数据的统计信息
print(aapl.describe())

High Low Open Close Volume \
count 1323.000000 1323.000000 1323.000000 1323.000000 1.323000e+03
mean 29.237566 28.507684 28.901012 28.889151 1.882896e+08
std 14.199012 14.029758 14.123131 14.119734 1.027007e+08
min 10.568571 10.371428 10.488571 10.461429 3.937360e+07
25% 17.752857 17.182143 17.457857 17.431429 1.122037e+08
50% 25.642857 24.725714 25.260000 25.120001 1.629866e+08
75% 39.132858 38.351429 38.777143 38.699999 2.316230e+08
max 60.957142 59.427143 60.251427 60.320000 8.432424e+08

Adj Close
count 1323.000000
mean 19.345990
std 9.455460
min 7.005628
25% 11.673178
50% 16.821934
75% 25.915947
max 40.394054

正如介绍中所述,这一数据包含四列,分别是苹果股票每天的开盘价、收盘价,以及最高、最低价。此外还有另外两列,成交量和调整的收盘价。成交量(Volume)这一列记录每天交易的股票数量。调整的收盘价(Adj Close)是将当天的收盘价进行调整,以包含第二天开盘前的任何举措。可以使用此列检查或深入分析历史回报情况。注意索引或行标签如何包含日期,以及列或列标签如何包含数值。提示:可以使用 Pandas 中的 to_csv() 函数将该数据保存为 csv 文档,并且通过 read_csv() 函数将数据重新读回到 Python 中。当因雅虎 API 端口被更改而无法获取数据时,这一方法是非常方便的。
import pandas as pd
aapl.to_csv(‘data/aapl_ohlc.csv’) # 注意事先要在当前工作目录下创建 data 文件夹
df = pd.read_csv(‘data/aapl_ohlc.csv’, header=0, index_col=’Date’, parse_dates=True)

现在你已经简单地查看了数据的前几行以及一些统计信息,是时候更深入一些了。一种方法是检查索引和列,并选择某列的最后 10 行数据。后者被称为构造子集,因为你选择了数据中的一小部分。构造子集得到一个序列(Series),它是一个可以存储任何数据类型的一维数组。请记住数据框结构是一个二维数组,它的每一列可以存储不同的数据类型。让我们在接下来的练习中检查上面所说的一切。首先,使用 index 和 columns 属性来查看数据的索引和列。接着选取数据集中 Close 列的最后 10 个观测量。使用方括号 [] 挑出最后 10 个数值。你也许从其他编程语言(比如 R 语言)中获知了这一构造子集的方法。最后,将后者指定给变量 ts,并使用 type() 函数查看 ts 的类型。
# 查看索引
aapl.index

DatetimeIndex([‘2006-10-02’, ‘2006-10-03’, ‘2006-10-04’, ‘2006-10-05’,
‘2006-10-06’, ‘2006-10-09’, ‘2006-10-10’, ‘2006-10-11’,
‘2006-10-12’, ‘2006-10-13’,

‘2011-12-16’, ‘2011-12-19’, ‘2011-12-20’, ‘2011-12-21’,
‘2011-12-22’, ‘2011-12-23’, ‘2011-12-27’, ‘2011-12-28’,
‘2011-12-29’, ‘2011-12-30′],
dtype=’datetime64[ns]’, name=’Date’, length=1323, freq=None)

# 查看列
aapl.columns

Index([‘High’, ‘Low’, ‘Open’, ‘Close’, ‘Volume’, ‘Adj Close’], dtype=’object’)

# 选择 Close 列的最后 10 个数值
ts = aapl[‘Close’][-10:]
print(ts)

Date
2011-12-16 54.431427
2011-12-19 54.601429
2011-12-20 56.564285
2011-12-21 56.635715
2011-12-22 56.935715
2011-12-23 57.618572
2011-12-27 58.075714
2011-12-28 57.520000
2011-12-29 57.874287
2011-12-30 57.857143
Name: Close, dtype: float64

# 查看 ts 的类型
type(ts)

pandas.core.series.Series

方括号可以很好的对数据取子集,但它可能不是 Pandas 中最惯用的方法。这就是你还需学习 loc() 和 iloc() 函数的原因:前者用于基于标签的索引,后者用于位置索引。实际上,这意味着你可以将行标签,如 2007 和 2006-11-01 传递给 loc() 函数,而将整数 22 和 43 传递给 iloc() 函数。完成下面的练习来了解 loc() 和 iloc() 的工作方式。
查看 2006 年 11、12 月数据的前几行
print(aapl.loc[pd.Timestamp(‘2006-11-01’):pd.Timestamp(‘2006-12-31’)].head())

High Low Open Close Volume Adj Close
Date
2006-11-01 11.625714 11.194285 11.585714 11.308572 152798100 7.572930
2006-11-02 11.331429 11.214286 11.274285 11.282857 116370800 7.555712
2006-11-03 11.361428 11.112857 11.337143 11.184286 107972200 7.489699
2006-11-06 11.437143 11.204286 11.278571 11.387143 108644200 7.625546
2006-11-07 11.571428 11.447143 11.492857 11.501429 131483100 7.702079

# 查看 2007 年数据的前几行
print(aapl.loc[‘2007’].head())

High Low Open Close Volume Adj Close
Date
2007-01-03 12.368571 11.700000 12.327143 11.971429 309579900 8.016820
2007-01-04 12.278571 11.974286 12.007143 12.237143 211815100 8.194759
2007-01-05 12.314285 12.057143 12.252857 12.150000 208685400 8.136404
2007-01-08 12.361428 12.182858 12.280000 12.210000 199276700 8.176582
2007-01-09 13.282857 12.164286 12.350000 13.224286 837324600 8.855811

# 查看 2006 年 11 月数据的前几行
print(aapl.iloc[22:43].head())

High Low Open Close Volume Adj Close
Date
2006-11-01 11.625714 11.194285 11.585714 11.308572 152798100 7.572930
2006-11-02 11.331429 11.214286 11.274285 11.282857 116370800 7.555712
2006-11-03 11.361428 11.112857 11.337143 11.184286 107972200 7.489699
2006-11-06 11.437143 11.204286 11.278571 11.387143 108644200 7.625546
2006-11-07 11.571428 11.447143 11.492857 11.501429 131483100 7.702079

# 查看 2006-11-01 和 2006-12-01 两天的开盘价和收盘价
print(aapl.iloc[[22,43], [2, 3]])

Open Close
Date
2006-11-01 11.585714 11.308572
2006-12-01 13.114285 13.045714

提示:如果仔细查看子集数据,你会发现某些天的数据是缺失的;如果你更仔细地观察其模式,你会发现经常是缺失两或三天;这些天是周末或者假日,所以并不包含在你的数据中。没什么可担心的:这很正常,也无需填补缺失的日期。除了索引,你可能还想研究其他技术来更好的了解数据。你永远不知道还会发生什么。让我们尝试从数据集中采集 20 行数据样本,然后按月而不是天对数据 aapl 进行重新采样。可以使用 sample() 和 resample() 函数来实现:
# 采样 20 行数据
sample = aapl.sample(20)

# 输出 `sample`
print(sample)

High Low Open Close Volume Adj Close
Date
2011-08-15 54.995716 54.012856 54.232857 54.772858 115136000 36.679344
2007-03-19 13.078571 12.798572 12.891429 13.018572 178240300 8.718055
2007-11-28 25.799999 25.049999 25.260000 25.745714 287728000 17.240946
2010-09-17 39.708572 39.097141 39.669998 39.338570 158619300 26.343573
2010-03-11 32.214287 31.902857 31.987143 32.214287 101425100 21.572710
2007-01-18 13.158571 12.721429 13.157143 12.724286 591151400 8.520981
2007-06-05 17.527143 17.214285 17.344286 17.524286 230196400 11.735363
2011-03-04 51.470001 51.107143 51.438572 51.428570 113316700 34.439796
2009-12-15 28.215714 27.610001 27.975714 27.738571 104864900 18.575493
2006-12-15 12.745714 12.475715 12.717143 12.531428 184984800 8.391831
2007-08-27 19.237143 18.871429 19.055714 18.892857 176859900 12.651844
2009-01-16 12.054286 11.485714 12.042857 11.761429 261906400 7.876194
2007-03-01 12.615714 11.964286 12.004286 12.437143 353882200 8.328694
2010-05-05 36.877144 35.532856 36.147144 36.570000 220775800 24.489573
2010-09-08 37.770000 37.014286 37.111427 37.560001 131637800 25.152536
2009-07-16 21.145714 20.795713 20.822857 21.074286 98392700 14.112666
2007-06-06 17.721428 17.421429 17.471428 17.662857 278060300 11.828161
2009-05-05 18.980000 18.731428 18.821428 18.958570 99563800 12.695851
2011-09-28 57.677143 56.644287 57.169998 56.715714 107409400 37.980404
2007-04-19 13.035714 12.832857 12.884286 12.895715 106478400 8.635779

# 按月重新采样
monthly_aapl = aapl.resample(‘M’).mean()

# 输出 `monthly_aapl` 的前几行
print(monthly_aapl.head())

High Low Open Close Volume \
Date
2006-10-31 11.123766 10.893117 11.002922 11.017987 1.634995e+08
2006-11-30 12.314626 12.028980 12.161565 12.192109 1.647010e+08
2006-12-31 12.546500 12.205571 12.415857 12.353071 2.111349e+08
2007-01-31 12.880857 12.522572 12.722357 12.697357 3.401223e+08
2007-02-28 12.382932 12.111804 12.252105 12.246842 1.805573e+08

Adj Close
Date
2006-10-31 7.378336
2006-11-30 8.164602
2006-12-31 8.272392
2007-01-31 8.502948
2007-02-28 8.201255

非常直截了当,不是吗?resample() 函数使用频繁,因为它为时间序列的频率转换提供了精细而灵活的控制:除了指定新的时间间隔,处理缺失数据以外,还能选择对数据重新采样的方式,如上述代码所示。asfreq() 方法与之类似,不过只能实现前两项功能。提示:在 IPython 控制台中尝试代码 aapl.asfreq(“M”, method=”bfill”),并查看其输出结果。最后,在进行数据可视化和常见的金融分析这些进阶的数据探索之前,你可能已经开始计算每天开盘价和收盘价之间的差值了。在 Pandas 的帮助下,可以轻易地实现这一算数运算;只需将 aapl 数据的 Open 列数值减去该数据的 Close 列数值即可。换言之,就是从 aapl.Open 中减去 aapl.Close。将结果存入 aapl 数据框中新的一列 diff 中,另外可以使用 del 将其删除:
# 在 `aapl` 中新增一列 `diff`
aapl[‘diff’] = aapl.Open – aapl.Close

# 删除 `diff` 列
#del aapl[‘diff’]

提示:请确保注释掉最后一行代码,这样 aapl 数据框的新列不会被删除,并且你可以检查算术运算的结果!当然,了解绝对收益可帮助你知道自己是否做了一个好的投资。但是作为一名定量分析者,你可能对使用相对方法衡量股票价值更感兴趣,比如某只股票上涨或下跌的幅度。计算每日的百分比变化便是这样一种方法。现在知道这一点很好,不过也不用担心,接下来你将会越来越深入地进行了解。本节介绍了在开始预分析之前,进行数据探索的一些方法。但是仍有可提高的余地,如果你想了解更多,可阅读 Python Exploratory Data Analysis 这篇教程。可视化时间序列数据在使用 head(), tail(),索引等方法探索数据之后,你大概想要可视化时间序列数据。多亏了 Pandas 的绘图功能整合了 Matplotlib 包,使这项任务变得容易了;只要使用 plot() 函数并传递给它相关的参数即可。另外,也可以使用 grid 参数来为图片添加网格背景。让我们检查并运行以下代码,看看如何绘制这样一幅图!
# 导入 Matplotlib 包的 `pyplot` 模块,简写为 `plt`
import matplotlib.pyplot as plt

# 绘制收盘价曲线
aapl[‘Close’].plot(grid=True)

# 显示绘图
plt.show()

如果你想进一步了解 Matplotlib 包,并学习如何开始使用它,那么可以查看 DataCamp 的课程 Intermediate Python for Data Science。

退出移动版