1. 程式人生 > 程式設計 >python實現馬丁策略的例項詳解

python實現馬丁策略的例項詳解

馬丁策略本來是一種賭博方法,但在投資界應用也很廣泛,不過對於投資者來說馬丁策略過於簡單,所以本文將其改進並使得其在震盪市中獲利,以下說明如何實現馬丁策略。

策略

逢跌加倉,間隔由自己決定,每次加倉是當前倉位的一倍。
連續跌兩次賣出,且賣出一半倉位。
如果爆倉則全倉賣出止損。
初始持倉設定為10%~25%,則可進行2到3次補倉。

初始化馬丁策略類屬性

def __init__(self,startcash,start,end):
 self.cash = startcash #初始化現金
 self.hold = 0 #初始化持倉金額
 self.holdper = self.hold /startcash #初始化倉位
 self.log = [] #初始化日誌
 self.cost = 0 #成本價 
 self.stock_num = 0 #股票數量
 self.starttime = start #起始時間
 self.endtime = end #終止時間
 self.quantlog = [] #交易量記錄
 self.earn = [] #總資產記錄
 self.num_log = []
 self.droplog = [0]

為了記錄每次買賣倉位的變化初始化了各種列表。

交易函式

首先匯入需要的模組

import pandas as pd 
import numpy as np
import tushare as ts 
import matplotlib.pyplot as plt
 def buy(self,currentprice,count):

 self.cash -= currentprice*count
 self.log.append('buy')
 self.hold += currentprice*count
 self.holdper = self.hold / (self.cash+ self.hold) 
 self.stock_num += count
 self.cost = self.hold / self.stock_num
 self.quantlog.append(count//100)
 print('買入價:%.2f,手數:%d,現在成本價:%.2f,現在持倉:%.2f,現在籌碼:%d' %(currentprice,count//100,self.cost,self.holdper,self.stock_num//100))
 self.earn.append(self.cash+ currentprice*self.stock_num)
 self.num_log.append(self.stock_num)
 self.droplog = [0]
 
 def sell(self,count):
 self.cash += currentprice*count
 self.stock_num -= count
 self.log.append('sell')
 self.hold = self.stock_num*self.cost
 self.holdper = self.hold / (self.cash + self.hold)
 #self.cost = self.hold / self.stock_num
 print('賣出價:%.2f,手數:%d,現在成本價:%.2f,現在持倉:%.2f,現在籌碼:%d' %(currentprice,self.stock_num//100))
 self.quantlog.append(count//100)    
 self.earn.append(self.cash+ currentprice*self.stock_num)
 self.num_log.append(self.stock_num)
 
 def holdstock(self,currentprice):
 self.log.append('hold')
 #print('持有,現在倉位為:%.2f。現在成本:%.2f' %(self.holdper,self.cost))
 self.quantlog.append(0)
 self.earn.append(self.cash+ currentprice*self.stock_num)
 self.num_log.append(self.stock_num)

持倉成本的計算方式是利用總持倉金額除以總手數,賣出時不改變持倉成本。持有則是不做任何操作只記錄日誌

資料介面

def get_stock(self,code):
 df=ts.get_k_data(code,autype='qfq',start= self.starttime,end= self.endtime)
 df.index=pd.to_datetime(df.date)
 df=df[['open','high','low','close','volume']]
 return df

資料介面使用tushare,也可使用pro介面,到官網註冊領取token。

token = '輸入你的token'
pro = ts.pro_api()
ts.set_token(token)
 def get_stock_pro(self,code):
 code = code + '.SH'
 df = pro.daily(ts_code= code,start_date = self.starttime,end_date= self.endtime)
 return df

資料結構:

在這裡插入圖片描述

回測函式

 def startback(self,data,everyChange,accDropday):
 """
 回測函式
 """
 for i in range(len(data)):
  if i < 1:
  continue
  if i < accDropday:
  drop = backtesting.accumulateVar(everyChange,i,i)
  #print('現在累計漲跌幅度為:%.2f'%(drop))
  self.martin(data[i],data[i-1],drop,i)
  elif i < len(data)-2:
  drop = backtesting.accumulateVar(everyChange,accDropday)
  #print('現在累計漲跌幅度為:%.2f'%(drop))
  self.martin(data[i],i)
  else:
  if self.stock_num > 0:
   self.sell(data[-1],self.stock_num)
  else: self.holdstock(data[i])

因為要計算每日漲跌幅,要計算差分,所以第一天的資料不能計算在for迴圈中跳過,accDropday是累計跌幅的最大計算天數,用來控制入場,當累計跌幅大於某個數值且倉位為0%時可再次入場。以下是入場函式:

def enter(self,ex_price,accuDrop):
 if accuDrop < -0.01:#and ex_price > currentprice:
  count = (self.cash+self.hold) *0.24 // currentprice //100 * 100
  print('再次入場')
  self.buy(currentprice,count)
 else: self.holdstock(currentprice)

入場倉位選擇0.24則可進行兩次抄底,如果抄底間隔為7%可承受最大跌幅為14%。

策略函式

 def martin(self,accuDrop,i):
 diff = (ex_price - currentprice)/ex_price
 self.droplog.append(diff)

 if sum(self.droplog) <= 0:
  self.droplog = [0]
 
 if self.stock_num//100 > 1:
  if sum(self.droplog) >= 0.04:
  if self.holdper*2 < 0.24:
   count =(self.cash+self.hold) *(0.25-self.holdper) // currentprice //100 * 100
   self.buy(currentprice,count)
  elif self.holdper*2 < 1 and (self.hold/currentprice)//100 *100 > 0 and backtesting.computeCon(self.log) < 5:
   self.buy(currentprice,(self.hold/currentprice)//100 *100)
   
  else: self.sell(currentprice,self.stock_num//100 *100);print('及時止損')

  elif (everyChange[i-2] < 0 and everyChange[i-1] <0 and self.cost < currentprice):# or (everyChange[i-1] < -0.04 and self.cost < currentprice):
   
  if (self.stock_num > 0) and ((self.stock_num*(1/2)//100*100) > 0):
   
   self.sell(currentprice,self.stock_num*(1/2)//100*100 )

   #print("現在累計漲跌幅為: %.3f" %(accuDrop))
  elif self.stock_num == 100: self.sell(currentprice,100)
  else: self.holdstock(currentprice)
  else: self.holdstock(currentprice)
 else: self.enter(currentprice,accuDrop)

首先構建了droplog專門用於計算累計漲跌幅,當其大於0時重置為0,每次購買後也將其重置為0。當跌幅大於0.04則買入,一下為流程圖(因為作圖軟體Visustin為試用版所以有水印,兩個圖可以結合來看):

在這裡插入圖片描述
在這裡插入圖片描述

此策略函式可以改成其他策略甚至是反馬丁,因為交易函式可以通用。

作圖和輸出結果

buylog = pd.Series(broker.log)
close = data.copy()
buy = np.zeros(len(close))
sell = np.zeros(len(close))
for i in range(len(buylog)):
 if buylog[i] == 'buy':
 buy[i] = close[i]
 elif buylog[i] == 'sell':
 sell[i] = close[i]

buy = pd.Series(buy)
sell = pd.Series(sell)
buy.index = close.index
sell.index = close.index
quantlog = pd.Series(broker.quantlog)
quantlog.index = close.index
earn = pd.Series(broker.earn)
earn.index = close.index

buy = buy.loc[buy > 0]
sell = sell.loc[sell>0]
plt.plot(close)
plt.scatter(buy.index,buy,label = 'buy')
plt.scatter(sell.index,sell,label = 'sell')
plt.title('馬丁策略')
plt.legend()

#畫圖
plt.rcParams['font.sans-serif'] = ['SimHei']

fig,(ax1,ax2,ax3) = plt.subplots(3,figsize=(15,8))

ax1.plot(close)
ax1.scatter(buy.index,label = 'buy',color = 'red')
ax1.scatter(sell.index,label = 'sell',color = 'green')
ax1.set_ylabel('Price')
ax1.grid(True)
ax1.legend()

ax1.xaxis_date()
ax2.bar(quantlog.index,quantlog,width = 5)
ax2.set_ylabel('Volume')

ax2.xaxis_date()
ax2.grid(True)
ax3.xaxis_date()
ax3.plot(earn)
ax3.set_ylabel('總資產包括浮盈')
plt.show()

馬丁策略回測(中通客車)

在這裡插入圖片描述

交易日誌

到此這篇關於python實現馬丁策略的例項詳解的文章就介紹到這了,更多相關python馬丁策略內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!