Technical Analysis Using R

20/10/2024 Yufy Firdiansyah, MM, S.Sos

Short Introduction

Technical analysis is a vital practice for traders and investors, providing insights into market behavior and potential price movements. In this article, we will explore how to conduct a comprehensive technical analysis using R, focusing on data obtained from Investing.com. Although this source offers a wealth of information about various financial instruments, it lacks a crucial element: the closing price. Therefore, we will begin by cleaning and preparing our dataset to ensure it is suitable for analysis. Once we have structured our data, we’ll visualize it using candlestick charts, a popular method for representing price action in financial markets. By the end of this tutorial, you will have a clear understanding of data preparation techniques and visualization strategies in R, empowering you to make data-driven trading decisions.

Case Studi PT. Lippo Karawaci TBK

Lippo Karawaci Tbk is one of Indonesia’s largest and most prominent property developers, renowned for its integrated urban developments and high-quality residential, commercial, and retail properties. Established in 1990, the company has built a diverse portfolio that includes townships, hospitals, shopping malls, and hotels, contributing significantly to Indonesia’s urban landscape. Lippo Karawaci is committed to enhancing the quality of life through sustainable and innovative real estate solutions, making it a key player in the nation’s property market. For investors looking to explore opportunities in Indonesia’s growing real estate sector, Lippo Karawaci’s stock is publicly traded on the Indonesia Stock Exchange (IDX) under the ticker symbol LPKR. Discover the potential of investing in a company that is at the forefront of Indonesia’s urban development.

Install Package & Loading Libraries

#Loading Libraries - Please install if necessary

library(readr)  # For read_csv
library(dplyr)  # For data manipulation
library(stringr)  # For string manipulation
library(quantmod) # For quant trading 
library(TTR) # For technical analysis
library(lubridate) # For manipulating date

# Clear the environment to avoid naming conflicts
rm(list = ls())

Data Cleaning

Data cleaning is essential because downloads from Investing.com typically lack a Close Price column for candlestick charts, require renaming the ‘Vol.’ column to ‘Volume’, and have Date, Vol., and Change columns in character format.

First we set up the directory and upload the file to be analyzed

#Set Directory and Upload The File
setwd("~/RProject/Stocks_2")
stocs <-read.csv("LPKR Historical Data.csv")
head(stocs)
##         Date Price Open High Low    Vol. Change..
## 1 10/01/2024   105  100  106 100 244.35M    3.96%
## 2 09/30/2024   101  100  101  98 179.18M    2.02%
## 3 09/27/2024    99  100  101  96 170.50M    0.00%
## 4 09/26/2024    99  100  101  98 150.01M   -1.00%
## 5 09/25/2024   100  103  104  98 237.06M   -1.96%
## 6 09/24/2024   102   99  105  97 417.81M    4.08%

Then we trim column names to tidy up the data

column_names <- colnames(stocs)
print(column_names)
## [1] "Date"     "Price"    "Open"     "High"     "Low"      "Vol."     "Change.."
str(stocs)
## 'data.frame':    176 obs. of  7 variables:
##  $ Date    : chr  "10/01/2024" "09/30/2024" "09/27/2024" "09/26/2024" ...
##  $ Price   : int  105 101 99 99 100 102 98 101 107 95 ...
##  $ Open    : int  100 100 100 100 103 99 101 107 114 88 ...
##  $ High    : int  106 101 101 101 104 105 103 108 116 98 ...
##  $ Low     : int  100 98 96 98 98 97 95 101 98 88 ...
##  $ Vol.    : chr  "244.35M" "179.18M" "170.50M" "150.01M" ...
##  $ Change..: chr  "3.96%" "2.02%" "0.00%" "-1.00%" ...
colnames(stocs) <- trimws(colnames(stocs))
head(stocs)
##         Date Price Open High Low    Vol. Change..
## 1 10/01/2024   105  100  106 100 244.35M    3.96%
## 2 09/30/2024   101  100  101  98 179.18M    2.02%
## 3 09/27/2024    99  100  101  96 170.50M    0.00%
## 4 09/26/2024    99  100  101  98 150.01M   -1.00%
## 5 09/25/2024   100  103  104  98 237.06M   -1.96%
## 6 09/24/2024   102   99  105  97 417.81M    4.08%

Aftwr that we rename the Vol. column name to Volume column

stocs <-stocs %>%
rename(Volume = 'Vol.')
head(stocs)
##         Date Price Open High Low  Volume Change..
## 1 10/01/2024   105  100  106 100 244.35M    3.96%
## 2 09/30/2024   101  100  101  98 179.18M    2.02%
## 3 09/27/2024    99  100  101  96 170.50M    0.00%
## 4 09/26/2024    99  100  101  98 150.01M   -1.00%
## 5 09/25/2024   100  103  104  98 237.06M   -1.96%
## 6 09/24/2024   102   99  105  97 417.81M    4.08%

