Python和股市数据分析

Posted Python商务实践

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python和股市数据分析相关的知识,希望对你有一定的参考价值。

写在前面

上期文章,小编和大家聊了一下怎么使用python去处理股市数据,主要讲述了怎么使用python去获取股市数据、对股市数据进行可视化操作以及绘制移动平均线。然而上期文章总的来说扮演的角色是工具,之所以进行股市可视化以及绘制移动平均线,最终的目的是为了投资决策,因此本期文章,小编就接着上期文章,和大家聊一下怎么通过移动平均线去进行投资决策,小编也将这种策略称之为移动平均交叉策略。当然,这一切的实现还是会依赖于python以及相关的库,这在下面的展开中会论及到,下面正式开始。

移动平均交叉策略

移动平均交叉策略属于技术分析流派中一种典型的交易策略,这种策略曾经也曾红极一时,及至现在也是多数教科书中常常谈及的交易策略。不过在展开这种策略的介绍之前,小编觉得有必要先对交易策略做一个粗略地介绍。

所谓股市交易策略,就是指一套买卖股票的相关规则集合构成。一般来说,一个交易策略主要包括四部分:选股、择时、仓位管理与风险控制。小编这里大致地介绍一下这四个方面。

  • 选股 —— 选股主要包括两个环节,第一个环节是确定你的选股范围,在国内来说无非就是A股、中小板、新三板等板块;而在这一环节确定之后,下面就要开始第二个艺术与科学相结合的环节,确定你的股票池,即从你所着眼的股票范围内通过一系列的指标或者独特见解最终选定你拟定投资的股票。可以说选股是股票交易的首选环节,也是最为基本的环节,股票选择的好坏直接影响你后续的投资收益。

  • 择时 —— 择时包括买入时间的确定以及卖出时间的确定。择时环节直接影响你的收益。不过小编这里希望将买入时间与卖出时间转变为另外两个术语:建仓时间与平仓时间。之所以选择这两个术语,是因为你刚开始的股票策略并不总是多头操作,你完全可以在后市看熊的时候选择进行空头交易,也就是从交易所借入股票进行卖出操作,所以小编认为初始的时间使用建仓时间会更加准确。当然,目前在国内来说,卖空基本是不可行的,但是现在的不可行不代表为了的不可行,我们总是需要为未来做些准备嘛。

  • 仓位管理 —— 股票池中的各种股票买卖多少,对每种股票的资金投入怎样安排。一般来说,进行组合投资者,每种股票的投资权重应与股票的市值大小成正比,而进行单只股票操作者,资金投入量最好是自己自有资金的一部分,切勿借贷炒股。

  • 风险控制 —— 股票市场每天都在经历着大起大落,所以对于股票交易,你必须设定一定的风险水准,即撤出时机。人是一种神奇的动物,总是斩涨抱跌,对于一路走熊的股票总是不愿解套,这就犯了投资的大忌,股票交易必须要有果断的心态,在股价跌到自己的最低风险线之时及时平仓。

上面这四个环节的每一个环节都完全可以变成一套书,所以小编这里也不准备对这些进行过度阐述,有兴趣的朋友自行阅读投资类书籍。既然已经初步了解了交易策略,那么接下来小编就和大家谈一下何为移动平均交叉策略。

移动平均交叉策略正如其名所示,就是建立在移动平均线之上。这种策略的初衷是,股价的短期移动平均线和长期移动平均线的相对形态可以预示后市价格走向,当短期移动平均线处于长期移动平均线上方时,市场表现为牛市;而当短期移动平均线位于长期移动平均线下方时,市场处于熊市,因此正确的投资时间为:当短期移动平均线自下而上穿过长期移动平均线时进行股票买入;而当短期移动平均线自上而下穿过长期移动平均线时卖出股票。所以股票的所有买卖时机都是处于均线交叉的位置,所以这种策略被称之为移动平均线交叉策略。根据上面的交易策略构成四环节,这种策略在这四方面的主要特征表现为:

  1. 选股。这种策略没有明确的股票偏好,你完全可以根据自己的经验和知识确定一些交易活跃的股票。小编这里选择谷歌股票作为分析基础。

  2. 择时。交叉点就是买卖时机,自下而上穿过则买入,自上而下穿过则卖出。这里小编准备在短期移动平均线方面选择7天移动平均,而在长期移动平均线方面选择50天移动平均线。

  3. 仓位管理。全仓购买谷歌股票。

  4. 风险控制。股价跌出其基准的20%时强制平仓。

