可轉(zhuǎn)債交易薅羊毛策略 — Python 量化投資實(shí)戰(zhàn)教程(10)
往期推薦
以下內(nèi)容僅供參考學(xué)習(xí),不構(gòu)成投資意見(jiàn)。
可轉(zhuǎn)債,是一種具有中國(guó)特色的、受到國(guó)家管理和保護(hù)的債券,之所以說(shuō)它很有特色,是因?yàn)樗砩暇邆淞藘煞N閃閃發(fā)光的特性:
1.債性 — 安全保本
可轉(zhuǎn)債本質(zhì)上是一種公司的債券,也就是我們生活中所說(shuō)的“欠條”,每張“欠條”的價(jià)值是100塊錢。
如果你持有一張可轉(zhuǎn)債,它就代表著上市公司欠你100塊錢,而且未來(lái)必須償還這筆錢并附帶利息。截止到目前,所有上市的可轉(zhuǎn)債,都完成了保本的使命。
也就是說(shuō),如果你在轉(zhuǎn)債價(jià)格低于100元的時(shí)候買入,此時(shí)就是保本的。而且公司在贖回時(shí),會(huì)付你一定的利息,所以當(dāng)你在可轉(zhuǎn)債價(jià)格低于100元的時(shí)候買入,就是保本保利的。
2.股性(可轉(zhuǎn)換) — 存在套利空間
可轉(zhuǎn)債是一張可轉(zhuǎn)換為股票的公司債券。比如目前 晨光轉(zhuǎn)債 轉(zhuǎn)股價(jià)為 12.25 元,100/12.25=8.16,那么一張晨光轉(zhuǎn)債會(huì)被取證轉(zhuǎn)為8股晨光生物,其余的尾數(shù)會(huì)被轉(zhuǎn)換為證券賬戶資金。
目前晨光生物正股16.10元,轉(zhuǎn)換后,你相當(dāng)于獲得了8*16.10=128.8元的股票,加上剛剛尾數(shù)補(bǔ)充的賬戶資金,轉(zhuǎn)股后你相當(dāng)于獲得了131.43元,131.43元被稱為轉(zhuǎn)股價(jià)值。
也就是說(shuō),如果你在前一天收盤(pán)前買入了1張晨光轉(zhuǎn)債,并在當(dāng)前轉(zhuǎn)換為股票,第二天你會(huì)獲得價(jià)值為131.43元的股票+證券賬戶資金。目前晨光轉(zhuǎn)債的價(jià)格為 130.2 元,凈利0.93%,但這個(gè)套利邏輯有個(gè)大前提:第二天開(kāi)盤(pán)股價(jià)不會(huì)跌,如果跌了,你就拿不到這么多的價(jià)值,甚至有可能虧本。
下面本文要研究和利用的,不是可轉(zhuǎn)債的股性,而是可轉(zhuǎn)債的債性。
就如前面所說(shuō)的,如果你買入100元以下的可轉(zhuǎn)債,除非公司老板帶著小姨子跑路,否則都是保本的。于是就有了下面這個(gè)自動(dòng)交易的邏輯:
對(duì)于100元以下的可轉(zhuǎn)債,觸發(fā)某種上漲信號(hào)時(shí),買入。上漲0.5%則賣出。如果下跌則一直持有。
上漲就賺了,下跌的話長(zhǎng)期持有也不虧,利用可轉(zhuǎn)債的債性及T+0交易的特性實(shí)行日內(nèi)高頻率交易就是這個(gè)策略的主要邏輯。
1.準(zhǔn)備
開(kāi)始之前,你要確保Python和pip已經(jīng)成功安裝在電腦上,如果沒(méi)有,可以訪問(wèn)這篇文章:超詳細(xì)Python安裝指南 進(jìn)行安裝。
如果你用Python的目的是數(shù)據(jù)分析,可以直接安裝Anaconda:Python數(shù)據(jù)分析與挖掘好幫手—Anaconda,它內(nèi)置了Python和pip.
此外,推薦大家用VSCode編輯器,它有許多的優(yōu)點(diǎn):Python 編程的最好搭檔—VSCode 詳細(xì)指南。
請(qǐng)選擇以下任一種方式輸入命令安裝依賴:
1. Windows 環(huán)境 打開(kāi) Cmd (開(kāi)始-運(yùn)行-CMD)。
2. MacOS 環(huán)境 打開(kāi) Terminal (command+空格輸入Terminal)。
3. 如果你用的是 VSCode編輯器 或 Pycharm,可以直接使用界面下方的Terminal.
pip install backtrader
pip install easytrader看到 Successfully installed xxx 則說(shuō)明安裝成功。
某些券商在登錄的時(shí)候可能需要識(shí)別驗(yàn)證碼,這時(shí)候需要下載tesseract:
1.下載并安裝tesseract
前往 tesseract-ocr 官網(wǎng)下載二進(jìn)制包,此外你也可以在Python實(shí)用寶典公眾號(hào)后臺(tái)回復(fù): 量化投資10,直接獲得本文源代碼和tesseract的安裝包。
雙擊下載下來(lái)的安裝包,然后傻瓜式安裝就可以,這里只需要注意一點(diǎn):安裝過(guò)程中有一個(gè)讓你選擇 Additional language data(download) 表示選擇的話幫你下載語(yǔ)言包,這里最好不要選擇勾選,因?yàn)楣催x的話,安裝過(guò)程非常慢,本教程只需要用到數(shù)字和英文識(shí)別而已。
2.配置環(huán)境變量
右擊我的電腦/計(jì)算機(jī),選擇屬性,然后選擇高級(jí)屬性設(shè)置,選擇環(huán)境變量,在系統(tǒng)變量的path變量中添加你的 tesseract 目錄就可以了

