1. 程式人生 > >數據分析之A股市場技術分析是否可行

數據分析之A股市場技術分析是否可行

python pandas pyalgotrade tushare 股票分析

前言

這裏並不是為了說明技術分析可行,也不是為了說明技術分析不可行,僅是以我淺薄的知識驗證一些事情,測試方法及測試結果都會公布如下.

至於測試方法是否嚴謹, 就請讀者自行判斷吧。

數據獲取

這裏隨機選擇300支股票,並下載最近七年的日內行情數據.

導入必要庫

import tushare as ts
import random
import os
import json
import datetime

篩選股票

num = 300
code_lis = []

# 獲取中小板數據
zxb_df = ts.get_gem_classified()
zxb_lis = list(zxb_df.code)

# 獲取滬深三百
hs300_df = ts.get_hs300s()
hs300_lis = list(hs300_df.code)

# 依次從中小板,滬深300中隨機選取 num/2支股票代碼
zxb_rand = random.sample(zxb_lis, int(num / 2))
hs300_rand = random.sample(hs300_lis, int(num / 2))

# 保存到code_lis並保存
code_lis.extend(zxb_rand)
code_lis.extend(hs300_rand)
with open(code_file, "w") as wf:
    json.dump(code_lis, wf)

return code_lis

篩選說明: 隨機從中小板以及滬深300裏面隨機各選擇150支股票用於此次測試。

下載數據

download_path = "download"
now = datetime.datetime.now()
start_time = now - datetime.timedelta(days=years * 365)
start = start_time.strftime("%Y-%m-%d")

try:
    print("{} 正在下載".format(code))
    df = ts.get_k_data(code, start=start)
    print("{} 下載完成".format(code))
except Exception as e:
    print("{} 下載失敗".format(code))
    return

# 新建Adj Close字段
df["Adj Close"] = df.close

# 將tushare下的數據的字段保存為pyalgotrade所要求的數據格式
df.columns = ["Date", "Open", "Close", "High", "Low", "Volume", "code", "Adj Close"]

# 將數據保存成本地csv文件
df.to_csv(save_path, index=False)

可直接執行源代碼 裏面的downloader.py文件

執行結果如下

技術分享圖片

最後數據文件結構如下:

Date,Open,Close,High,Low,Volume,code,Adj Close
2011-03-10,5.606,5.488,5.606,5.477,308493.0,000001,5.488
2011-03-11,5.464,5.42,5.501,5.413,230166.0,000001,5.42
2011-03-14,5.403,5.461,5.467,5.4,217999.0,000001,5.461
2011-03-15,5.41,5.349,5.437,5.302,284381.0,000001,5.349
2011-03-16,5.356,5.386,5.403,5.315,242075.0,000001,5.386
2011-03-17,5.342,5.305,5.369,5.295,207262.0,000001,5.305
2011-03-18,5.366,5.346,5.366,5.319,145243.0,000001,5.346
2011-03-21,5.336,5.326,5.366,5.309,160157.0,000001,5.326

註: 這裏下載每只股票最近七年的日內行情數據,但是並不是所有的公司都上市了七年。
源代碼參考:donwloader.py

策略選取

策略的選擇原則是不會涵蓋大量的計算。單純通過開收高低、前復權收盤價、交易量這些基本數據用於決策買入賣出。

註: 關於策略的選擇以及其參數這裏有很大的主觀成分。

選擇策略如下

雙均線策略

雙均線策略應該是最簡單的策略了,主要原理是,選擇一條短期的移動平均線,一條長期的移動平均線,當短期移動平均線向上突破長期移動平均線則買入,反之,則賣出。

這裏選擇10日的短期移動平均線,25日的長期移動平均線。

隨機策略

這個策略用來隨機選擇買入時間點,然後20個交易日後賣出.

瞧瞧隨機的力量。

