前言
由于笔者并无深厚的数学功底也无深厚的金融知识, 所以不会在本文中引用各种高深的投资模型或数学模型,参考书籍主要是《海龟交易法则》《以交易为生》。
交易系统
在交易之前,我们应该首先有一个交易系统用于指导我们自己交易,不一定有什么规范,但是可以作为一个交易的依据,至于这个依据可不可行,科不科学那就见仁见智了。
当然了,这里的交易系统不一定是程序,只是指你自己的交易原则或者遵守的一些技巧或者方法,你可以手动执行也可以借助编程语言,编程语言不就是一套用来使用的工具么.
这里参考海龟交易法则里面的交易体系(这里只是参考大方向).
建立一个完善的交易体系,我们至少应该思考一下六个方面。
1、市场----买卖什么
2、头寸规模----买卖多少
3、入市----何时买入
4、止损----何时退出亏损的头寸
5、止盈----何时退出盈利的头寸
6、离市----何时离市
简单的示例
买卖A股
全仓
当日涨幅超过3%买入。
当持有头寸亏损超过3%,平仓
当日跌幅大于3%或者三个连续阴线
分析: 这个交易策略其实只有在行情以波浪形状向上的行情时候才能获利,如果是盘整的情况下,怕是会亏的很惨。这里之所以写的这么简单粗暴是为了后面策略测试撸代码简单。
数据获取及处理
因为这里说的是用python炒股,所以应该采用程序的方式去获取数据,如果人工炒股,下载任何股票行情软件都是可以的,但是人工的执行是需要花费比较多的精力的。
而python语言中用于获取股票行情数据的库,最有名莫过于tushare了。
这里以上证乐视的股票为例吧。
python环境安装
安装Anaconda(python2版本)
下载地址:https://www.anaconda.com/download/
注:如果没安装过这个环境的经验,就百度或者谷歌一下吧,如果不是安装anaconda则需要艰难的自行解决依赖。
安装tushare
pipinstall tushare
获取行情数据
importpandasaspdimporttushareasts# 通过股票代码获取股票数据,这里没有指定开始及结束日期df = ts.get_k_data("300104")# 查看前十条数据df.head()# 查看后十条数据df.tail()# 将数据的index转换成date字段对应的日期df.index = pd.to_datetime(df.date)# 将多余的date字段删除df.drop("date", inplace=True, axis=1)
注:关于股票数据的相关处理需要由pandas,matplotlib的知识,参考:http://pandas.pydata.org/pandas-docs/version/0.20/10min.html
计算常用指标
# 计算5,15,50日的移动平均线, MA5, MA15, MA50days = [5,15,50]formain days: column_name ="MA{}".format(ma) df[column_name] = pd.rolling_mean(df.close,ma)# 计算浮动比例df["pchange"] = df.close.pct_change()# 计算浮动点数df["change"] = df.close.diff()
最终处理完成后的结果如下:
df.head()Out[13]:openclosehighlowvolumecodeMA5MA15MA50\date2013-11-299.3969.7419.8709.389146587.0300104NaNNaNNaN2013-12-029.2988.7689.3448.768177127.0300104NaNNaNNaN2013-12-038.1428.4148.5467.890176305.0300104NaNNaNNaN2013-12-048.3918.0728.6078.053120115.0300104NaNNaNNaN2013-12-057.9837.3668.1087.280253764.0300104 8.4722NaNNaNpchangechangedate2013-11-29NaNNaN2013-12-02-0.099887-0.9732013-12-03-0.040374-0.3542013-12-04-0.040647-0.342
可视化
走势图
所谓一图胜前言,将数据可视化可以非常直观的感受到股票的走势。
个人觉得,如果用程序炒股还是应该一切都量化的,不应该有过多的主观观点,如果过于依赖直觉或者当时心情,那么实在没必要用程序分析了。
df[["close","MA5","MA15","MA50"]].plot(figsiz=(10,18))
效果如下:
k线图
importmatplotplib.pyplotaspltfrommatplotlib.daetimportDateFormatterfrommatplotlib.financeimportdate2num, candlestick_ohlcdefcandlePlot(data, title=""):data["date"] = [date2num(pd.to_datetime(x))forxindata.index] dataList = [tuple(x)forxindata[ ["date","open","high","low","close"]].values] ax = plt.subplot() ax.set_title(title) ax.xaxis.set_major_formatter(DateFormatter("%y-%m-%d")) candlestick_ohlc(ax, dataList, width=0.7, colorup="r", colordown="g") plt.setp(plt.gca().get_xticklabels(), rotation=50, horizontalalignment="center") fig = plt.gcf() fig.set_size_inches(20,15) plt.grid(True)candlePlot(df)
效果如下:
注: 这里只是一个示例,说明matplotlib的强大以及小小的演示,如果遇到什么奇怪的问题就查api或者google吧。
策略测试
手动撸代码
这里用最近买过的一只股票吧,京东方A(000725)。
# 导入相关模块importtushareastsimportpandasaspd# 获取数据df = ts.get_k_data("000725")# 处理数据df.index = pd.to_datetime(df.date)df.drop("date", axis=1, inplace=True)# 计算浮动比例df["pchange"] = df.close.pct_change()# 计算浮动点数df["change"] = df.close.diff()# 查看当前数据数据前五行open close high low volume code pchange changedate2015-07-204.2644.2344.3424.16513036186.0000725NaN NaN2015-07-214.1364.1954.2744.0968776773.0000725-0.009211-0.0392015-07-224.1754.1464.2144.0679083703.0000725-0.011681-0.0492015-07-234.1364.2544.2834.09612792734.00007250.0260490.1082015-07-244.2244.1364.2544.10613009620.0000725-0.027739-0.118# 设定回撤值withdraw =0.03# 设定突破值breakthrough =0.03# 设定账户资金account =10000# 持有仓位手数position =0defbuy(bar):globalaccount, position print("{}: buy {}".format(bar.date, bar.close))# 一手价格one = bar.close *100position = account // one account = account - (position * one)defsell(bar):globalaccount, position# 一手价格print("{}: sell {}".format(bar.date, bar.close)) one = bar.close *100account += position * one position =0print("开始时间投资时间: ", df.iloc[0].date)fordateindf.index: bar = df.loc[date]ifbar.pchangeandbar.pchange > breakthroughandposition ==0: buy(bar)elifbar.pchangeandbar.pchange < withdrawandposition >0: sell(bar)print("最终可有现金: ", account)print("最终持有市值: ", position * df.iloc[-1].close *100)
输出如下:
开始时间投资时间: 2015-07-202015-07-29: buy 3.832015-07-30: sell 3.6532015-08-04: buy 3.752......2018-02-27: sell 5.712018-03-06: buy 5.79最终可有现金: 333.3最终持有市值: 7527.0
结论: 通过上面的测试发现资亏了两千多...
借助测试框架
借助测试框架才是正确的回撤姿势,因为框架包含了更多的功能。这里使用pyalgotrade。
简单使用
frompyalgotradeimportstrategyfrompyalgotradeimporttechnicalfrompyalgotrade.barfeedimportyahoofeed# 自定义事件窗口类classDiffEventWindow(technical.EventWindow):def__init__(self, period):assert(period >0) super(DiffEventWindow, self).__init__(period) self.__value =NonedefonNewValue(self, dateTime, value):super(DiffEventWindow, self).onNewValue(dateTime, value)ifself.windowFull(): lastValue = self.getValues()[0] nowValue = self.getValues()[1] self.__value = (nowValue - lastValue) / lastValuedefgetValue(self):returnself.__value# 自定义指标classDiff(technical.EventBasedFilter):def__init__(self, dataSeries, period, maxLen=None):super(Diff, self).__init__(dataSeries, DiffEventWindow(period), maxLen)# 定义自己的策略classMyStrategy(strategy.BacktestingStrategy):def__init__(self, feed, instrument, diffPeriod=2):# 传入feed及初始账户资金super(MyStrategy, self).__init__(feed,10000) self.__instrument = instrument self.__position =Noneself.setUseAdjustedValues(True) self.__prices = feed[instrument].getPriceDataSeries() self.__diff = Diff(self.__prices, diffPeriod) self.__break =0.03self.__withdown =-0.03defgetDiff(self):returnself.__diffdefonEnterCanceled(self, position):self.__position =NonedefonEnterOk(self, position):execInfo = position.getEntryOrder().getExecutionInfo() self.info("BUY at $%.2f"% (execInfo.getPrice()))defonExitOk(self, position):execInfo = position.getExitOrder().getExecutionInfo() self.info("SELL at $%.2f"% (execInfo.getPrice())) self.__position =NonedefonExitCanceled(self, position):# If the exit was canceled, re-submit it.self.__position.exitMarket()defonBars(self, bars):account = self.getBroker().getCash() bar = bars[self.__instrument]ifself.__positionisNone: one = bar.getPrice() *100oneUnit = account // oneifoneUnit >0andself.__diff[-1] > self.__break: self.__position = self.enterLong(self.__instrument, oneUnit *100,True)elifself.__diff[-1] < self.__withdownandnotself.__position.exitActive(): self.__position.exitMarket()defrunStrategy():# 下载数据jdf = ts.get_k_data("000725")# 新建Adj Close字段jdf["Adj Close"] =jdf.close# 将tushare下的数据的字段保存为pyalgotrade所要求的数据格式jdf.columns = ["Date","Open","Close","High","Low","Volume","code","Adj Close"]# 将数据保存成本地csv文件jdf.to_csv("jdf.csv", index=False) feed = yahoofeed.Feed() feed.addBarsFromCSV("jdf","jdf.csv") myStrategy = MyStrategy(feed,"jdf") myStrategy.run() print("Final portfolio value: $%.2f"% myStrategy.getResult())runStrategy()
输出如下
2015-07-30 00:00:00 strategy [INFO] BUY at$3.782015-07-31 00:00:00 strategy [INFO] SELL at$3.572015-08-05 00:00:00 strategy [INFO] BUY at$3.732015-08-06 00:00:00 strategy [INFO] SELL at$3.56...2018-02-13 00:00:00 strategy [INFO] BUY at$5.45Final portfolio value:$7877.30
猛地一看会发现,用框架似乎写了更多的代码,但是框架内置了更多分析工具。
下面简单介绍。
策略可视化
frompyalgotradeimportstrategyfrompyalgotradeimporttechnicalfrompyalgotrade.barfeedimportyahoofeedfrompyalgotradeimportplotterfrompyalgotrade.stratanalyzerimportreturnsclassDiffEventWindow(technical.EventWindow):def__init__(self, period):assert(period >0) super(DiffEventWindow, self).__init__(period) self.__value =NonedefonNewValue(self, dateTime, value):super(DiffEventWindow, self).onNewValue(dateTime, value)ifself.windowFull(): lastValue = self.getValues()[0] nowValue = self.getValues()[1] self.__value = (nowValue - lastValue) / lastValuedefgetValue(self):returnself.__valueclassDiff(technical.EventBasedFilter):def__init__(self, dataSeries, period, maxLen=None):super(Diff, self).__init__(dataSeries, DiffEventWindow(period), maxLen)classMyStrategy(strategy.BacktestingStrategy):def__init__(self, feed, instrument, diffPeriod=2):super(MyStrategy, self).__init__(feed,10000) self.__instrument = instrument self.__position =Noneself.setUseAdjustedValues(True) self.__prices = feed[instrument].getPriceDataSeries() self.__diff = Diff(self.__prices, diffPeriod) self.__break =0.03self.__withdown =-0.03defgetDiff(self):returnself.__diffdefonEnterCanceled(self, position):self.__position =NonedefonEnterOk(self, position):execInfo = position.getEntryOrder().getExecutionInfo() self.info("BUY at $%.2f"% (execInfo.getPrice()))defonExitOk(self, position):execInfo = position.getExitOrder().getExecutionInfo() self.info("SELL at $%.2f"% (execInfo.getPrice())) self.__position =NonedefonExitCanceled(self, position):# If the exit was canceled, re-submit it.self.__position.exitMarket()defonBars(self, bars):account = self.getBroker().getCash() bar = bars[self.__instrument]ifself.__positionisNone: one = bar.getPrice() *100oneUnit = account // oneifoneUnit >0andself.__diff[-1] > self.__break: self.__position = self.enterLong(self.__instrument, oneUnit *100,True)elifself.__diff[-1] < self.__withdownandnotself.__position.exitActive(): self.__position.exitMarket()defrunStrategy():# 下载数据jdf = ts.get_k_data("000725")# 新建Adj Close字段jdf["Adj Close"] =jdf.close# 将tushare下的数据的字段保存为pyalgotrade所要求的数据格式jdf.columns = ["Date","Open","Close","High","Low","Volume","code","Adj Close"]# 将数据保存成本地csv文件jdf.to_csv("jdf.csv", index=False) feed = yahoofeed.Feed() feed.addBarsFromCSV("jdf","jdf.csv") myStrategy = MyStrategy(feed,"jdf") returnsAnalyzer = returns.Returns() myStrategy.attachAnalyzer(returnsAnalyzer) plt = plotter.StrategyPlotter(myStrategy) plt.getInstrumentSubplot("jdf") plt.getOrCreateSubplot("returns").addDataSeries("Simple returns", returnsAnalyzer.getReturns()) myStrategy.run() print("Final portfolio value: $%.2f"% myStrategy.getResult()) plt.plot()runStrategy()
原文链接:http://blog.51cto.com/youerning/2083764
网友评论