五 量化交易全流程( 七 )


我们使用talib来计算移动均线 , 也就是SMA() , 代码如下:
import talib as ta# 两条均线的参数L1 = 3L2 = 7# 使用talib计算移动均线df['ma1'] = ta.SMA(df.close.values, timeperiod=L1)df['ma2'] = ta.SMA(df.close.values, timeperiod=L2)
import talib as ta# 两条均线的参数L1 = 3L2 = 7# 使用talib计算移动均线df['ma1'] = ta.SMA(df.close.values, timeperiod=L1)df['ma2'] = ta.SMA(df.close.values, timeperiod=L2)
根据移动均线ma1、ma2来计算趋势值 , 用0代表没有趋势 , 1代表多头趋势 , -1代表空头趋势 , 计算趋势代码如下:
# 定义多头空头趋势con_long = df['ma1'] > df['ma2']con_short = df['ma1'] < df['ma2']# 计算趋势 。0代表没有趋势 , 1代表多头趋势 , -1代表空头趋势df['trend'] = 0df.loc[con_long,'trend'] = 1df.loc[con_short,'trend'] = -1
现在可以直接计算每天收盘后的仓位了 。这里假设出信号后 , 第二天开盘再交易 , 因为出信号的时候 , 已经收盘了 , 这个时候是无法交易的 , 所以仓位要比趋势滞后一天 。计算仓位代码如下:
# 假设出现信号后 , 第二天开盘进行交易 , 每次开仓1手(即100股)df['pos'] = 100 * df['trend'].shift(1)
现在可以通过仓位来计算每天的盈亏了 。这里有一点需要注意的是 , 今天新开的仓位 , 与从昨天继承的旧仓位 , 盈亏是不一样的 。因为今天的新仓位是以开盘价为起点 , 收盘价为终点 , 所以盈亏是当天的收盘价减开盘价 。而从昨天继承的旧仓位 , 是以昨天的收盘价为起点 , 以今天的收盘价为终点 , 所以盈亏是今天的收盘价减去昨天的开盘价 。所以这两种情况应分开来计算盈亏 , 计算盈亏代码如下:
# 计算旧仓位和新仓位df['new_pos'] = df['pos'] - df['pos'].shift(1)df['old_pos'] = df['pos'] - df['new_pos']
假设我们都能以开盘价成交 , 即开仓价就是开盘价 open , 代码如下:
# 把开盘价作为交易价格df['entry_p'] = df['open']
分别计算两种仓位的盈亏值 , 代码如下:
# 计算旧仓位的盈利和新仓位的盈利df['p&l_new'] = (df['close']-df['entry_p']) * df['new_pos']df['p&l_old'] = (df['close']-df['close'].shift(1)) * df['old_pos']# 每日盈亏由两部分组成df['p&l'] = df['p&l_new'] + df['p&l_old']
将每日的盈亏累加在一起 , 再加上初始资本 , 就可以得到资本曲线(如图所示),代码如下:
# 计算累计盈亏df['p&l_cum']=df['p&l'].cumsum()# 计算净值曲线 , 假设初始资金是1000ini_cap=1000000df['capital'] = df['p&l_cum'] + ini_capdf['net_value'] = df['capital'] / ini_cap# 绘制净值曲线df = df.set_index('date')df.plot(figsize=(12,6), y=['net_value'])
最后盈亏状况如图:
下面是完整代码:(拉取数据——画蜡烛图——画净收益图)
import mysql.connectorimport pymysqlimport pandas as pdimport numpy as npimport talib as tafrom mpl_finance import candlestick_ohlcimport matplotlib as mplimport matplotlib.pyplot as pltimport matplotlib.dates as mdatesimport datetimempl.rcParams['font.sans-serif'] = ['SimHei']# 指定默认字体mpl.rcParams['axes.unicode_minus'] = False# 解决保存图像是负号'-'显示为方块的问题class StockData:def __init__(self ,L1, L2):self.host = '127.0.0.1'self.user = 'root'self.password='152617'self.port= 3306 self.db='stock_info'self.L1 = L1# 均线策略的两个参数self.L2 = L2def data_convert(self, sql_data):# 拉取数据库对数据进行转换sql_data["open"] = sql_data['open'].astype(float)sql_data["high"] = sql_data['high'].astype(float)sql_data["low"] = sql_data['low'].astype(float)sql_data["close"] = sql_data['close'].astype(float)sql_data["volume"] = sql_data['volume'].astype(float)sql_data = http://www.kingceram.com/post/sql_data.dropna()return sql_datadef convert_date(self, candlestick_data):# 蜡烛图日期格式转换data_price = candlestick_data# 选取日期、高开低收价格、成交量数据data_price['date'] = data_price['date'].astype(str)# 先将日期转为字符串data_price.set_index('date', inplace=True)# 将日期作为索引# 将日期格式转为 candlestick_ohlc 可识别的数值data_price['date'] = list(map(lambda x:mdates.date2num(datetime.datetime.strptime(x,'%Y-%m-%d')),data_price.index.tolist()))return data_pricedef get_stock_data(self, stock_code, start_date, end_date):# 拉取数据stock_dataconn = pymysql.connect(host=self.host, user=self.user, password=self.password, port=self.port, db=self.db, charset='utf8')cur = conn.cursor()sql = f"select * from `stocks` where stock_code = {stock_code} and date > {start_date} and date