海龜交易策略

  • 市場: A股市場
  • 倉位: 通過市場波動性調整及管理倉位.具體計算流程如下。

    1. True Range

      True Range = Maximum(H ? L, H ? PDC, PDC ? L)

      公式中, True Range代表一天內的波動量,H為當日最高價, L為當日最低價, PDC為前一日收盤價.

    2. N

      N= (19 * PDN + TR) / 20

      公式中:TR為True Range,即一天波動量,PDN為前一日N值。如果沒有PDN則取TR的二十日平均值.

    3. Doller Volatility

      Dollar Volatility =N ?DollarsPerPoint

      公式中, Dollar Volatility指的是波動的價格,Dollars per Point指的是標的股票每波動一個最小單位,1手股票的總價格變化量。在國內最小變化量是0.01元,1手是100股。所以Dollars per Point就是0.01×100=1

    4. Unit

      Unit = (1 % of AccountMarketDollar) / Volatility

      公式中, Unit即為我們買賣的單位,1% of Account是總資產的1%,Market Dollar Volatility就是我們之前算出的Dollar Volatility,通過此公式計算出的Unit就是我們要買入的單位數量。此公式的意義是在一般情況下(市場波動率不大的時候),如果買入1Unit單位的資產,當天震幅使得總資產的變化不超過1%

  • 入市: 海龜有兩個交易系統,可以自由選擇,這裏只選擇系統一。

    系統一

    1.若當前價格高於過去20日的最高價,則買入一個Unit(註意是分鐘回測)

    2.加倉:若股價在上一次買入(或加倉)的基礎上上漲了0.5N,則加倉一個Unit

    系統二

    與系統一相一致,但當如破55日最高價時才購買

    1.若當前價格高於過去55日的最高價,則買入一個Unit.

    1.加倉:若股價在上一次買入(或加倉)的基礎上上漲了0.5N,則加倉一個Unit

    Example:若某只股票A的N為2,20日最高價為100,則當股價突破100時買入一個Unit,當股價突破100+0.5×2=101時加倉一個Unit,當股價突破101+0.5×2=102時加倉一個Unit。

  • 止盈:

    系統一

    當股價跌破10日內最低價時(10日唐奇安通道下沿),清空頭寸結束本次交易

    系統二

    當股價跌破20日內最低價時(20日唐奇安通道下沿),清空頭寸結束本次交易

  • 技巧: 資金的調整。

    開始時設定兩個比例:Loss和Adjust。若交易結束後損失的資金占總資金比例大於Loss,則今後只用現有投資資金的Adjust比例。

    Example:若初始資金為100萬,設定Loss=80%,Adjust=90%。則當總資產低於100×80%=80萬時,進行一次資金調整,以後只使用80×90%=72萬的資金用於投資行為

參考鏈接:https://www.joinquant.com/post/c1747eae8096b5028e471892bef0cf1d?f=stydy&m=algorithm

Dual Thrust交易策略

  • 計算觸發值

    1)N日High的最高價HH, N日Close的最低價LC;

    (2)N日Close的最高價HC,N日Low的最低價LL;

    (3)Range = Max(HH-LC,HC-LL)

    (4)BuyLine = Open + K * Range

    (5)SellLine = Open + K * Range

策略模型參考下圖

技術分享圖片

  • 入市

    (1)當價格向上突破上軌時,如果當時持有空倉,則先平倉,再開多倉;如果沒有倉位,則直接開多倉;

    (2)當價格向下突破下軌時,如果當時持有多倉,則先平倉,再開空倉;如果沒有倉位,則直接開空倉;

    用於A股只能做過所以不用賣空策略賣空策略用於離市.K值使用0.3, 由於這個k值沒有參數調優過,完全是隨便想的值,所以可能讓dual thrust策略的效果沒有發揮到最大。

  • 止損: 無

這裏N日的值取15天。

參考鏈接: https://www.joinquant.com/post/274

源代碼

由於代碼段並非幾十行, 會占據文章很大篇幅,請參考GitHub鏈接:stock-analysis

測試結果分析

分析說明: 由於個人水平有限,所以只能以我淺薄的知識來解釋我看到的,如果你有興趣做出自己的解讀,可以翻看源代碼,自行測試。就不用說我業余了,我的確是業余玩家^_^

再者這裏的測試至少是存在以下問題的

  1. 實際交易中,在漲停板或跌停板不一定能買得進去或賣的出去.
  2. 實際交易中買入或賣出的價格並沒有回測中那麽理想
  3. 實際交易中,不會只買一只股票。

我關註的測試結果主要如下:

  • 該策略是否適用於所有股票,即測試的所有股票都能盈利麽,如果不是,那麽盈利的概率如何。
  • 回撤比例。

所以選擇以下指標用於分析結果

  • 最終收益情況
  • 最大回測比例
  • 交易次數

最終產生數據格式如下

code,cum return,end,max drawdown,start,trade count
000008,99.6340721572,2018-03-12,37.6096792448,2011-03-22,24
000060,-34.5886186243,2018-03-12,49.0665092914,2011-03-15,35
000063,36.5405019876,2018-03-12,44.1047335728,2011-03-15,38
000069,-61.6228879039,2018-03-12,64.7843454103,2011-03-15,41
000100,88.7160620486,2018-03-12,44.7998410399,2011-03-15,29

這裏的cum return指累計收益,max drawdown指最大回撤比例,單位都是%

上證指數走勢圖

首先瞧瞧上證指數走勢圖
執行以下命令

