In my last post, Creating a Crypto Stats Page: Part 2: Asset Pair Dropdown, I showed you how to add an asset pair selection dropdown to your stats page. In this post, we will add the SMA and EMA to our stats page.
Step 1: What is the SMA?
The SMA is a metric used in trading. It stands for ‘Simple Moving Average’. If you take a set of prices over a number of time periods, the SMA is the sum of those prices divided by the number of time periods. So it is simply the mean price over a certain period of time, and in our current stats page we already calculate it. Therefore, all we need to do to add the SMA is label the mean value as such.
Step 2: What is the EMA?
The EMA is another metric used in trading. It stands for ‘Exponential Moving Average’. It is similar to the SMA, except when it is used to calculate a mean price, it gives a stronger weighting to the more recent prices. This means the EMA is more sensitive to recent price changes, and not so biased by more historic prices.
The formula for calculating the EMA is:
EMA = last_closing_price x weighting_factor + ((EMA_previous) * (1 - weighting_factor))
The last_closing_price
is the price at the end of the last period (if the period is a day, it would be the last day’s closing price). The EMA_previous
is the EMA calculated for the last period. If we are calculating the EMA for the first time for a set of periods, the previous SMA is used instead of the EMA for the first calculation. The weighting_factor
is the factor that gives the greater weighting to the more recent prices. It is calculated with this formula:
weighting_factor = smoothing_factor/(num_periods + 1)
Here, the smoothing_factor
can vary, but the most commonly used value is 2. The num_periods
value is how many periods of prices we are using to calculate the EMA. So if we were calculating the 7-Day EMA, the num_periods
value would be 7 (the period being one day). If we use the most common value for smoothing_factor
, the formula for the weighting_factor
becomes:
weighting_factor = 2/(num_periods + 1)
Step 3: Update the Code
Most of the changes we need are going to be to the get_rolling_mean_data
function. First, we’ll need to add some extra parameters to it, and set some defaults:
function get_rolling_mean_data($pair, $interval = null, $num_periods = null) { if ($interval == null) { $interval = 1440; // daily (num minutes in day) } if ($num_periods == null) { $num_periods = 7; // 7 days of data }
This will give us more flexibility later to generate stats for different intervals and numbers of periods. Next, we need to double the amount of historic data we retrieve. This is because, in order to calculate the EMA, we need to have an n-period SMA to have been calculated before we can start calculating EMAs. So we will calculate the n-period SMA with the first n periods of data, then start calculating the EMA at the n+1th period (using the n-period SMA as our starting previous EMA), which gives us n-1 EMA calculations.
$now = time(); // 14 days ago timestamp by default (get double for the ema) $since = $now - ($interval * 60 * $num_periods * 2);
Next we get the historic data as before, but instead of calculating the mean prices straight away, we store the historic data to an array first, as we will need to be able to loop through it in different ranges for the n-period SMA and n-period EMA calculations:
$url = 'https://api.kraken.com/0/public/OHLC?pair=' . $pair . '&interval=' . $interval . '&since=' . $since; $data = file_get_contents($url); // Example data (headings not present in real data): // '{"error": [], "result": {"ADAGPB": // [ // timestamp open high low close vwap volume count // [1624147200, "1.00520", "1.05875", "0.95188", "1.03663", "0.99311", "865404.81347312", 1285], // [1624233600, "1.04041", "1.04212", "0.84270", "0.84904", "0.90723", "1829249.14853250", 1702], // ], "last": 1624579200}}'; $json_array = json_decode($data, true); $historic_data = $json_array['result'][$pair]; $count = 0; $results = array(); $historic_prices = array(); foreach($historic_data as $x => $daily_data) { $count++; $closing_price = $daily_data[4]; $high = $daily_data[2]; $low = $daily_data[3]; $historic_prices[$count]['closing_price'] = $closing_price; $historic_prices[$count]['high'] = $high; $historic_prices[$count]['low'] = $low; }
Now we calculate the n-period SMAs, and save them to an array. We are calculating a rolling SMA over an n-period timeframe, so we don’t use all the data at once, only the last n entries from the current price in the loop.
$smas = array(); $num = count($historic_prices); for ($i=$num_periods; $i<=$num; $i++) { // Start at nth period, to get n-period smas $total = 0; $total_high = 0; $total_low = 0; for ($j=$i; $j>$i-$num_periods; $j--) { // Average n-periods of data, working backwards from starting point $closing_price = $historic_prices[$j]['closing_price']; $high = $historic_prices[$j]['high']; $low = $historic_prices[$j]['low']; $total += $closing_price; $total_high += $high; $total_low += $low; } $rolling_mean = round($total / $num_periods, 6); $rolling_mean_high = round($total_high / $num_periods, 6); $rolling_mean_low = round($total_low / $num_periods, 6); $smas[$i]['mean'] = $rolling_mean; $smas[$i]['mean_high'] = $rolling_mean_high; $smas[$i]['mean_low'] = $rolling_mean_low; }
Next we calculate the n-period EMAs, and save them to another array. Note that we start calculating the EMAs at the n+1th period. This is to ensure we have an n-period SMA to use as the first previous EMA value. For the subsequent calculations, we use the previously calculated EMA as the previous EMA value. The function get_exponential_moving_average
is a new function that we’ll add shortly.
$emas = array(); for ($i=$num_periods+1; $i<=$num; $i++) { // Start at n+1th period, to get n-period emas $closing_price = $historic_prices[$i]['closing_price']; if ($i==$num_periods+1) { $last_ema = $smas[$num_periods]['mean']; } else { $last_ema = $emas[$i-1]; } $ema = get_exponential_moving_average($pair, $last_ema, $interval, $num_periods, $closing_price); $emas[$i] = $ema; }
The final part of get_rolling_mean_data
collates the results and returns them:
$results['mean'] = $smas[$num]['mean']; $results['mean_high'] = $smas[$num]['mean_high']; $results['mean_low'] = $smas[$num]['mean_low']; $results['ema'] = $emas[$num]; return $results; }
Here is the get_exponential_moving_average
function, which uses the formula described in ‘Step 2: What is the EMA?’:
function get_exponential_moving_average($pair, $last_ema, $interval, $num_periods, $last_closing_price) { $smoothing_factor = 2; $weighting_factor = ($smoothing_factor / (1 + $num_periods)); $ema = ($last_closing_price * $weighting_factor) + ($last_ema * (1 - $weighting_factor)); return round($ema, 6); }
Step 4: Update the Stats Page Display
Now we need to update the stats page html/php to display the EMA and label the mean as the SMA. We also need to change the call to get_rolling_mean_data
to include the interval
and num_periods
arguments.
$num_periods = 7; $interval = 1440; $mean_array = get_rolling_mean_data($pair, $interval, $num_periods); $mean = $mean_array['mean']; $mean_high = $mean_array['mean_high']; $mean_low = $mean_array['mean_low']; $ema = $mean_array['ema']; $current_price = get_current_price($pair); ?> <p>7-Day <?=$pairs[$pair];?> Stats:</p> <p>Current Price: <?=$current_price;?> GBP</p> <p>Mean Price (SMA): <?=$mean;?> GBP</p> <p>EMA: <?=$ema;?> GBP</p> <p>Mean High Price: <?=$mean_high;?> GBP</p> <p>Mean Low Price: <?=$mean_low;?> GBP</p>
Step 5: Upload and Test
We should now be ready to upload and test the code. The complete script should now look like this:
<?php function get_current_price($pair) { $url = 'https://api.kraken.com/0/public/Ticker?pair=' . $pair; $data = file_get_contents($url); $json_array = json_decode($data, true); $current_price = $json_array['result'][$pair]['c'][0]; return $current_price; } function get_rolling_mean_data($pair, $interval = null, $num_periods = null) { if ($interval == null) { $interval = 1440; // daily (num minutes in day) } if ($num_periods == null) { $num_periods = 7; // 7 days of data } $now = time(); // 14 days ago timestamp by default (get double for the ema) $since = $now - ($interval * 60 * $num_periods * 2); $url = 'https://api.kraken.com/0/public/OHLC?pair=' . $pair . '&interval=' . $interval . '&since=' . $since; $data = file_get_contents($url); // Example data (headings not present in real data): // '{"error": [], "result": {"ADAGPB": // [ // timestamp open high low close vwap volume count // [1624147200, "1.00520", "1.05875", "0.95188", "1.03663", "0.99311", "865404.81347312", 1285], // [1624233600, "1.04041", "1.04212", "0.84270", "0.84904", "0.90723", "1829249.14853250", 1702], // ], "last": 1624579200}}'; $json_array = json_decode($data, true); $historic_data = $json_array['result'][$pair]; $count = 0; $results = array(); $historic_prices = array(); foreach($historic_data as $x => $daily_data) { $count++; $closing_price = $daily_data[4]; $high = $daily_data[2]; $low = $daily_data[3]; $historic_prices[$count]['closing_price'] = $closing_price; $historic_prices[$count]['high'] = $high; $historic_prices[$count]['low'] = $low; } $smas = array(); $num = count($historic_prices); for ($i=$num_periods;$i<=$num;$i++) { // Start at nth period, to get n-period smas $total = 0; $total_high = 0; $total_low = 0; for ($j=$i; $j>$i-$num_periods; $j--) { // Average n-periods of data, working backwards from starting point $closing_price = $historic_prices[$j]['closing_price']; $high = $historic_prices[$j]['high']; $low = $historic_prices[$j]['low']; $total += $closing_price; $total_high += $high; $total_low += $low; } $rolling_mean = round($total / $num_periods, 6); $rolling_mean_high = round($total_high / $num_periods, 6); $rolling_mean_low = round($total_low / $num_periods, 6); $smas[$i]['mean'] = $rolling_mean; $smas[$i]['mean_high'] = $rolling_mean_high; $smas[$i]['mean_low'] = $rolling_mean_low; } $emas = array(); for ($i=$num_periods+1; $i<=$num; $i++) { // Start at n+1th period, to get n-period emas $closing_price = $historic_prices[$i]['closing_price']; if ($i==$num_periods+1) { $last_ema = $smas[$num_periods]['mean']; } else { $last_ema = $emas[$i-1]; } $ema = get_exponential_moving_average($pair, $last_ema, $interval, $num_periods, $closing_price); $emas[$i] = $ema; } $results['mean'] = $smas[$num]['mean']; $results['mean_high'] = $smas[$num]['mean_high']; $results['mean_low'] = $smas[$num]['mean_low']; $results['ema'] = $emas[$num]; return $results; } function get_exponential_moving_average($pair, $last_ema, $interval, $num_periods, $last_closing_price) { $smoothing_factor = 2; $weighting_factor = ($smoothing_factor / (1 + $num_periods)); $ema = ($last_closing_price * $weighting_factor) + ($last_ema * (1 - $weighting_factor)); return round($ema, 6); } function sanitise($data) { $data = trim($data); $data = stripslashes($data); $data = htmlspecialchars($data); return $data; } $pairs = array( "ADAGBP" => "ADAGBP", "DOTGBP" => "DOTGBP", "XETHZGBP" => "ETHGBP", "XXBTZGBP" => "BTCGBP" ); ?> <html> <head> <title>Crypto Stats</title> <meta name="robots" content="noindex"> <style type="text/css"> p,label,select,option,input { font-size: 1.25em; font-family: Verdana, sans-serif; } </style> </head> <body> <form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="post" id="pairform"> <label for="pair">Asset pair:</label> <select name="pair" id="pair" form="pairform"> <option value="">--- Select an assert pair ---</option> <? foreach ($pairs as $pair_id => $pair_name) { ?> <option value="<?=$pair_id;?>"$gt;<?=$pair_name;?></option> <? } ?> </select> <input type="submit"> </form> <? if ($_SERVER["REQUEST_METHOD"] == "POST") { $pair = sanitise($_POST["pair"]); if ($pair == "") { ?> <p>Please select an asset pair from the list</p> <? } else { $num_periods = 7; $interval = 1440; $mean_array = get_rolling_mean_data($pair, $interval, $num_periods); $mean = $mean_array['mean']; $mean_high = $mean_array['mean_high']; $mean_low = $mean_array['mean_low']; $ema = $mean_array['ema']; $current_price = get_current_price($pair); ?> <p>7-Day <?=$pairs[$pair];?> Stats:</p> <p>Current Price: <?=$current_price;?> GBP</p> <p>Mean Price (SMA): <?=$mean;?> GBP</p> <p>EMA: <?=$ema;?> GBP</p> <p>Mean High Price: <?=$mean_high;?> GBP</p> <p>Mean Low Price: <?=$mean_low;?> GBP</p> <?} }?> </body> </html>
If you select an asset pair and submit the form, you should see the EMA value added to the stats page.
Conclusion
You should now have an updated crypto stats page that shows you the SMA and EMA for a 7-day period. If you want to learn about and add the MACD to your page, see my follow-on post Creating a Crypto Stats Page: Part 4: MACD. Thanks for reading, see you next time!
2 Responses
[…] Creating a Crypto Stats page: Part 3: SMA and EMA October 10, 2021 […]
[…] Creating a Crypto Stats Page: Part 3: SMA and EMA October 10, 2021 […]