Next, we create close column from open column next row

# Create the 'Close' column with the 'Open' value from the next row
socs <- stocs %>%
  mutate(Close = lead(Open))

# Print the updated dataframe
head(socs)
##         Date Price Open High Low  Volume Change.. Close
## 1 10/01/2024   105  100  106 100 244.35M    3.96%   100
## 2 09/30/2024   101  100  101  98 179.18M    2.02%   100
## 3 09/27/2024    99  100  101  96 170.50M    0.00%   100
## 4 09/26/2024    99  100  101  98 150.01M   -1.00%   103
## 5 09/25/2024   100  103  104  98 237.06M   -1.96%    99
## 6 09/24/2024   102   99  105  97 417.81M    4.08%   101

So, we have to delete the latest row because it does not have value inside close cell

#Delete latest row

socs_data_cleaned <- na.omit(socs)
head(socs_data_cleaned)
##         Date Price Open High Low  Volume Change.. Close
## 1 10/01/2024   105  100  106 100 244.35M    3.96%   100
## 2 09/30/2024   101  100  101  98 179.18M    2.02%   100
## 3 09/27/2024    99  100  101  96 170.50M    0.00%   100
## 4 09/26/2024    99  100  101  98 150.01M   -1.00%   103
## 5 09/25/2024   100  103  104  98 237.06M   -1.96%    99
## 6 09/24/2024   102   99  105  97 417.81M    4.08%   101

Do not miss the Date Column because it is still in character/string format

#Convert Date to Date Format

socs_data_cleaned$Date <- parse_date_time(socs_data_cleaned$Date, orders = c("ymd", "dmy", "mdy"), quiet = TRUE)
head(socs_data_cleaned)
##         Date Price Open High Low  Volume Change.. Close
## 1 2024-10-01   105  100  106 100 244.35M    3.96%   100
## 2 2024-09-30   101  100  101  98 179.18M    2.02%   100
## 3 2024-09-27    99  100  101  96 170.50M    0.00%   100
## 4 2024-09-26    99  100  101  98 150.01M   -1.00%   103
## 5 2024-09-25   100  103  104  98 237.06M   -1.96%    99
## 6 2024-09-24   102   99  105  97 417.81M    4.08%   101

Candlestick Chart Without Volume

This is the interesting part, visualization! First Just Candlestick Chart, plain and simple

# Prepare data for chartSeries
socs_xts <- xts(socs_data_cleaned[, c("Open", "High", "Low", "Close")], order.by = socs_data_cleaned$Date)

# Plot candlestick chart
chartSeries(socs_xts, type = "candlesticks", name = "LPKR Candlestick Chart",
            theme = chartTheme("white"),
            TA = NULL)  # TA = NULL to avoid additional technical analysis overlays

Candlestick Chart with Volume,EMA & Macd

Now prepare the Volume column before visualize it with EMA 34 and MAcD. We convert Volume column to numeric.

# Function to clean and convert Volume column
clean_volume <- function(volume) {
  if (grepl('M', volume)) {
    return(as.numeric(gsub('M', '', volume)) * 1000000)
  } else if (grepl('B', volume)) {
    return(as.numeric(gsub('B', '', volume)) * 1000000000)
  } else if (grepl('K', volume)) {
    return(as.numeric(gsub('K', '', volume)) * 1000)
  } else {
    return(as.numeric(volume))
  }
}


# Apply the cleaning function to the Volume column
socs_data_cleaned$Volume <- sapply(socs_data_cleaned$Volume, clean_volume)

head(socs_data_cleaned)
##         Date Price Open High Low    Volume Change.. Close
## 1 2024-10-01   105  100  106 100 244350000    3.96%   100
## 2 2024-09-30   101  100  101  98 179180000    2.02%   100
## 3 2024-09-27    99  100  101  96 170500000    0.00%   100
## 4 2024-09-26    99  100  101  98 150010000   -1.00%   103
## 5 2024-09-25   100  103  104  98 237060000   -1.96%    99
## 6 2024-09-24   102   99  105  97 417810000    4.08%   101
str(socs_data_cleaned)
## 'data.frame':    175 obs. of  8 variables:
##  $ Date    : POSIXct, format: "2024-10-01" "2024-09-30" ...
##  $ Price   : int  105 101 99 99 100 102 98 101 107 95 ...
##  $ Open    : int  100 100 100 100 103 99 101 107 114 88 ...
##  $ High    : int  106 101 101 101 104 105 103 108 116 98 ...
##  $ Low     : int  100 98 96 98 98 97 95 101 98 88 ...
##  $ Volume  : num  2.44e+08 1.79e+08 1.70e+08 1.50e+08 2.37e+08 ...
##  $ Change..: chr  "3.96%" "2.02%" "0.00%" "-1.00%" ...
##  $ Close   : int  100 100 100 103 99 101 107 114 88 87 ...
##  - attr(*, "na.action")= 'omit' Named int 176
##   ..- attr(*, "names")= chr "176"

