Have you ever thought about creating an animated plot that evolves over time, perfect for sharing on Instagram Reels or other content platforms? Well, now you can achieve that using R and data from the Sectors. In this recipe, I’ll guide you to create the animated plot of the most traded stocks on Indonesia Stock Exchange in 2024.

Install Libraries

Before we dive into the project, let’s set up our environment by installing essential packages. We’ll use the tidyverse package for data manipulation in R, along with ggplot and gganimate libraries to craft our visualization and animated plot.

install.packages(c("tidyverse","gganimate"))

Load Library

library(tidyverse)
library(gganimate)

Data Source

After installing the necessary packages and loading the libraries, we can start fetching the data from the Sectors Financial API. To access the API you only need to go to the Sectors website, subscribe to the plan and call the API directly from your markdown. Here is how we fetch the most-traded stock data from R using the endpoint that has been created by Sectors team.

library(httr)

# Replace the URL with a URL from the Available Endpoints section
url <- "https://api.sectors.app/v1/most-traded/?start=2024-01-01&end=2024-03-24&n_stock=10" #Top 10 Daily most traded stocks from 1st January 2024 to 24th March 2024
api_key <- "YOUR API KEY"

headers <- c(Authorization = api_key)

response <- GET(url, add_headers(.headers = headers))

if (response$status_code == 200) {
  data <- content(response, "parsed")
} else {
  # Handle error
  print(response$status_code)
}

Using the most-traded API endpoints we could fetch the top-n stocks based on the trading volume each day, we could configure the start date, the end date, and the top-n most traded companies in each day. After that we only need to process the data to make it a proper data frame that can be used in our next step.

df <- do.call(rbind, lapply(names(data), function(date) {
  data.frame(
    date = rep(date, each = length(data[[date]])),
    symbol = unlist(lapply(data[[date]], function(x) x[[1]])),
    volume = unlist(lapply(data[[date]], function(x) x[[3]]))
  )
}))

Here is the glimpse result of the API endpoint that we have called and processed. We will use this data to create a animation bar chart in this recipe

datesymbolvolume
2024-02-20GOTO.JK1033123000
2024-02-20BUMI.JK998119800
2024-02-20CARE.JK673579200
2024-02-20DEWA.JK509467700
2024-02-20DOOH.JK489452400

You can also use this dataset for this task, it is the same with the data that we called from the API, and it’s accessible on Sectors.

Data Manipulation

We will start our data manipulation process by calculating the cumulative sum for each stock from the beginning of 2024 until now. Additionally, we will select only the top 10 stocks with the highest cumulative sum to include in the plot. This can be accomplished using the functions available in the tidyverse package in R.

df_filter <- df %>%
  group_by(symbol) %>%
  arrange(date) %>%
  mutate(accumulated_volume = cumsum(volume))

df_finished <- df_filter %>%
  group_by(date) %>%
  top_n(10,accumulated_volume) %>%
  arrange(date,accumulated_volume)

df_finished <- df_finished %>%
  group_by(date) %>%
  mutate(rank = rank(-accumulated_volume))

With the provided code, the data will be transformed as shown in the table below. This table will include the ranking and cumulative sum up until the most recent date, which is essential for creating the plot.

datesymbolvolumeaccumulated_volumerank
2024-01-02MPXL.JK21310910021310910010
2024-01-02BAPA.JK2828552002828552009
2024-01-02BIPI.JK3000425003000425008
2024-01-02STRK.JK3286911003286911007
2024-01-02NATO.JK4540953004540953006
2024-01-02DOOH.JK4894524004894524005
2024-01-02DEWA.JK5094677005094677004
2024-01-02CARE.JK6735792006735792003
2024-01-02BUMI.JK9981198009981198002
2024-01-02GOTO.JK103312300010331230001

To finalize the data preparation for the plot, we will format the accumulated volume to enhance readability. This formatting will make the numbers easier for people to interpret.

formatNumber <- function(number) {
  absNumber <- abs(number)

  if (absNumber > 1e12) {
    return(paste0(format(round(number / 1e12, 2), nsmall = 2), "T"))
  } else if (absNumber > 1e9) {
    return(paste0(format(round(number / 1e9, 2), nsmall = 2), "B"))
  } else if (absNumber > 1e6) {
    return(paste0(format(round(number / 1e6, 2), nsmall = 2), "M"))
  } else if (absNumber > 1e3) {
    return(paste0(format(round(number / 1e3, 2), nsmall = 2), "K"))
  } else {
    return(as.character(number))
  }
}

