因爲筆者並沒有深厚的數學功底也無深厚的金融知識, 因此不會在本文中引用各類高深的投資模型或數學模型,參考書籍主要是《海龜交易法則》《以交易爲生》。html
在交易以前,咱們應該首先有一個交易系統用於指導咱們本身交易,不必定有什麼規範,可是能夠做爲一個交易的依據,至於這個依據可不可行,科不科學那就見仁見智了。python
固然了,這裏的交易系統不必定是程序,只是指你本身的交易原則或者遵照的一些技巧或者方法,你能夠手動執行也能夠藉助編程語言,編程語言不就是一套用來使用的工具麼.git
這裏參考海龜交易法則裏面的交易體系(這裏只是參考大方向).
創建一個完善的交易體系,咱們至少應該思考一下六個方面。github
分析: 這個交易策略其實只有在行情以波浪形狀向上的行情時候才能獲利,若是是盤整的狀況下,怕是會虧的很慘。這裏之因此寫的這麼簡單粗暴是爲了後面策略測試擼代碼簡單。編程
由於這裏說的是用python炒股,因此應該採用程序的方式去獲取數據,若是人工炒股,下載任何股票行情軟件都是能夠的,可是人工的執行是須要花費比較多的精力的。api
而python語言中用於獲取股票行情數據的庫,最有名莫過於tushare了。微信
這裏以上證樂視的股票爲例吧。框架
安裝Anaconda(python2版本)
下載地址:https://www.anaconda.com/download/
注:若是沒安裝過這個環境的經驗,就百度或者谷歌一下吧,若是不是安裝anaconda則須要艱難的自行解決依賴。編程語言
pip install tushare
參考: http://tushare.org/ide
import pandas as pd import tushare as ts # 經過股票代碼獲取股票數據,這裏沒有指定開始及結束日期 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, MA50 days = [5, 15, 50] for ma in 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]: open close high low volume code MA5 MA15 MA50 \ date 2013-11-29 9.396 9.741 9.870 9.389 146587.0 300104 NaN NaN NaN 2013-12-02 9.298 8.768 9.344 8.768 177127.0 300104 NaN NaN NaN 2013-12-03 8.142 8.414 8.546 7.890 176305.0 300104 NaN NaN NaN 2013-12-04 8.391 8.072 8.607 8.053 120115.0 300104 NaN NaN NaN 2013-12-05 7.983 7.366 8.108 7.280 253764.0 300104 8.4722 NaN NaN pchange change date 2013-11-29 NaN NaN 2013-12-02 -0.099887 -0.973 2013-12-03 -0.040374 -0.354 2013-12-04 -0.040647 -0.342
所謂一圖勝前言,將數據可視化能夠很是直觀的感覺到股票的走勢。
我的以爲,若是用程序炒股仍是應該一切都量化的,不該該有過多的主觀觀點,若是過於依賴直覺或者當時心情,那麼實在不必用程序分析了。
df[["close", "MA5", "MA15", "MA50"]].plot(figsiz=(10,18))
效果以下:
import matplotplib.pyplot as plt from matplotlib.daet import DateFormatter from matplotlib.finance import date2num, candlestick_ohlc def candlePlot(data, title=""): data["date"] = [date2num(pd.to_datetime(x)) for x in data.index] dataList = [tuple(x) for x in data[ ["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)。
# 導入相關模塊 import tushare as ts import pandas as pd # 獲取數據 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 change date 2015-07-20 4.264 4.234 4.342 4.165 13036186.0 000725 NaN NaN 2015-07-21 4.136 4.195 4.274 4.096 8776773.0 000725 -0.009211 -0.039 2015-07-22 4.175 4.146 4.214 4.067 9083703.0 000725 -0.011681 -0.049 2015-07-23 4.136 4.254 4.283 4.096 12792734.0 000725 0.026049 0.108 2015-07-24 4.224 4.136 4.254 4.106 13009620.0 000725 -0.027739 -0.118 # 設定回撤值 withdraw = 0.03 # 設定突破值 breakthrough = 0.03 # 設定帳戶資金 account = 10000 # 持有倉位手數 position = 0 def buy(bar): global account, position print("{}: buy {}".format(bar.date, bar.close)) # 一手價格 one = bar.close * 100 position = account // one account = account - (position * one) def sell(bar): global account, position # 一手價格 print("{}: sell {}".format(bar.date, bar.close)) one = bar.close * 100 account += position * one position = 0 print("開始時間投資時間: ", df.iloc[0].date) for date in df.index: bar = df.loc[date] if bar.pchange and bar.pchange > breakthrough and position == 0: buy(bar) elif bar.pchange and bar.pchange < withdraw and position > 0: sell(bar) print("最終可有現金: ", account) print("最終持有市值: ", position * df.iloc[-1].close * 100)
輸出以下:
開始時間投資時間: 2015-07-20 2015-07-29: buy 3.83 2015-07-30: sell 3.653 2015-08-04: buy 3.752 ...... 2018-02-27: sell 5.71 2018-03-06: buy 5.79 最終可有現金: 333.3 最終持有市值: 7527.0
結論: 經過上面的測試發現資虧了兩千多...
藉助測試框架纔是正確的回撤姿式,由於框架包含了更多的功能。這裏使用pyalgotrade。
from pyalgotrade import strategy from pyalgotrade import technical from pyalgotrade.barfeed import yahoofeed # 自定義事件窗口類 class DiffEventWindow(technical.EventWindow): def __init__(self, period): assert(period > 0) super(DiffEventWindow, self).__init__(period) self.__value = None def onNewValue(self, dateTime, value): super(DiffEventWindow, self).onNewValue(dateTime, value) if self.windowFull(): lastValue = self.getValues()[0] nowValue = self.getValues()[1] self.__value = (nowValue - lastValue) / lastValue def getValue(self): return self.__value # 自定義指標 class Diff(technical.EventBasedFilter): def __init__(self, dataSeries, period, maxLen=None): super(Diff, self).__init__(dataSeries, DiffEventWindow(period), maxLen) # 定義本身的策略 class MyStrategy(strategy.BacktestingStrategy): def __init__(self, feed, instrument, diffPeriod=2): # 傳入feed及初始帳戶資金 super(MyStrategy, self).__init__(feed, 10000) self.__instrument = instrument self.__position = None self.setUseAdjustedValues(True) self.__prices = feed[instrument].getPriceDataSeries() self.__diff = Diff(self.__prices, diffPeriod) self.__break = 0.03 self.__withdown = -0.03 def getDiff(self): return self.__diff def onEnterCanceled(self, position): self.__position = None def onEnterOk(self, position): execInfo = position.getEntryOrder().getExecutionInfo() self.info("BUY at $%.2f" % (execInfo.getPrice())) def onExitOk(self, position): execInfo = position.getExitOrder().getExecutionInfo() self.info("SELL at $%.2f" % (execInfo.getPrice())) self.__position = None def onExitCanceled(self, position): # If the exit was canceled, re-submit it. self.__position.exitMarket() def onBars(self, bars): account = self.getBroker().getCash() bar = bars[self.__instrument] if self.__position is None: one = bar.getPrice() * 100 oneUnit = account // one if oneUnit > 0 and self.__diff[-1] > self.__break: self.__position = self.enterLong(self.__instrument, oneUnit * 100, True) elif self.__diff[-1] < self.__withdown and not self.__position.exitActive(): self.__position.exitMarket() def runStrategy(): # 下載數據 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.78 2015-07-31 00:00:00 strategy [INFO] SELL at $3.57 2015-08-05 00:00:00 strategy [INFO] BUY at $3.73 2015-08-06 00:00:00 strategy [INFO] SELL at $3.56 ... 2018-02-13 00:00:00 strategy [INFO] BUY at $5.45 Final portfolio value: $7877.30
猛地一看會發現,用框架彷佛寫了更多的代碼,可是框架內置了更多分析工具。
下面簡單介紹。
from pyalgotrade import strategy from pyalgotrade import technical from pyalgotrade.barfeed import yahoofeed from pyalgotrade import plotter from pyalgotrade.stratanalyzer import returns class DiffEventWindow(technical.EventWindow): def __init__(self, period): assert(period > 0) super(DiffEventWindow, self).__init__(period) self.__value = None def onNewValue(self, dateTime, value): super(DiffEventWindow, self).onNewValue(dateTime, value) if self.windowFull(): lastValue = self.getValues()[0] nowValue = self.getValues()[1] self.__value = (nowValue - lastValue) / lastValue def getValue(self): return self.__value class Diff(technical.EventBasedFilter): def __init__(self, dataSeries, period, maxLen=None): super(Diff, self).__init__(dataSeries, DiffEventWindow(period), maxLen) class MyStrategy(strategy.BacktestingStrategy): def __init__(self, feed, instrument, diffPeriod=2): super(MyStrategy, self).__init__(feed, 10000) self.__instrument = instrument self.__position = None self.setUseAdjustedValues(True) self.__prices = feed[instrument].getPriceDataSeries() self.__diff = Diff(self.__prices, diffPeriod) self.__break = 0.03 self.__withdown = -0.03 def getDiff(self): return self.__diff def onEnterCanceled(self, position): self.__position = None def onEnterOk(self, position): execInfo = position.getEntryOrder().getExecutionInfo() self.info("BUY at $%.2f" % (execInfo.getPrice())) def onExitOk(self, position): execInfo = position.getExitOrder().getExecutionInfo() self.info("SELL at $%.2f" % (execInfo.getPrice())) self.__position = None def onExitCanceled(self, position): # If the exit was canceled, re-submit it. self.__position.exitMarket() def onBars(self, bars): account = self.getBroker().getCash() bar = bars[self.__instrument] if self.__position is None: one = bar.getPrice() * 100 oneUnit = account // one if oneUnit > 0 and self.__diff[-1] > self.__break: self.__position = self.enterLong(self.__instrument, oneUnit * 100, True) elif self.__diff[-1] < self.__withdown and not self.__position.exitActive(): self.__position.exitMarket() def runStrategy(): # 下載數據 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()
圖片輸出以下
注: 這裏的策略測試股票選擇以及時間選擇並不嚴謹,僅做功能展現,測試結果可能有很大的巧合性。Pyalgotrade詳細介紹皆使用參考:http://gbeced.github.io/pyalgotrade/docs/v0.18/html/index.html
上述源代碼:https://github.com/youerning/blog/blob/master/python-trade/demo.py
根據這個需求寫了一個股價監控的半成品,經過郵箱監控。
項目參考: https://github.com/youerning/UserPyScript/tree/master/monitor
技巧:在微信的輔助功能裏面啓用QQ郵箱提醒的功能,那麼股價變更的通知就會很及時了,由於微信幾乎等同於短信了。
這裏簡單說一下各個配置項及使用方法。
default段落
breakthrough表明突破的比例,須要傳入兩個值,項目裏面的突破比例依次是3%,5%.
withdraw表明回撤,也須要兩個值,示例爲3%,5%.
attention表明關注的股票列表,填入關注的股票代碼,用空格隔開
注:這裏暫時沒有考慮關注股票的狀況,因此不少的關注股票也許有性能上的問題。
mail段落
依次輸入用戶名及密碼以及收件人的郵箱
position段落
當前持倉的股票以及其持倉成本。
如持有京東方A(000725)以5.76的股價。
000725 = 5.76
若是多個持倉就多個如上的相應的鍵值對。
使用方法參考該腳本的readme
https://github.com/youerning/UserPyScript/blob/master/monitor/README.md
==PS:很難過的是英文水平很差還用由於註釋,以及用英文詞彙作變量名,若是詞不達意請見諒。==
這一部分本人暫時沒有讓程序自動執行,由於暫時尚未打磨出來一套適合本身並相信的體系,因此依靠股價監控的通知,根據不斷修正的體系在手動執行交易。
因爲入市不到一年,因此就不用問我走勢或者收益了, 當前戰績是5局3勝,微薄盈利。
最後如下圖結束.
關注一下再走唄^_^