金融/证券/会计/公司治理等方向的编程课程
今天我们介绍金融证券经常用到的一个场景 量化交易
量化交易涉及到的内容非常多,有不少知识理解起来很有挑战
我们还是以核心概念理解为主,暂时忽略复杂而不关键的部分细节
先看一下最终结果,赚了不少钱,哈哈
我们先在上交所 http://www.sse.com.cn/market/price/trends/ 的页面上查看
通过打开控制台,刷新页面后,注意到了真实的数据地址, 如下所示
http://yunhq.sse.com.cn:32041/v1/sh1/dayk/000001?callback=jQuery111205234775875526079_1542185571865&select=date%2Copen%2Chigh%2Clow%2Cclose%2Cvolume&begin=-2000&end=-1&_=1542185571881
这里面重要数据在kline
里面,简单列一些如下(去掉了外层的jQuery,只保留json数据)
{
begin: 6525,
code: "000001",
end: 6824,
kline: [
[20170823, 3283.796, 3299.457, 3274.44, 3287.704, 179832208],
[20170824, 3287.959, 3297.988, 3266.358, 3271.511, 163468937],
...
]
}
每一项对应的意义分别是
# 引入相关的包
import json # 处理json格式的数据
import requests # 发起网络请求
def run():
''' 主程序, 用来调度各个重要流程 '''
kline = load_sse()
print(kline)
def load_sse():
''' 获取上交所的上证指数K线, 最近2000个交易日数据 '''
response = requests.get(
'http://yunhq.sse.com.cn:32041/v1/sh1/dayk/000001?callback=jQuery111205234775875526079_1542185571865&select=date%2Copen%2Chigh%2Clow%2Cclose%2Cvolume&begin=-2000&end=-1&_=1542185571881',
headers={'Referer': 'http://www.sse.com.cn/market/price/trends/'}
)
# 针对结果进行格式处理
json_str = response.text[42:-1]
data = json.loads(json_str)
return data['kline']
if __name__ == '__main__':
run()
有好几点需要注意
网络请求的知识,前几节已经讲的比较多了,如果有问题可以翻看一下前面的内容
import pandas as pd # 优秀的分析数据工具
def init_df(kline):
''' 根据K线数据,创建含有日期与收盘价的矩阵 '''
df = pd.DataFrame({})
df['date'] = [x[0] for x in kline]
df['close'] = [x[2] for x in kline]
return df
我们引入了pandas库,他非常强大,用于处理数据
我们先看 [x[0] for x in kline]
的作用
这是快速提取数据某些元素的方式, kline里面有很多条数据,我们把每条数据的第一项,也就是交易日期提取出来,成为列表
第三项是收盘价,也是我们这次分析最重要的数据
这样创建的data_frame
简称df
就是一个矩阵,可以理解成如下内容
date close
0 20170101 777
1 20170102 888
通过df,我们可以很方便操作里面的数据
def strategy(df):
''' 根据价格计算平均值,然后把策略记录下来 '''
# 连续15天数据,计算平均值,作为当天的平均价格指标
window_size = 15
df['avg'] = df['close'].rolling(window_size).apply(lambda x: sum(x) / len(x), raw=True)
def avg_buy(x):
''' 做多策略 '''
# 这里应该是追涨杀跌的策略,我在视频里说的类似低建高平策略,视频说的是错的
# 写代码的时候改过几次,后来没改注释,导致视频说错了
# 不过具体策略不影响我们程序的学习,大家注意到就好了
min_percent = 0.995
max_percent = 1.005
# 追涨,当我们的价格超过了均线一定程度时
if (x[1] / x[0]) < min_percent:
return 'open buy'
# 杀跌,当我们的价格低于均线一定程度时
if (x[1] / x[0]) > max_percent:
return 'close buy'
# 其他情况不操作
return 'wait'
df['action'] = df[['close', 'avg']].apply(avg_buy, axis=1)
return df
rolling那一行的整体意思是计算最近15天的平均收盘价,我们拆分来看
lambda x: sum(x) / len(x)
是一个简写的函数,这里的x是指一系列数字,这个函数用于计算平均值, sum是求和,len是求总数df['close'].rolling(window_size)
是把收盘价按连续15个的方式,依次进行处理,类似很多人排队从一个大窗口走过, 这个窗口同时可以看到15个人,每走一个人我们就拍一下照片,得到了一个相册apply(求平均函数, raw=True)
的意思是把每张照片交给求平均函数,每次产生一个平均数,最终产生一组结果这样就得到了每天对应的15日平均收盘价,保存到 df['avg']
方便后面使用
avg_buy(x)
也是一个函数,根据传入的数据进行处理,而df[['close', 'avg']].apply(avg_buy, axis=1)
正是把每天的收盘价与平均价传入了函数中, 这里的axis=1
的原理非常复杂,我们只要知道,写上这个就能按照一天一天的交易日处理就行。
如果你对axis详细原理很感兴趣,可以单独找我,涉及到高级维度方面的数学知识,我已经弄明白了,愿意讨论分析
在avg_buy
里面,x[0]
表示当天收盘价, x[1]
表示当天平均价
我们把他们进行比较,如果收盘价超过均线一定比例,我们就买股票,追涨
如果收盘价跌过均线一定比例,就卖掉股票,杀跌
请注意视频中的说明是错误的,文字版这里的解释才是正确的
理论上是不可能收盘时买股票的,所以我们这里的收盘价可以认为是接近收盘的5分钟内的价格,这时候买入跟收盘价比较接近。我们所有统计也基于这样的收盘价,并不影响策略与收益
我在代码注释里用的术语是 开多仓 开空仓 平多仓 平空仓
这样更接近通用的表达,因为A股虽然不能做空,但其他金融产品是可以的,策略也是类似
股市有风险 投资需谨慎
这个策略其实是非常简单的,只是用来演示,请不要根据这个进行投资,无法保证任何收益,切记
def backtest(df):
''' 回归测试 '''
global shares, cash
amount = 1000000
shares = 0
cash = amount
def run_strategy(row):
''' 把每天的数据执行策略 '''
global shares, cash
action = row['action']
close = row['close']
# 资产 = 现金 + 股票价值
liquidate = cash + shares * close
message = 'nothing'
# 策略要求开仓做多,而且当前空仓时,做多
if action == 'open buy' and shares == 0:
shares = int(cash / close)
cash -= shares * close
message = 'open buy ' + str(shares)
# 策略要求平仓,而且当前有仓时,平掉
if action == 'close buy' and shares > 0:
message = 'close buy ' + str(shares)
cash += shares * close
shares = 0
return [message, shares, cash, liquidate]
rows = df[['close', 'action']].apply(run_strategy, axis=1)
df['message'], df['shares'], df['cash'], df['liquidate'] = zip(*rows)
return df
这里的作用,就是执行策略,计算持股,资产,现金等数据
流程介绍(不含复杂语法)
share:持股数量
cash:当前现金
amount:初始资金100万
run_strategy
接收一天的数据, 根据收盘价与持股数量及现金,计算最新的资产,因为买入或者卖出都不影响当天的资产了,所以可以先计算好apply
后得到了多行数据message:交易信息
及其他几项数据复杂点解释,可以不用深究
df
是一列一列的,所以通过zip(*rows)
进行一次转置,如果你学过线性代数的话,应该比较容易想象到,就是行与列的转换axis=1
也是按行处理的意思,跟上一个函数类似import numpy as np # 处理数字的工具
import matplotlib.pyplot as plt # 强大的画图工具
def draw(df):
''' 画图 '''
# 创建画板
fig = plt.figure(figsize=(10, 5))
# 准备横坐标
count = df.count()['close']
index = np.arange(count)
# 设置横坐标的刻度与显示标签
limit = 200
plt.xticks(index[::limit], df['date'][::limit])
# 收盘价与资产的两套坐标系
ax_close = plt.gca()
ax_liquidate = ax_close.twinx()
# 画收盘价曲线
ax_close.set(xlabel='Date', ylabel='close')
l_close, = ax_close.plot(index, df['close'], 'black', label='close')
# 画资产曲线
ax_liquidate.set(ylabel = 'liquidate')
l_liquidate, = ax_liquidate.plot(index, df['liquidate'], 'blue', label='liquidate')
# 给两条线都提供一个图例说明
plt.legend(handles=[l_close, l_liquidate])
plt.show()
其实不用画图,我们的量化交易也做完了,画出图可以让我们更好地分析
本课不详细介绍画图流程了,希望大家把重点放在数据处理上,后面我们会详细讲画图
注意, 我在视频教程中用的是旧版,最新的我已经加入了均线与买入点,卖出点,请注意
df.to_csv('./result.csv', index = False)
pandas用来处理数据很方便,导出到csv也就是这一行代码
可以在你的python文件同目录下找到csv
股市有风险 投资需谨慎
虽然这个示例程序的演示效果赚了不少钱,但一定不要用于真正的投资,这一点再次强调
另外,我们做量化交易的时候,其实需要经常调整参数,好的策略也要配合好的参数,本例中的15天均值,0.995 1.005
等策略用到的比例,都是很重要的参数,需要不断调试才能确定
至此,我们的最简单版本量化交易程序就实现了
还有很多功能可以加进来, 也算是策略的优化
当然,具体策略取决于你对证券的理解,我列一些思路
如果你有好的建议,我们可以讨论更多策略
# lesson5.py
# 最简单的量化交易及可视化图
# 引入相关的包
# 如果本地环境没有,需要先安装
# pip install pandas
# pip install numpy
# pip install matplotlib
import json # 处理json格式的数据
import requests # 发起网络请求
import pandas as pd # 优秀的分析数据工具
import numpy as np # 处理数字的工具
import matplotlib.pyplot as plt # 强大的画图工具
def run():
''' 主程序, 用来调度各个重要流程 '''
kline = load_sse()
df = init_df(kline)
df = strategy(df)
df = backtest(df)
draw(df)
df.to_csv('./result.csv', index = False)
def load_sse():
''' 获取上交所的上证指数K线, 最近2000个交易日数据 '''
response = requests.get(
'http://yunhq.sse.com.cn:32041/v1/sh1/dayk/000001?callback=jQuery111205234775875526079_1542185571865&select=date%2Copen%2Chigh%2Clow%2Cclose%2Cvolume&begin=-2000&end=-1&_=1542185571881',
headers={'Referer': 'http://www.sse.com.cn/market/price/trends/'}
)
# 针对结果进行格式处理
json_str = response.text[42:-1]
data = json.loads(json_str)
return data['kline']
def init_df(kline):
''' 根据K线数据,创建含有日期与收盘价的矩阵 '''
df = pd.DataFrame({})
df['date'] = [x[0] for x in kline]
df['close'] = [x[2] for x in kline]
return df
def strategy(df):
''' 根据价格计算平均值,然后把策略记录下来 '''
# 连续15天数据,计算平均值,作为当天的平均价格指标
window_size = 15
df['avg'] = df['close'].rolling(window_size).apply(lambda x: sum(x) / len(x), raw=True)
def avg_buy(x):
''' 做多策略 '''
# 这里应该是追涨杀跌的策略,我在视频里说的类似低建高平策略,视频说的是错的
# 写代码的时候改过几次,后来没改注释,导致视频说错了
# 不过具体策略不影响我们程序的学习,大家注意到就好了
min_percent = 0.995
max_percent = 1.005
# 追涨,当我们的价格超过了均线一定程度时
if (x[1] / x[0]) < min_percent:
return 'open buy'
# 杀跌,当我们的价格低于均线一定程度时
if (x[1] / x[0]) > max_percent:
return 'close buy'
# 其他情况不操作
return 'wait'
df['action'] = df[['close', 'avg']].apply(avg_buy, axis=1)
return df
def backtest(df):
''' 回归测试 '''
global shares, cash
amount = 1000000
shares = 0
cash = amount
def run_strategy(row):
''' 把每天的数据执行策略 '''
global shares, cash
action = row['action']
close = row['close']
# 资产 = 现金 + 股票价值
liquidate = cash + shares * close
message = 'nothing'
# 策略要求开仓做多,而且当前空仓时,做多
if action == 'open buy' and shares == 0:
shares = int(cash / close)
cash -= shares * close
message = 'open buy ' + str(shares)
# 策略要求平仓,而且当前有仓时,平掉
if action == 'close buy' and shares > 0:
message = 'close buy ' + str(shares)
cash += shares * close
shares = 0
return [message, shares, cash, liquidate]
rows = df[['close', 'action']].apply(run_strategy, axis=1)
df['message'], df['shares'], df['cash'], df['liquidate'] = zip(*rows)
return df
def draw(df):
''' 画图 '''
# 创建画板
fig = plt.figure(figsize=(10, 5))
# 准备横坐标
count = df.count()['close']
index = np.arange(count)
df['index'] = index
# 设置横坐标的刻度与显示标签
limit = 200
plt.xticks(index[::limit], df['date'][::limit])
# 收盘价与资产的两套坐标系
ax_close = plt.gca()
ax_liquidate = ax_close.twinx()
# 画收盘价曲线
ax_close.set(xlabel='Date', ylabel='close')
l_close, = ax_close.plot(index, df['close'], 'black', label='close')
l_avg, = ax_close.plot(index, df['avg'], 'pink', label='avg')
# 画资产曲线
ax_liquidate.set(ylabel = 'liquidate')
l_liquidate, = ax_liquidate.plot(index, df['liquidate'], 'blue', label='liquidate')
def drawAction(row):
if row['message'] == 'nothing':
return
color = ''
marker = 'o'
size = 12
if row['action'] == 'open buy':
color='r'
if row['action'] == 'close buy':
color='g'
ax_close.scatter(row['index'], row['close'], s=size, color=color, zorder=2, marker=marker)
df[['index', 'action', 'message', 'close']].apply(drawAction, axis=1)
# 给两条线都提供一个图例说明
plt.legend(handles=[l_close, l_avg, l_liquidate])
plt.show()
if __name__ == '__main__':
run()
我近期一边整理免费课程,也会推出更多的免费视频,方便大家结合查看学习
如果你对我的课程感兴趣,欢迎与我联系,提供一对一教学,也可以帮助实现特定程序
了解清楚目标后,第一次可以先听课后付款
如果有概念没有理解清楚,随时可以询问,不再收费