# How Effective is Moving Average Strategy in KLSE?

## Moving Average Strategy

Moving average strategy is a common strategy used by traders.

Unlike some of the more exotic indicators,
moving average readily available in many charting software or website such as klsescreener or tradingview

One common usage of moving average is that,
when the “fast” moving average goes above the “slow” moving average,
when the “fast” moving average goes below the “fast” moving average,
we sell.

So in this article I will test the performance of this strategy on KLSE index.

Though we can’t directly buy the KLSE index,
we can achieve similar by buying futures or warrants that tracks the KLSE index.

I downloaded historical data of KLSE from Yahoo Finance in csv form.

We need to read it into memory to perform backtesting.

``````import pandas as pd

KLSE = KLSE.replace(',','', regex=True)
KLSE['Open'] = KLSE['Open'].astype(float)
KLSE['Close'] = KLSE['Close'].astype(float)
KLSE['Change'] = (KLSE['Close'] - KLSE['Close'].shift(1))/KLSE['Open'] + 1

## Rules

We are using very simple moving average strategy,

the rules are:

1. when the “fast” moving average goes above the “slow” moving average, we buy in.
2. when the “fast” moving average goes below the “fast” moving average, we sell.
``````def simulate(df,fast,slow):

import talib

df['fast'] = talib.SMA(df['Close'],fast)
df['slow'] = talib.SMA(df['Close'],slow)

df.dropna(inplace = True)

gold_cross = df[df['fast'] > df['slow']].index
df.loc[gold_cross,'Cross'] = 1

gold_cross = df[df['fast'] < df['slow']].index
df.loc[gold_cross,'Cross'] = 0

df['Return'] = df['Cross']*df['Change']

def norm(x):
if x == 0:
return 1
else:
return x

df['Return'] = df['Return'].apply(lambda x: norm(x))
df['Nav'] = (df['Return']).cumprod()

# divide by 252 because generally a year has 252 trading days
num_periods = df.shape/252
rety = ((df['Nav'].iloc[-1] / df['Nav'].iloc) ** (1 / (num_periods - 1)) - 1)*100.0

if len(price_out) > len(price_in):
price_out = price_out[:len(price_in)]

if len(price_in) > len(price_out):
price_in = price_in[:len(price_out)]

VictoryRatio = ((price_out - price_in)>0).mean()*100.0
DD = 1 - df['Nav']/df['Nav'].cummax()
MDD = max(DD)*100.0

return df, round(rety, 2), round(VictoryRatio, 2), round(MDD,2)``````

## Strategy Performance

We are backtesting 3 pairs of moving average

1. 10 and 20 (Short term trade)
2. 20 and 50 (Medium term trade)
3. 50 and 200 (Long term trade)
``````MA1020,cagr1020,vr1020,mdd1020 = simulate(KLSE.copy(),10,20)
MA2050,cagr2050,vr2050,mdd2050 = simulate(KLSE.copy(),20,50)
MA50200,cagr50200,vr50200,mdd50200 = simulate(KLSE.copy(),50,200)

KLSE['KLSE'] = (KLSE['Change']).cumprod()
import matplotlib.pyplot as plt
plt.style.use('seaborn')

ax = MA1020['Nav'].plot(figsize=(10, 6))
MA2050['Nav'].plot(ax=ax)
MA50200['Nav'].plot(ax=ax)
KLSE['KLSE'].plot(ax=ax)
# plt.plot( 'Date','Nav', data = MA2050, marker='', color='olive', linewidth=2)
ax.legend(['MA1020','MA2050','MA50200','KLSE']);
plt.show()

from prettytable import PrettyTable

t = PrettyTable(['Strategy', 'CAGR', 'Win Rate', 'Max Drawdown'])

print(t)``````
``````+----------+-------+----------+--------------+
| Strategy |  CAGR | Win Rate | Max Drawdown |
+----------+-------+----------+--------------+
|  MA1020  |  6.44 |  50.77   |     8.52     |
|  MA2050  |  3.14 |  30.77   |    12.15     |
| MA50200  | -0.89 |  28.57   |    30.08     |
+----------+-------+----------+--------------+``````

## Thoughts

From the results, it seems like 10 and 20 moving average pair is the best
it consistently outperforms the benchmark.

The rest are disappointing,
longer term pairs are more likely to perform better in long bull runs index like SP500.

If you know coding,
you can try this out on other stocks that you like by getting csv data from Yahoo Finance.

This isn’t any trading advice and the studies and analysis done were for educational and sharing purposes only