# Apply the formatNumber function using mutate
df_finished <- df_finished %>%
  mutate(accumulated_volume_text = formatNumber(accumulated_volume))
datesymbolvolumeaccumulated_volumerankaccumulated_volume_text
2024-01-02MPXL.JK21310910021310910010213.11M
2024-01-02BAPA.JK2828552002828552009282.86M
2024-01-02BIPI.JK3000425003000425008300.04M
2024-01-02STRK.JK3286911003286911007328.69M
2024-01-02NATO.JK4540953004540953006454.10M
2024-01-02DOOH.JK4894524004894524005489.45M
2024-01-02DEWA.JK5094677005094677004509.47M
2024-01-02CARE.JK6735792006735792003673.58M
2024-01-02BUMI.JK9981198009981198002998.12M
2024-01-02GOTO.JK1033123000103312300011033.12M

Data Visualization

Let’s begin by designing the theme for our plot! You don’t necessarily have to create a custom theme from scratch; there are pre-built themes available for use in ggplot that are free. However, in this instance, I’ve created a custom theme to ensure that the final content aligns with other Sectors content.

custom_theme <- theme(
  axis.line=element_blank(),
  axis.text.x=element_blank(),
  axis.text.y=element_blank(),
  axis.ticks=element_blank(),
  axis.title.x=element_blank(),
  axis.title.y=element_blank(),
  legend.position="none",
  panel.background=element_rect(fill="black"), # Black background
  panel.border=element_blank(),
  panel.grid.major=element_blank(),
  panel.grid.minor=element_blank(),
  panel.grid.major.x = element_line( size=.1, color="grey" ),
  panel.grid.minor.x = element_line( size=.1, color="grey" ),
  plot.title=element_text(size=25, hjust=0.5, face="bold", colour="grey", vjust=-1.5, margin=margin(t=2, unit="line")),
  plot.subtitle=element_text(size=18, hjust=0.5, vjust=0,face="italic", color="grey"),
  plot.caption =element_text(size=12, hjust=0.5, face="italic", color="grey"),
  plot.margin = margin(2,2, 2, 4, "cm"),
  plot.background=element_rect(fill="black"),
)

After creating the theme, we can directly plot the data into static plot first before we animate it into an animated plot.

# Specify color for each stocks (company)
colors <- c("#f87171", "#fb923c","#fbbf24", "#facc15",
            "#a3e635","#4ade80","#34d399","#2dd4bf",
            "#22d3ee","#38bdf8","#60a5fa","#818cf8",
            "#a78bfa","#c084fc","#e879f9","#f472b6",
            "#fb7185","#b91c1c","#b45309","#4d7c0f",
            "#15803d","#0f766e","#0e7490","#4338ca",
            "#fde68a","#bbf7d0","#bae6fd","#fbcfe8")


staticplot <- ggplot(df_finished, aes(rank,accumulated_volume/2, group = symbol)) +
  # Create the bar chart
  geom_tile(aes(y = accumulated_volume/2,
                height = accumulated_volume,fill = as.factor(symbol),
                width = 0.9), alpha = 0.8, color = NA) +
  #Set the color of each bar according to the 'color' variable
  scale_fill_manual(values = colors)+
  #Set the xticker label
  geom_text(aes(y = 0, label = paste(symbol, " "), color="white"), vjust = 0.2, hjust = 1,size=7) +
  #Set the bar chart (accumulated volume label)
  geom_text(aes(y=accumulated_volume,label = paste0(" ",accumulated_volume_text),color="white", hjust=0),size=4) +
  coord_flip(clip = "off", expand = FALSE) +
  #Add the logo of the company in each bar
  geom_image(aes(image=image),size=0.05, asp=1.5) +
  scale_y_continuous(labels = scales::comma) +
  scale_x_reverse() +
  guides(color = FALSE, fill = FALSE) +
  custom_theme

After finishing create the static plot, we can directly convert it into a gif by using this code:

anim <- staticplot + transition_states(date, transition_length = 4, state_length = 1) +
  view_follow(fixed_x = TRUE)  +
  labs(title = 'Accumulative Transaction Volume: {closest_state}',
       subtitle  =  "Most traded stocks on Indonesia Stock Exchange since 2024",
       caption  = "Source: https://sectors.app")

and here is the result of the gif:

In our latest experiment, we dove into the world of animated plots using data from Sectors with R. Stay tuned for more exciting recipes featuring Sectors data!