在后文的分析中,小编将暂时不考虑风险控制这一环节,本文后面的量化分析主要针对前三点。既然已经有了相应的交易策略,为了能够证明策略的有效性,下面对这一策略进行一下回测,即用历史数据检验一下策略的效果。

移动平均交叉策略回测实践

首先,需要计算7天移动平均值和50天移动平均值的差值,从而确定移动平均线的相对位置,这里小编先用谷歌的股价数据进行实践,见如下代码。

# 导入相关库
import numpy as np
import pandas as pd
from pandas_datareader import data as web
import datetime

# 获取谷歌自2006年至今的股价数据
start = datetime.datetime(2006,1,1)
end = datetime.date.today()
google = web.DataReader('GOOG','yahoo',start,end)

# 构造7天移动平均值、50天移动平均值及其差值
google["7d"] = np.round(google["Close"].rolling(window = 7, center = False).mean(), 2)
google["50d"] = np.round(google["Close"].rolling(window = 50, center = False).mean(), 2)
google['7d-50d'] = google['7d'] - google['50d']

# 查看一下数据
google.tail()
移动平均值差值

既然有了差值,那么下面就需要判断哪些交易日7天移动平均线在50天移动平均线之上,为此,小编使用numpy库中的where函数,将7天移动线在上的状态值定为1,之下的定为-1,否则赋值为0.
# 创建状态变量值并查看一下表格状态
google["State"] = np.where(google['7d-50d'] > 010)
google["State"] = np.where(google['7d-50d'] < 0-1, google["State"])
google.tail()

# 绘制状态图表
import matplotlib as mpl
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['figure.figsize'] = (169)
google["State"].plot(ylim =(-2,2)).axhline(y =0, color ="black", lw =2)
Python和股市数据分析(二)
状态表

Python和股市数据分析(二)
差值状态图

粗略地看了一下,发现多数交易日中,7天移动平均线在50天移动平均线之上,为了更加明确地看出到底是不是这种形态,稍微对状态值进行一下统计。
google['State'].value_counts()
Python和股市数据分析(二)
状态值统计

可以看出,在所有的交易日中有1838天处于牛市,而有1260天处于熊市,还有49天没有倾向性。而交易的时点发生在移动平均线交叉之时,所以小编这里需要再创建一个变量用来描述其所处的交易日是否是交易时点,姑且将这个变量命名为Signal吧。这个变量的计算规则为:
Python和股市数据分析(二)
其中的sign()函数为一个符号函数,最终使得 Python和股市数据分析(二),代码如下:
Medium = google.ix[-1"State"]
google.ix[-1"State"] = 0
google["Signal"] = np.sign(google["State"] - google["State"].shift(1))
google.ix[-1"State"] = Medium
google.tail() # 显示表特征
google["Signal"].plot(ylim =(-22)) # 绘图
google["Signal"].value_count() # 统计买卖次数
Python和股市数据分析(二)
交易信号表

Python和股市数据分析(二)
交易信号图

Python和股市数据分析(二)
买卖次数统计

可以看出从2006年至今,共有45次买入机会,46次卖出机会,如果我们执行这一策略,那么在这12年的时间内共需交易46次。既然已经知道了买卖的时机,那么接下来小编就统计一下这些交易时点的股价情况,而后制作一个交易信息备忘表。
google.loc[google["Signal"] ==1"Close"]
google.loc[google["Signal"] ==-1"Close"]
google_signals = pd.concat([
        pd.DataFrame({"Price": google.loc[google["Signal"] == 1"Close"],
                     "State": google.loc[google["Signal"] == 1"State"],
                     "Signal""Buy"}),
        pd.DataFrame({"Price": google.loc[google["Signal"] == -1"Close"],
                     "State": google.loc[google["Signal"] == -1"State"],
                     "Signal""Sell"}),
    ])
google_signals.sort_index(inplace = True)
google_signals.head(8)
Python和股市数据分析(二)
交易信息备忘表

既然已经有了买卖时机,那么下面就要计算按照这种买卖时机,最终的盈利情况将是多少(注意,小编这里的都还只是以一股股票进行说明,后面会讲述全仓情况下的盈利情况),盈利的计算代码与统计表如下。
google_profits = pd.DataFrame({
        "Price": google_signals.loc[(google_signals["Signal"] == "Buy") &
                                  google_signals["State"] == 1"Price"],
        "Profit": pd.Series(google_signals["Price"] - google_signals["Price"].shift(1)).loc[
            google_signals.loc[(google_signals["Signal"].shift(1) == "Buy") & (google_signals["State"].shift(1) == 1)].index
        ].tolist(),
        "End Date": google_signals["Price"].loc[
            google_signals.loc[(google_signals["Signal"].shift(1) == "Buy") & (google_signals["State"].shift(1) == 1)].index
        ].index
    })
