Testing KYY’s Golden Rule using data from year 2011-2021

If you are in the Malaysian investing community,
you sure will read a lot about Mr Koon Yew Yin,
he is a very respected figure in business and investing world.

He is also kind enough to share his insights on investing.
He publicly shared his golden rule to investing
which is

My golden rule is the company must report increasing profit for 2 consecutive quarters and the price is selling below P/E 10.

source : https://koonyewyin.com/2019/03/16/investment-talk-on-17-march-2019-my-golden-rule-for-share-selection/

Today we are going to bring this golden rule to a test.

Stock Picking Methodology

  • Filter all stocks with PE > 10
  • Pick top 10 stocks with highest 2 consecutive eps growth
  • Trading Costs are ignored
  • Repeat the steps every month
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

What Stocks did We Bought?

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...

How Does It Look in Real Life?

With the stocks that we picked, let’s run the numbers to see how much RM1000 capital invested will grow.

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
RM1000 invested

Conclusion

  • KYY’s golden rule offers superb performance with 21% cagr, with 58% win rate
  • Backtesting result shows capital grew from RM1k to RM8k in 10 years
  • The stock pick rules used are very mechanistic, Mr Koon Yew Yin might not follow this rule blindly, as investing is more to art than science, but this serves as a reference on how this strategy will do

This is not a trading advice and the studies and analysis done were for educational and sharing purposes only

Leave a Reply

Your email address will not be published.