python index_data.py

技術分享圖片

這裏選擇近七年的數據,我覺得還是可以的,因為有橫盤期,上漲期,下跌期,所以可以檢驗策略是否能夠逃過下跌期,以及能不能在橫盤期有所作為。因為本文可能更註重的是如何獲取數據,及編寫策略,最後數據分析

以下輸出通過執行以下命令:

python strategy_sma.py index
python strategy_random.py index
python strategy_dual_trust.py index

上證指數雙均線策略收益圖

輸出如下

start at 2011-03-16
Total trades: 35
Final portfolio value: $1399434.50
Cumulative returns: 39.94 %
Max. drawdown: 31.97 %
end at 2018-03-13

收益圖如下

技術分享圖片

上證指數隨機策略收益圖

輸出如下

Total trades: 73
Final portfolio value: $1173928.61
Cumulative returns: 17.39 %
Max. drawdown: 38.10 %
end at 2018-03-13

收益圖如下

技術分享圖片

上證指數daul thrust策略收益圖

輸出如下

start at 2011-03-16
Total trades: 32
Final portfolio value: $1860958.06
Cumulative returns: 86.10 %
Max. drawdown: 21.70 %
end at 2018-03-13

收益圖如下

技術分享圖片

值得註意的是用tushare下載的上證指數的數據可能是有問題的,因為2015-03-27這天的最低價(Low)居然大於開盤價(Open)!!!

這裏對上證指數的回測是基於上證指數可買,並且價格是指數值,並且可買一股。

股票雙均線策略結果

# 讀取雙均線策略輸出結果
sma = pd.read_csv("result/strategy_sma.csv")

# 查看數據前5條
sma.head()
   code  cum return         end  max drawdown       start  trade count
0     8   99.634072  2018-03-12     37.609679  2011-03-22           24
1    60  -34.588619  2018-03-12     49.066509  2011-03-15           35
2    63   36.540502  2018-03-12     44.104734  2011-03-15           38
3    69  -61.622888  2018-03-12     64.784345  2011-03-15           41
4   100   88.716062  2018-03-12     44.799841  2011-03-15           29

# 統計結果
sma.describe()
                code   cum return  max drawdown  trade count
count     299.000000   299.000000    299.000000   299.000000
mean   348897.023411    52.958878     46.737723    24.498328
std    202821.008968   127.114389     14.790231    12.048942
min         8.000000   -70.380766      4.832993     0.000000
25%    300140.500000   -16.502707     37.721394    13.000000
50%    300452.000000    20.156509     46.572972    29.000000
75%    600192.500000    73.175792     58.063121    34.000000
max    603858.000000  1236.661103     78.002843    42.000000

股票隨機策略結果

rand = pd.read_csv("result/strategy_random.csv")

rand.head()
Out[120]: 
   code  cum return         end  max drawdown       start  trade count
0     8   57.171853  2018-03-12     42.133326  2011-03-22           41
1    60    0.717321  2018-03-12     64.914506  2011-03-15           64
2    63   38.541158  2018-03-12     63.594804  2011-03-15           58
3    69  -13.064397  2018-03-12     48.498738  2011-03-15           70
4   100   48.973887  2018-03-12     42.911270  2011-03-15           58

rand.describe()
Out[121]: 
                code  cum return  max drawdown  trade count
count     299.000000  299.000000    299.000000   299.000000
mean   348897.023411   37.561896     53.839700    40.755853
std    202821.008968   98.958464     15.073869    20.628316
min         8.000000  -78.754755      6.127005     1.000000
25%    300140.500000  -29.201594     43.073549    22.000000
50%    300452.000000    5.418000     54.826874    45.000000
75%    600192.500000   69.043188     64.120164    59.000000
max    603858.000000  651.783545     90.516418    73.000000

股票dual thrust策略結果

dual = pd.read_csv("result/strategy_dual_trust.csv")

dual.head()
Out[123]: 
   code  cum return         end  max drawdown       start  trade count
0     8   -1.724564  2018-03-12     47.924826  2011-03-22           21
1    60  -15.859906  2018-03-12     48.323636  2011-03-15           30
2    63   46.218235  2018-03-12     59.273602  2011-03-15           35
3    69   22.708655  2018-03-12     33.797895  2011-03-15           34
4   100  140.985523  2018-03-12     39.504217  2011-03-15           24

dual.describe()
Out[24]: 
                code  cum return  max drawdown  trade count