Now prepare We also want to convert Change.. column from character

socs_data_cleaned <- socs_data_cleaned %>%
  mutate(Change.. = as.numeric(gsub("%", "", Change..)) / 100)
str(socs_data_cleaned)
## 'data.frame':    175 obs. of  8 variables:
##  $ Date    : POSIXct, format: "2024-10-01" "2024-09-30" ...
##  $ Price   : int  105 101 99 99 100 102 98 101 107 95 ...
##  $ Open    : int  100 100 100 100 103 99 101 107 114 88 ...
##  $ High    : int  106 101 101 101 104 105 103 108 116 98 ...
##  $ Low     : int  100 98 96 98 98 97 95 101 98 88 ...
##  $ Volume  : num  2.44e+08 1.79e+08 1.70e+08 1.50e+08 2.37e+08 ...
##  $ Change..: num  0.0396 0.0202 0 -0.01 -0.0196 ...
##  $ Close   : int  100 100 100 103 99 101 107 114 88 87 ...
##  - attr(*, "na.action")= 'omit' Named int 176
##   ..- attr(*, "names")= chr "176"

Now, check if it is ready to be visualized

# Prepare data for chartSeries

socs_xts <- xts(socs_data_cleaned[, c("Open", "High", "Low", "Close","Volume")], order.by = socs_data_cleaned$Date)
str(socs_xts)
## An xts object on 2024-01-03 / 2024-10-01 containing: 
##   Data:    double [175, 5]
##   Columns: Open, High, Low, Close, Volume
##   Index:   POSIXct,POSIXt [175] (TZ: "UTC")

This is the part of visualization The Candlestick chart with Volume and EMA 34

# Plot candlestick chart
chartSeries(socs_xts, type = "candlesticks", name = "LPKR Candlestick Chart",
            theme = chartTheme("white"),
            TA = NULL)  # TA = NULL to avoid additional technical analysis overlays

# Add volume to the bottom of the chart
#addTA(socs_xts$Volume, on = 1, col = "blue")  # This adds the volume as a blue bar chart

# Optionally, you can use these lines to customize your volume chart
addTA(socs_xts$Volume, legend = "Volume",on = NA, col = "blue", type = "h")  # Vertical histogram for volume

# Calculate EMA 34
socs_xts$EMA_34 <- EMA(Cl(socs_xts), n = 34)  # Calculate EMA for closing prices

# Add EMA to the chart
addTA(socs_xts$EMA_34, on = 1,  col = "red", lwd = 2)  # Add EMA line

We could add other indicator such as MacD to enhance the analysis.

# Plot candlestick chart
chartSeries(socs_xts, type = "candlesticks", name = "LPKR Candlestick Chart",
            theme = chartTheme("white"),
            TA = NULL)  # TA = NULL to avoid additional technical analysis overlays

# Add volume to the bottom of the chart
#addTA(socs_xts$Volume, on = 1, col = "blue")  # This adds the volume as a blue bar chart

# Optionally, you can use these lines to customize your volume chart
addTA(socs_xts$Volume, legend = "Volume",on = NA, col = "blue", type = "h")  # Vertical histogram for volume

# Calculate EMA 34
socs_xts$EMA_34 <- EMA(Cl(socs_xts), n = 34)  # Calculate EMA for closing prices

# Add EMA to the chart
addTA(socs_xts$EMA_34,on= 1,col = "red", lwd = 0 )  # Add EMA line

addMACD(fast=5,slow=34,signal=1,type="EMA")

Conclusion

When obtaining data from Investing.com, the initial process of downloading and preparing the dataset for analysis can be quite time-consuming and meticulous. The data often requires extensive cleaning to address inconsistencies, such as missing values, incorrect formats, or extraneous characters. Specifically, formatting columns such as dates and prices, and ensuring that volume figures are correctly interpreted, is essential for accurate visualizations. This thorough preparation is critical as it lays the foundation for meaningful analysis.

Once the data is properly sanitized and formatted, creating visualizations allows for clear and insightful analysis. For instance, when the price breaks above the 34-period Exponential Moving Average (EMA), it typically indicates a bullish trend for the stock. Traders often consider this level as a dynamic support line, suggesting that prices might continue to rise. To add further confidence to this bullish signal, a surge in trading volume accompanying the breakout can indicate strong market interest and support for the price movement, reducing the likelihood of a false breakout.

Moreover, to enhance decision-making, traders often look for confirmation of a potential trend reversal through MACD (Moving Average Convergence Divergence) divergence. If the MACD line crosses above its signal line after a price breakout above the EMA 34, especially combined with increased volume, this reinforces the bullish sentiment. It indicates that the upward momentum is not only supported by price movement but also corroborated by strong trader interest, leading to more confident trading decisions. By blending these signals, traders can optimize their entry points, effectively managing risk while capitalizing on potential market opportunities.