google_profits.tail()
Python和股市数据分析(二)
获利情况统计表

上面大致讲述了整个回测的相关流程,最后,将一切整合在一起,考虑在有100万现金的情况下全仓进行谷歌股票的买卖将会得到的盈利情况。
tradeperiods =pd.DataFrame({"Start": google_profits.index,
                            "End": google_profits["End Date"]})
google_profits["Low"] =tradeperiods.apply(lambda x: min(google.loc[x["Start"]:x["End"], "Low"]), axis =1)
cash =1000000
google_backtest = pd.DataFrame({"Start Port. Value": [],
                         "End Port. Value": [],
                         "End Date": [],
                         "Shares": [],
                         "Share Price": [],
                         "Trade Value": [],
                         "Profit per Share": [],
                         "Total Profit": [],
                         "Stop-Loss Triggered": []})
port_value =.1
batch =100
stoploss =.2
for index, row in google_profits.iterrows():
    batches =np.floor(cash * port_value) //np.ceil(batch * row["Price"]) 
    trade_val =batches * batch * row["Price"
    if row["Low"] < (1-stoploss) * row["Price"]:   
        share_profit =np.round((1-stoploss) * row["Price"], 2)
        stop_trig = True
    else:
        share_profit =row["Profit"]
        stop_trig =False
    profit =share_profit * batches * batch 
    google_backtest =google_backtest.append(pd.DataFrame({
                "Start Port. Value": cash,
                "End Port. Value": cash +profit,
                "End Date": row["End Date"],
                "Shares": batch *batches,
                "Share Price": row["Price"],
                "Trade Value": trade_val,
                "Profit per Share": share_profit,
                "Total Profit": profit,
                "Stop-Loss Triggered": stop_trig
            }, index =[index]))
    cash =max(0, cash +profit)

google_backtest.tail()
google_backtest["End Port. Value"].plot()
Python和股市数据分析(二)
仓位盈利情况统计表

Python和股市数据分析(二)
盈利情况图

小编的财产总额12年增加了12%。考虑到每次交易额只有总额的10%,所以十二年的资产总收益率约为120%,相对来说有点低了,这需要我们进一步调整相应的交易策略,不过这些小编不准备在本文讲了。最后,小编来简单地讲这一收益率与指数收益率进行一下比较,从而真正了解这种收益率的效果。

基准对比

既然上面获得了谷歌的盈利数据,那么为了判别这种收益情况到底如何,就有必要将其与基准收益率进行对比,而对于美国股市来说,SP500可能是一个比较好的参照对象,这里就以SP500的收益为基准来说明这种策略的优劣,见下面代码与对照图。

# 获取SP500数据
spyder =web.DataReader("SPY""yahoo", start, end)

# 构建投资收益表
batches =1000000//np.ceil(100*spyder.ix[0,"Close"]) 
trade_val =batches *batch *spyder.ix[0,"Close"
final_val =batches *batch *spyder.ix[-1,"Close"] +(1000000-trade_val) 

# 绘制对照图
ax_bench =(spyder["Close"] /spyder.ix[0"Close"]).plot(label ="SPY")
ax_bench =(google_backtest["End Port. Value"] /1000000).plot(ax =ax_bench, label ="Google")
ax_bench.legend(loc ='best')
基准盈利对比图

可见,这种移动平均策略相对来说收益率较低,只有在少数几个时期跑赢了大盘,其它时间段基本被SP500超越,因此这种策略需要进一步修正,这不是本文目的,本文的目的截止到这里已经介绍完毕,更多策略探讨会在以后给出。

后记

本文讲到这里就暂时告一段落,本期文章和大家继续聊了一下怎么使用python去处理股市数据,这期主要在于探索移动平均策略的相应效果,最终的实践看来,这种策略盈利水平较低,需要进一步予以完善。后续系列的文章会对各种策略进行深层次探索。再次感谢你们的支持与鼓励,你们的陪伴是小编前进的动力!



以上是关于Python和股市数据分析的主要内容,如果未能解决你的问题,请参考以下文章

python的量化代码怎么用到股市中

Python 东方财富网-股市行情数据抓取

利用python获取股票数据

一颗韭菜的自我修养:用Python分析下股市,练练手

爬虫练习五:多进程爬取股市通股票数据

python 用于数据探索的Python代码片段(例如,在数据科学项目中)