3.判斷是否安裝成功
在命令行中輸入:
tesseract --version出現(xiàn)下面的提示說(shuō)明安裝成功:

2.回測(cè)
按照這個(gè)策略的邏輯,回測(cè)的目的不在于探討是否會(huì)虧損,而在于最高能賺多少。
為什么不需要探討虧損?因?yàn)閷?shí)際上即便我們買入80元的廣匯轉(zhuǎn)債,一直持有,3年后它以100元的價(jià)格贖回,我們的3年收益都達(dá)到了20%,平均年化率達(dá)6.67%。
當(dāng)然,風(fēng)險(xiǎn)是有的,廣匯老板如果帶著小姨子跑路了,那你的轉(zhuǎn)債可能一文不值,但是目前還沒(méi)有出現(xiàn)過(guò)這種情況,因?yàn)槿绻霈F(xiàn)了這種情況,將出現(xiàn)嚴(yán)重的信用危機(jī),監(jiān)管部門不可能允許這種情況發(fā)生。
回測(cè)的主要目的在于:怎樣進(jìn)行自動(dòng)化交易,能最大化我們的收益。
關(guān)于編寫(xiě)策略的方法我們前面九篇系列文章都講的很清楚了,這里就不再贅述,這里只重點(diǎn)研究買入的邏輯。
2.1 基于分鐘K線的Sma金叉策略
這是最簡(jiǎn)單的策略,如果sma5和sma10實(shí)現(xiàn)金叉,則買入債券。漲0.5%則賣出,否則不動(dòng)。
部分代碼如下:
def golden(self, a, b):
if a[-1] - b[-1] < 0 and a[0] - b[0] > 0:
return True
else:
return False
def next(self):
if self.order:
return
if not self.position:
if self.golden(self.exp1, self.exp2):
self.order = self.buy()
self.params.buydays.append(self.datas[0].datetime.date(0))
else:
condition = (self.dataclose[0] - self.bar_executed_close) / self.bar_executed_close
if condition > 0.005:
self.order = self.sell()
self.dead = False
self.params.selldays.append(self.datas[0].datetime.date(0))
self.params.hold_days.append((self.params.selldays[-1] - self.params.buydays[-1]).days)
if self.params.buydays[-1] != self.params.selldays[-1]:
days = get_every_day(self.params.buydays[-1], self.params.selldays[-1])
for day in days:
self.params.hold_count[day] += 1隨機(jī)抽取了15只低價(jià)債券,回測(cè)了 2020-9-15 至 2020-11-8 之間的走勢(shì),一共交易了59次,每次買入50股。

可以看到每次交易的平均持有日期為4天,這是平均值,如果你看中位數(shù)會(huì)更大,有些交易可能連續(xù)持有了30天,有些可能當(dāng)天買入當(dāng)天賣出,兩極分化比較大。
不過(guò)即便這樣,按平均每次收益0.5%、平均每支債券價(jià)格為80元的情況來(lái)看, 59次交易*0.005*80*50股 = 1180元,如果可轉(zhuǎn)債每次交易的平均手續(xù)費(fèi)是1元,那么除去手續(xù)費(fèi) 1180-2*59=1062元。看起來(lái)是一筆可觀的羊毛,但是你必須忍受有部分資金被套牢在一些低價(jià)轉(zhuǎn)債的情況。
2.2 基于分鐘K線的EMA金叉的策略
接下來(lái)我們測(cè)試一下使用EMA均線的效果,這里選擇的是ema(12)和ema(50):
def golden(self, a, b):
if a[-1] - b[-1] < 0 and a[0] - b[0] > 0:
return True
else:
return False
def next(self):
if self.order:
return
if not self.position:
if self.golden(self.exp1, self.exp2):
self.order = self.buy()
self.params.buydays.append(self.datas[0].datetime.date(0))
else:
condition = (self.dataclose[0] - self.bar_executed_close) / self.bar_executed_close
if condition > 0.005:
self.order = self.sell()
self.dead = False
self.params.selldays.append(self.datas[0].datetime.date(0))
self.params.hold_days.append((self.params.selldays[-1] - self.params.buydays[-1]).days)
if self.params.buydays[-1] != self.params.selldays[-1]:
days = get_every_day(self.params.buydays[-1], self.params.selldays[-1])
for day in days:
self.params.hold_count[day] += 1效果如下:

交易次數(shù)相對(duì)于sma策略少了14次,所以收益肯定也會(huì)下降。平均持有日期達(dá)到了5.4天,相比于sma策略也有所提高。因此ema在我們的整個(gè)策略邏輯里表現(xiàn)地比sma策略稍差一些。
上面展示了兩種策略在我們的低價(jià)轉(zhuǎn)債投資邏輯里的應(yīng)用和分析,由于篇幅關(guān)系這里就不再展示一些其他策略的回測(cè)結(jié)果,大家有興趣可以自己試一下。
3.自動(dòng)交易
使用easytrader模塊,可以簡(jiǎn)單實(shí)現(xiàn)一個(gè)單進(jìn)程的自動(dòng)交易程序。
對(duì)于本策略而言單進(jìn)程的自動(dòng)交易程序也夠了,因?yàn)檫@個(gè)策略要求的實(shí)時(shí)性并不是很高。
由于這部分代碼無(wú)法脫敏,因此不能進(jìn)行深入地講解。邏輯并不難,每分鐘對(duì)指定的股票進(jìn)行監(jiān)控,當(dāng)其符合相關(guān)策略時(shí)進(jìn)行買入或賣出,下面進(jìn)行簡(jiǎn)單的講解。
1.讀取今日需檢測(cè)的股票
在一天開(kāi)始交易之前,需要獲取今日所符合條件的低價(jià)可轉(zhuǎn)債:
def start():
account = Account()
codes = read_today_codes()
logger.info(f"總股票數(shù) {len(codes)}")
last_ping_time = datetime.datetime.now().timestamp()2.巡檢
在交易時(shí)間里,循環(huán)檢測(cè)可轉(zhuǎn)債是否符合買入策略:
while True:
# 是否在交易時(shí)間
if not check_time():
continue
for code in codes:
now = int(datetime.datetime.now().timestamp())
logger.info(f"{now} - {code}")
try:
# TODO: 異步執(zhí)行算法
buy_dict = algorithm(code)
if not buy_dict:
continue
logger.info(buy_dict)
buy_time = list(buy_dict.keys())[0]
buy_value = list(buy_dict.values())[0]
if abs(int(buy_time.timestamp()) - now) < 300:
logic(account, code, buy_time, buy_value)
except Exception as e:
traceback.print_exc()
logger.info(e)
# send_mail(f"算法解析失敗: {traceback.print_exc()}", "WRONG", code)如果符合買入規(guī)則,在logic函數(shù)內(nèi),便會(huì)對(duì)可轉(zhuǎn)債發(fā)布買單,同時(shí)掛一個(gè)0.5%利潤(rùn)的賣單。
3.賣單兜底
理想化情況下,在你掛了買單后立馬成交,此時(shí)順利掛出賣單。
不理想情況下,在你掛了買單后,幾分鐘后才成交,出現(xiàn)這種異步的情況后,賣單無(wú)法順利掛出。
所以我們需要有一個(gè)兜底的措施:
now = datetime.datetime.now()
new_ping_time = now.timestamp()
if new_ping_time - last_ping_time > 15:
# 大于15秒,檢測(cè)持倉(cāng),把未委托賣出的單子委托賣出
account.every_day_sell()
last_ping_time = new_ping_time
logger.info(f"{datetime.datetime.strftime(now, '%Y-%m-%d %H:%M:%S')} ping")每次巡檢任務(wù)都檢查持倉(cāng),如果存在未掛出賣單的可轉(zhuǎn)債,則按成本價(jià)+0.5%的利潤(rùn)掛出賣單。這樣保證沒(méi)有漏掉的可轉(zhuǎn)債。
上述只是簡(jiǎn)單的幾個(gè)步驟,實(shí)現(xiàn)上你會(huì)還有許多細(xì)節(jié)需要考慮,大家可以自己嘗試實(shí)現(xiàn)一個(gè)這樣的自動(dòng)交易流程。
我們的文章到此就結(jié)束啦,如果你喜歡今天的Python 實(shí)戰(zhàn)教程,請(qǐng)持續(xù)關(guān)注Python實(shí)用寶典。
有任何問(wèn)題,可以在公眾號(hào)后臺(tái)回復(fù):加群,回答相應(yīng)紅字驗(yàn)證信息,進(jìn)入互助群詢問(wèn)。
原創(chuàng)不易,希望你能在下面點(diǎn)個(gè)贊和在看支持我繼續(xù)創(chuàng)作,謝謝!
點(diǎn)擊下方閱讀原文可獲得更好的閱讀體驗(yàn)
Python實(shí)用寶典 (pythondict.com)
不只是一個(gè)寶典
歡迎關(guān)注公眾號(hào):Python實(shí)用寶典