count     299.000000  299.000000    299.000000   299.000000
mean   348897.023411   43.451147     46.518626    21.548495
std    202821.008968   95.254409     17.610361    11.283421
min         8.000000  -71.021800      0.000000     0.000000
25%    300140.500000  -17.341418     33.842472    11.000000
50%    300452.000000   17.209486     46.212199    25.000000
75%    600192.500000   75.395195     60.644285    31.000000
max    603858.000000  838.836061     82.656125    46.000000

由於pyalgotrade框架自身的限制,我在這個交易策略中按照EventWindow的模式自行建立一個EventWindow的類。

海龜交易策略結果

以後補上

收益分析

從上面的回測結果你會發現累計收益無論是最大值還是平均值都是雙均線策略.

交易頻繁的是dual thrust

但是上面的分析其實是有問題的,因為這些股票中有很多的股票可能上市事件不長,所以會產生很大的誤導,因為太短時間的回測有很大的隨機性,這會導致,以為這個策略很好但是,其實知識恰好而已。

當然了, 我這裏的測試,其實也有一個很大的隨機因素的占比。

這裏讓我們將上市時間小於七年的股票去除,再次查看收益情況

回測股票數據如下

python strategy_sma.py
python strategy_randome.py
python strategy_dual_trust.py

雙均線策略

sma[sma.start > pd.to_datetime("2011-03-15")].describe()
Out[32]: 
                code  cum return  max drawdown  trade count
count     151.000000  151.000000    151.000000   151.000000
mean   332525.701987   31.358881     44.409062    15.390728
std    143561.914878  112.389358     16.168624    10.214352
min         8.000000  -70.380766      4.832993     0.000000
25%    300299.500000  -23.127234     33.424244     6.000000
50%    300470.000000    2.206130     44.971140    13.000000
75%    300637.500000   33.425479     56.288630    24.500000
max    603858.000000  828.628299     78.002843    37.000000

隨機策略

rand[sma.start > pd.to_datetime("2011-03-15")].describe()
Out[33]: 
                code  cum return  max drawdown  trade count
count     151.000000  151.000000    151.000000   151.000000
mean   332525.701987   17.033825     52.263797    24.821192
std    143561.914878   86.767753     16.254297    15.643140
min         8.000000  -70.347804      6.127005     1.000000
25%    300299.500000  -34.364267     42.012492    10.500000
50%    300470.000000   -4.734439     54.193410    22.000000
75%    300637.500000   40.879252     63.065449    38.500000
max    603858.000000  651.783545     88.746525    60.000000

dual thrus策略

dual[sma.start > pd.to_datetime("2011-03-15")].describe()
Out[34]: 
                code  cum return  max drawdown  trade count
count     151.000000  151.000000    151.000000   151.000000
mean   332525.701987   24.231603     43.172343    13.284768
std    143561.914878   74.396766     19.763869     9.501142
min         8.000000  -65.661905      0.000000     0.000000
25%    300299.500000  -22.606043     28.152275     5.000000
50%    300470.000000   -0.466305     42.328093    11.000000
75%    300637.500000   48.149141     60.095440    21.500000
max    603858.000000  328.261149     82.357505    35.000000

最後瞧瞧獲得收益的概率
這裏假設最低基準是支付寶的收益,即4%,如果七年後的收益小於31%都是虧損的,計算方法如下。

from math import pow

pow(1.04, 7)
Out[38]: 1.3159317792358403

雙均線策略盈利概率

len(sma[sma.start > pd.to_datetime("2011-03-15")][sma["cum return"] > 31])/len(sma[sma.start > pd.to_datetime("2011-03-15")])

Out[40]: 0.271523178807947

隨機策略盈利概率

len(rand[sma.start > pd.to_datetime("2011-03-15")][rand["cum return"] > 31])/len(rand[sma.start > pd.to_datetime("2011-03-15")])

Out[42]: 0.2913907284768212

dual thrust策略盈利概率

len(dual[sma.start > pd.to_datetime("2011-03-15")][dual["cum return"] > 31])/len(dual[sma.start > pd.to_datetime("2011-03-15")])

Out[43]: 0.2847682119205298

總結

好吧,就收益分析而言居然隨機策略的盈利概率居然大於其他兩個策略,而且概率都小於50%.

這裏的分析還是有很大的局限性,比如數據的頻度,以及樣本的大小。

所以就就這個不太嚴謹的回測分析會發現,在時間長度為七年的條件下,單純技術分析似乎勝率不大,但是這裏沒有在回測之前篩選一些股票,是一個不太現實的問題,比如一些基本面的數據。再者這裏沒有設置調倉,且是全倉。這裏當且僅當是股票分析的一篇分析層次超淺的文章吧。

後面會寫pyalgotrade的源碼分析以及使用說明.

最後的最後

關註以下再走吧。。。^_^

數據分析之A股市場技術分析是否可行