如果你在马来西亚股市混了一段日子,
我相信你一定有读过关于官有缘先生的文章,
官老前辈在商界和投资界都非常有名
他也跟大家分享他选股的黄金法则
My golden rule is the company must report increasing profit for 2 consecutive quarters and the price is selling below P/E 10.
今天我们就来回测这个黄金法则
选股规则
- 只选PE小于10的公司
- 只选前10间连续两个季度EPS成长最高的公司
- 没有计算交易成本
- 每月重复以上步骤
import pandas as pd
from datetime import datetime
import warnings
import numpy as np
import matplotlib.pyplot as plt
from dateutil.relativedelta import relativedelta
import math
warnings.filterwarnings("ignore")
plt.style.use('seaborn')
stock_data = pd.read_csv('M.csv')
KLSE = pd.read_csv('MKLSE.csv')
codename = pd.read_csv('codename.csv')
stock_data['Change'] = (stock_data['Change']/100) + 1
def Code2Name(code):
code = code.replace('KL','')
row = codename.loc[codename['Code'] == code]
return row['Name'].values[0]
def calculate_eps_growth(stock_data):
stock_data['Changes_Next_Month'] = stock_data.groupby('Code')['Change'].shift(-1)
stock_data['EPS_Increase'] = stock_data.groupby('Code')['EPS'].diff()
stock_data['EPS_Increase'] = stock_data['EPS_Increase'].replace(0, None)
stock_data['EPS_Increase'] = stock_data['EPS_Increase'].bfill()
stock_data['Previous_EPS'] = stock_data['EPS'] - stock_data['EPS_Increase']
stock_data['EPS_Increase_2'] = stock_data.groupby('Code')['Previous_EPS'].diff()
stock_data['EPS_Increase_2'] = stock_data['EPS_Increase_2'].replace(0, None)
stock_data['EPS_Increase_2'] = stock_data['EPS_Increase_2'].bfill()
stock_data['Previous_EPS_2'] = stock_data['Previous_EPS'] - stock_data['EPS_Increase_2']
stock_data['EPS_Increase'] = stock_data['EPS_Increase']/abs(stock_data['Previous_EPS']) + 1
stock_data['EPS_Increase_2'] = stock_data['EPS_Increase_2']/abs(stock_data['Previous_EPS_2']) + 1
stock_data['2_Consecutive_EPS_Growth'] = (stock_data['EPS_Increase'] * stock_data['EPS_Increase_2'])
stock_data = stock_data[~stock_data.isin([np.nan, np.inf, -np.inf]).any(1)]
return stock_data
stock_data = calculate_eps_growth(stock_data)
NumberOfShares = 10
InitialCapital = 1000
def cagr(df):
start = datetime.strptime(df.iloc[0].name, '%Y-%m-%d')
end = datetime.strptime(df.iloc[-1].name, '%Y-%m-%d')
num_periods = relativedelta(start, end).years
return ((df['Capital'][0] / df['Capital'][-1]) ** (1 / (num_periods - 1)) - 1)*100.0
stock_data = stock_data[stock_data['PE'] <= 10]
stock_data = stock_data.sort_values(['Datetime','2_Consecutive_EPS_Growth'],ascending = False)
stock_data = stock_data.groupby(['Datetime']).head(NumberOfShares)
output = pd.DataFrame()
stock_data['Code'] += ' '
stock_data_groupby = stock_data.groupby('Datetime')
output['Shares_bought'] = stock_data_groupby['Code'].sum()
output['Number of shares'] = stock_data_groupby.size()
output['Average gains'] = stock_data_groupby['Changes_Next_Month'].mean()
output['Median gains'] = stock_data_groupby['Changes_Next_Month'].median()
output['Max gains'] = stock_data_groupby['Changes_Next_Month'].max()
output['Min gains'] = stock_data_groupby['Changes_Next_Month'].min()
output['Average gains'].replace(np.inf, 0, inplace=True)
KLSE = KLSE.set_index('Date')
output = output.join(KLSE['Change'])
output['Benchmark'] = 0
output['Capital'] = 0
output['Trading Fees'] = 0
output = output.dropna()
for i in range(0,len(output)):
if i == 0:
output['Capital'][i] = ((output['Average gains'][i]) * InitialCapital)
output['Benchmark'][i] = (output['Change'][i] + 1.0) * InitialCapital
else:
#print(((output['Average gains'][i]+1.0).cumprod()*output['Capital'][i-1]) - NumberOfShares*TradingFees)
output['Capital'][i] = ((output['Average gains'][i])*output['Capital'][i-1])
output['Benchmark'][i] = (output['Change'][i] + 1.0)*output['Benchmark'][i-1]
output['Capital (No fees)'] = (output['Average gains']).cumprod()*InitialCapital
#output['Trading Fees'] = output['Capital (No fees)'] - output['Capital']
output['Average gains'] = output['Average gains']*100.0
CompoundedGrowth = cagr(output)
for index, row in output.iterrows():
codes = row['Shares_bought'].split()
names = [Code2Name(c) for c in codes]
names = ' '.join(names)
output.at[index,'Shares_bought'] = names
所以我们都买了什么股?
pd.set_option("display.max_rows", None, "display.max_columns", None)
print(output[['Shares_bought']].reset_index())
Datetime Shares_bought
0 2011-03-31 DNONCE MASTER KHIND RSAWIT ICAP MBSB ECOFIRS G...
1 2011-04-30 BLDPLNT SSTEEL INSAS DNONCE ASTINO YTL MALTON ...
2 2011-05-31 TNLOGIS SSTEEL ANALABS AMPROP INSAS SMCAP MAA ...
3 2011-06-30 TNLOGIS SSTEEL ANALABS AMPROP INSAS SMCAP MAA ...
4 2011-07-31 TNLOGIS SSTEEL SURIA ANALABS AMPROP INSAS SMCA...
5 2011-08-31 TANCO FN LFECORP ANALABS SMCAP ASTINO GUNUNG I...
6 2011-09-30 ARK TANCO FN SURIA SMCAP GUNUNG IREKA FCW GPHA...
7 2011-10-31 ARK TANCO FN SMCAP GUNUNG IREKA FCW GPHAROS KS...
8 2011-11-30 BINTAI DNEX KPPROP TGL KERJAYA HCK KPS SYCAL Q...
9 2011-12-31 SYF BINTAI DNEX KPPROP TGL KERJAYA IQGROUP IGB...
10 2012-01-31 PEB SYF BINTAI DNEX KPPROP TGL KERJAYA IQGROUP...
11 2012-02-29 PEB SYF TIMWELL FARLIM ENCORP CIHLDG VERTICE M...
12 2012-03-31 KYM PEB FARLIM ENCORP CIHLDG ACME VERTICE MJPE...
13 2012-04-30 KYM FARLIM ENCORP ACME VERTICE MJPERAK CFM SCO...
14 2012-05-31 ATTA KYM TPC ACME MJPERAK KUCHAI LG KNM PASDEC...
15 2012-06-30 ATTA TPC MJPERAK KUCHAI LG KNM PASDEC JOHOTIN ...
16 2012-07-31 ATTA TPC MJPERAK KUCHAI LG KNM PASDEC JOHOTIN ...
17 2012-08-31 DYNACIA KTB GESHEN OCB SEACERA LSTEEL PO VIZIO...
18 2012-09-30 DYNACIA KTB OCB SEACERA LSTEEL PO VIZIONE MESB...
19 2012-10-31 DYNACIA KTB PEB TMCLIFE GESHEN OCB SEACERA LST...
20 2012-11-30 MUIIND PEB TMCLIFE TGL NYLEX SHH PO GOB RGB LA...
21 2012-12-31 MUIIND PEB TMCLIFE TGL WATTA NYLEX PO GOB OCNC...
22 2013-01-31 MUIIND TGL WATTA PO GOB RGB LATITUD PPHB MFLOU...
23 2013-02-28 LIENHOE PMHLDG OSK MTDACPI MUH EMICO YLI DBHD ...
24 2013-03-31 LIENHOE PMHLDG OSK MTDACPI ADVENTA MUH EMICO Y...
25 2013-04-30 POLY LIENHOE PMHLDG OSK MTDACPI ADVENTA MUH EM...
26 2013-05-31 MTDACPI POLY ASIAPAC ADVENTA CHHB MCT LIONIND ...
27 2013-06-30 MTDACPI POLY ASIAPAC CHHB MCT LIONIND DSONIC G...
28 2013-07-31 MTDACPI ASIAPAC ICAP CHHB MCT LIONIND DSONIC D...
29 2013-08-31 HUBLINE APEX NOTION MMSV ICAP KTB TAWIN ENGTEX...
30 2013-09-30 HUBLINE APEX NOTION MMSV ICAP KTB TAWIN BORNOI...
31 2013-10-31 HUBLINE APEX NOTION MMSV KTB TAWIN BORNOIL ENG...
32 2013-11-30 KSTAR CAB IMPIANA FACBIND LBS DWL MINHO KSSC E...
33 2013-12-31 KSTAR VS POHUAT CAB IMPIANA FACBIND LBS DWL MI...
34 2014-01-31 KSTAR VS HIGHTEC POHUAT CAB IMPIANA FACBIND LB...
35 2014-02-28 VS HIGHTEC HARNLEN POHUAT PMHLDG VIZIONE JCY N...
36 2014-03-31 HARNLEN PMHLDG VIZIONE JCY NAIM MENTIGA OMESTI...
37 2014-04-30 HARNLEN PMHLDG VIZIONE JCY NAIM MENTIGA ICAP O...
38 2014-05-31 AMPROP MTDACPI SNC IQZAN SEACERA EURO SKBSHUT ...
39 2014-06-30 AMPROP MTDACPI SNC IQZAN SEACERA EURO SKBSHUT ...
40 2014-07-31 DNONCE AMPROP MTDACPI SNC IQZAN SEACERA EURO S...
41 2014-08-31 KIMHIN DNONCE PJBUMI MRCB GLOTEC BHIC PDZ KUCH...
42 2014-09-30 DNONCE PJBUMI MRCB GLOTEC BHIC HIGHTEC PDZ KUC...
43 2014-10-31 KIMHIN PJBUMI MRCB GLOTEC DNONCE BHIC HIGHTEC ...
44 2014-11-30 TGL TALIWRK DNONCE MINHO FCW EKSONS CHGP HIGHT...
45 2014-12-31 TALIWRK DNONCE MINHO FCW EKSONS BJFOOD BJCORP ...
46 2015-01-31 TALIWRK MINHO FCW EKSONS BJFOOD BJCORP CHGP PR...
47 2015-02-28 JCBNEXT LEWEKO EKSONS JADI BJFOOD BJCORP ARBB ...
48 2015-03-31 ICON JCBNEXT BJCORP LEWEKO EKSONS JADI ARBB GP...
49 2015-04-30 ICON JCBNEXT BJCORP LEWEKO EKSONS JADI ARBB GP...
50 2015-05-31 ASIAPAC EASTLND MENTIGA ATAIMS BJCORP RESINTC ...
51 2015-06-30 ASIAPAC EASTLND MENTIGA ATAIMS BJLAND RESINTC ...
52 2015-07-31 ASIAPAC MENTIGA BJLAND RESINTC DUTALND PENSONI...
53 2015-08-31 SEACERA GUOCO DAYA SUCCESS TADMAX YONGTAI BJLA...
54 2015-09-30 SEACERA GUOCO DAYA SUCCESS TADMAX YONGTAI MYSC...
55 2015-10-31 SEACERA GUOCO DAYA TADMAX YONGTAI MYSCM UMS PL...
56 2015-11-30 GPHAROS NGGB MYSCM JCBNEXT DAYA INCKEN TIGER T...
57 2015-12-31 GPHAROS NGGB MYSCM HIGHTEC BJLAND JCBNEXT DAYA...
58 2016-01-31 GPHAROS NGGB MYSCM HIGHTEC BJLAND JCBNEXT DAYA...
59 2016-02-29 EMETALL SKBSHUT GETS HIGHTEC BJLAND EFFICEN RO...
60 2016-03-31 EMETALL SKBSHUT GETS SAPIND EFFICEN JKGLAND RO...
61 2016-04-30 EMETALL SKBSHUT REACH SAPIND EFFICEN JKGLAND R...
62 2016-05-31 DRBHCOM REACH SAPIND PARAGON MTRONIC KPS NPC K...
63 2016-06-30 SUBUR DRBHCOM REACH PARAGON MTRONIC KPS JKGLAN...
64 2016-07-31 SUBUR DRBHCOM REACH PARAGON MTRONIC KPS JKGLAN...
65 2016-08-31 SUBUR YKGI ZECON LIONPSIM LBICAP ANNJOO REACH ...
66 2016-09-30 TECGUAN YKGI ZECON LIONPSIM LBICAP ANNJOO REAC...
67 2016-10-31 TECGUAN YKGI ZECON LIONPSIM LBICAP ANNJOO REAC...
68 2016-11-30 TECGUAN EMICO PHB REACH BJASSET ATTA SMCAP KOM...
69 2016-12-31 EMICO PHB REACH ATTA SMCAP GPA BSLCORP CWG LAY...
70 2017-01-31 EMICO HIGHTEC PHB REACH ATTA SMCAP GPA CWG LAY...
71 2017-02-28 CNASIA HIGHTEC UMW MIECO LIONIND SUCCESS REACH...
72 2017-03-31 CNASIA UMW MIECO LIONIND SUCCESS EDEN EMICO ME...
73 2017-04-30 POLY CNASIA UMW MIECO LIONIND EDEN EMICO GUOCO...
74 2017-05-31 POLY ATAIMS BINTAI ACME BJASSET TIGER EATECH G...
75 2017-06-30 POLY ATAIMS BINTAI ACME BJASSET TIGER EATECH G...
76 2017-07-31 ATAIMS BINTAI ACME BJASSET TIGER EATECH GUOCO ...
77 2017-08-31 GRANFLO HUAAN LEWEKO MG LIONPSIM ZECON DWL BAR...
78 2017-09-30 GRANFLO HUAAN LEWEKO MG ZECON DWL BARAKAH MEDI...
79 2017-10-31 GRANFLO LEWEKO HUAAN MG ZECON DWL BARAKAH MEDI...
80 2017-11-30 MULPHA LIONPSIM HARNLEN HUAAN STAR TOYOINK HAR...
81 2017-12-31 MULPHA LIONPSIM HARNLEN HUAAN STAR TOYOINK BPL...
82 2018-01-31 MULPHA LIONPSIM HARNLEN HUAAN STAR TOYOINK BPL...
83 2018-02-28 PRKCORP AMPROP TPC JAKS DBE ALAM TOYOINK WATTA...
84 2018-03-31 PRKCORP SAPNRG AMPROP TPC JAKS DBE ALAM TOYOIN...
85 2018-04-30 PRKCORP SAPNRG AMPROP TPC JAKS DBE ALAM TOYOIN...
86 2018-05-31 SAPNRG HUMEIND OLYMPIA ARREIT JKGLAND ASIAPAC ...
87 2018-06-30 GUOCO MUDA HUMEIND OLYMPIA ARREIT ASIAPAC GUNU...
88 2018-07-31 MUDA HUMEIND OLYMPIA CCB ARREIT ASIAPAC GUNUNG...
89 2018-08-31 COASTAL AXIATA TIGER PEB CCB HTPADU VERTICE EA...
90 2018-09-30 COASTAL AXIATA TIGER PEB CCB HTPADU VERTICE EA...
91 2018-10-31 COASTAL WZSATU AXIATA TIGER PEB CCB AJIYA HTPA...
92 2018-11-30 FGV WZSATU PCCS FLBHD TADMAX ENGKAH AJIYA SWKP...
93 2018-12-31 FGV WZSATU UZMA PCCS FLBHD TADMAX ENGKAH AJIYA...
94 2019-01-31 FGV PCCS FLBHD TADMAX PJBUMI CVIEW SWKPLNT ITR...
95 2019-02-28 THPLANT DBHD ARBB CHUAN HWGB MAYBULK ADVENTA A...
96 2019-03-31 THPLANT DBHD ARBB CHUAN HWGB MAYBULK ADVENTA A...
97 2019-04-30 THPLANT DBHD CHUAN HWGB MAYBULK ADVENTA ARK IP...
98 2019-05-31 HENGYUAN SEB HBGLOB IJMPLNT ARBB MULPHA ASIAPA...
99 2019-06-30 HENGYUAN SEB HBGLOB IJMPLNT ARBB MULPHA ASIAPA...
100 2019-07-31 HENGYUAN SEB HBGLOB IJMPLNT ARBB MULPHA ASIAPA...
101 2019-08-31 DOLMITE PMCORP HENGYUAN ENGKAH PRESBHD ENGTEX ...
102 2019-09-30 DOLMITE PMCORP HENGYUAN ENGKAH PRESBHD ENGTEX ...
103 2019-10-31 DOLMITE PMCORP HENGYUAN ENGKAH PRESBHD ENGTEX ...
104 2019-11-30 FGV KHEESAN DBE DAYANG BONIA NAIM MJPERAK PINE...
105 2019-12-31 SCNWOLF FGV KHEESAN DBE DAYANG NAIM MJPERAK PI...
106 2020-01-31 SCNWOLF FGV KHEESAN DBE NAIM MJPERAK PINEPAC S...
107 2020-02-29 HARBOUR OWG ILB DPS KUB KHEESAN BSTEAD PARAGON...
108 2020-03-31 HARBOUR OWG ILB DPS KUB BSTEAD PARAGON TIGER G...
109 2020-04-30 HARBOUR OWG ILB DPS KUB PENSONI BSTEAD PARAGON...
110 2020-05-31 HARBOUR OWG ILB CNASIA DPS MAYBULK KHIND PENSO...
111 2020-06-30 SMISCOR CNASIA MAYBULK KHIND PENSONI COMPLET A...
112 2020-07-31 SMISCOR CNASIA TAS MAYBULK NYLEX KHIND KPPROP ...
113 2020-08-31 IMPIANA ALLIANZ JERASIA STAR TAS BJASSET TCHON...
114 2020-09-30 SEACERA ALLIANZ JERASIA STAR TAS BJASSET TCHON...
115 2020-10-31 SEACERA ALLIANZ JERASIA STAR BJASSET TCHONG MS...
116 2020-11-30 SWSCAP DBE JERASIA MSC CNI HARNLEN DPS TSRCAP ...
117 2020-12-31 SWSCAP DBE MBSB MSC CNI HARNLEN DPS TRC SIMEPR...
118 2021-01-31 SWSCAP DBE MBSB EUPE MSC CNI HARNLEN DPS PENER...
119 2021-02-28 DBE SWSCAP GMUTUAL AYS LATITUD PPG MRCB ITRONI...
120 2021-03-31 DBE PRKCORP AYS LATITUD MJPERAK PPG MRCB ITRON...
121 2021-04-30 DBE PRKCORP AYS LATITUD MJPERAK PPG MRCB SASBA...
122 2021-05-31 PRKCORP AYS PPG MJPERAK MRCB SASBADI IJMPLNT I...
123 2021-06-30 AYS PPG SASBADI BREM COASTAL MHB YTL ICON TITI...
124 2021-07-31 AYS BREM COASTAL YTL ICON TITIJYA AMEDIA CYMAO...
125 2021-08-31 GLBHD COASTAL BHIC SCABLE SHCHAN YTL EMETALL A...
126 2021-09-30 PRTASCO GLBHD AAX MPCORP MTRONIC BHIC SCABLE S...
127 2021-10-31 PRTASCO GLBHD AAX MPCORP MTRONIC BHIC SCABLE S...
黄金法则的资金增长
有了选好的公司,现在就来计算资金的增长
maxreturn = output['Average gains'].max()
if math.isnan(maxreturn):
maxreturn = 0
win = output[output['Average gains']>=100].shape[0]
total = output.shape[0]
print('Maximum return : ' + str(maxreturn))
print('Maximum retreat : ' + str(output['Average gains'].min()))
print('Average return : ' + str(output['Average gains'].median()))
print('CAGR : ' + str(CompoundedGrowth))
print('Winrate : ' + str(float(win)/float(total)))
fig, axs = plt.subplots(2,1)
output.plot.line(y='Capital', subplots=True, ax = axs[0], color = 'Green', label = 'KYY')
output.plot.line(y='Benchmark', subplots=True, ax = axs[0], color = 'Blue', label = 'KLCI')
# axs[0].set_yscale('log')
output.plot.line(y='Average gains', subplots=True, ax = axs[1])
plt.legend(loc='upper right')
plt.show()
Maximum return : 145.3678663108833
Maximum retreat : 78.13646022905483
Average return : 101.15013712494064
CAGR : 21.124786717204923
Winrate : 0.5859375
结论
- 官老前辈的黄金法则有不错的成绩,21% 复合年增长 和 58% 的赢率
- 回测结构显示资金在10年内从RM1000增长到RM8000
- 文章内的选股方法是非常机械性的,官老前辈可能会灵活地使用他的黄金法则,不过这篇文章可以大概研究黄金法则的绩效
纯属分享,无买卖建议