Data Overview

cat("**Date Range:**", format(START_DATE), "to", format(END_DATE), "\n\n")
## **Date Range:** 2025-09-15 to 2025-12-17
cat("**Total Days in Range:**", as.numeric(END_DATE - START_DATE) + 1, "\n\n")
## **Total Days in Range:** 94
cat("**Report Generated:**", format(Sys.Date()))
## **Report Generated:** 2025-12-18

Load Data

# Load dimension table
etf_master <- readRDS(file.path(STAR_SCHEMA_DIR, "etf_master.rds"))

# Open fact table (Parquet dataset)
etf_daily <- open_dataset(file.path(STAR_SCHEMA_DIR, "etf_daily"))

cat("Loaded etf_master:", nrow(etf_master), "ETFs\n")
## Loaded etf_master: 375 ETFs
cat("Opened etf_daily dataset\n")
## Opened etf_daily dataset

Calculate Volume Statistics

# Calculate comprehensive volume statistics for each ETF
volume_stats <- etf_daily %>%
  filter(trade_date >= START_DATE & trade_date <= END_DATE) %>%
  filter(!is.na(volume_cleaned)) %>%
  collect() %>%
  group_by(stock_code) %>%
  summarise(
    # Basic counts
    total_days = n(),
    days_with_volume = sum(volume_cleaned > 0, na.rm = TRUE),
    days_with_zero_volume = sum(volume_cleaned == 0, na.rm = TRUE),
    days_with_na = sum(is.na(volume_cleaned)),
    
    # Volume aggregates
    total_volume = sum(volume_cleaned, na.rm = TRUE),
    avg_volume = mean(volume_cleaned, na.rm = TRUE),
    median_volume = median(volume_cleaned, na.rm = TRUE),
    max_volume = max(volume_cleaned, na.rm = TRUE),
    min_volume = min(volume_cleaned, na.rm = TRUE),
    
    # Trading frequency metrics
    trading_frequency = days_with_volume / total_days,  # Proportion of days with trading
    avg_volume_when_traded = mean(volume_cleaned[volume_cleaned > 0], na.rm = TRUE),
    
    # Volume consistency (coefficient of variation)
    volume_cv = ifelse(avg_volume > 0, 
                       sd(volume_cleaned, na.rm = TRUE) / avg_volume, 
                       NA),
    
    # Percentiles for distribution understanding
    p25_volume = quantile(volume_cleaned, 0.25, na.rm = TRUE),
    p75_volume = quantile(volume_cleaned, 0.75, na.rm = TRUE),
    p90_volume = quantile(volume_cleaned, 0.90, na.rm = TRUE),
    p95_volume = quantile(volume_cleaned, 0.95, na.rm = TRUE),
    
    .groups = "drop"
  ) %>%
  # Add date range info
  mutate(
    date_range_start = START_DATE,
    date_range_end = END_DATE,
    date_range_days = as.numeric(END_DATE - START_DATE) + 1
  )

cat("Calculated statistics for", nrow(volume_stats), "ETFs\n")
## Calculated statistics for 354 ETFs

Join with Master Data

volume_categorized <- volume_stats %>%
  left_join(etf_master, by = "stock_code") %>%
  select(
    stock_code,
    stock_short_name,
    issuer,
    asset_class,
    geographic_focus,
    investment_focus,
    listing_date,
    # Volume statistics
    total_volume,
    avg_volume,
    median_volume,
    max_volume,
    trading_frequency,
    days_with_volume,
    total_days,
    # Categories
    volume_category_adv,
    volume_category_frequency,
    volume_category_combined,
    volume_category_total,
    # Additional metrics
    volume_cv,
    avg_volume_when_traded
  ) %>%
  arrange(desc(avg_volume))

cat("Joined data for", nrow(volume_categorized), "ETFs\n")
## Joined data for 354 ETFs

Overall Statistics

overall_stats <- volume_categorized %>%
  summarise(
    total_etfs = n(),
    etfs_with_zero_volume = sum(avg_volume == 0 | is.na(avg_volume)),
    etfs_with_some_volume = sum(avg_volume > 0, na.rm = TRUE),
    median_avg_volume = median(avg_volume, na.rm = TRUE),
    mean_avg_volume = mean(avg_volume, na.rm = TRUE),
    median_trading_frequency = median(trading_frequency, na.rm = TRUE),
    mean_trading_frequency = mean(trading_frequency, na.rm = TRUE),
    total_volume_all_etfs = sum(total_volume, na.rm = TRUE)
  )

overall_stats_df <- data.frame(
  Metric = c(
    "Total ETFs",
    "ETFs with Zero Volume",
    "ETFs with Some Volume",
    "Median Average Volume",
    "Mean Average Volume",
    "Median Trading Frequency",
    "Mean Trading Frequency",
    "Total Volume (All ETFs)"
  ),
  Value = c(
    overall_stats$total_etfs,
    overall_stats$etfs_with_zero_volume,
    overall_stats$etfs_with_some_volume,
    overall_stats$median_avg_volume,
    overall_stats$mean_avg_volume,
    overall_stats$median_trading_frequency,
    overall_stats$mean_trading_frequency,
    overall_stats$total_volume_all_etfs
  ),
  stringsAsFactors = FALSE
)

overall_stats_df %>%
  gt() %>%
  tab_header(
    title = "Overall Volume Statistics"
  ) %>%
  fmt_number(columns = Value, rows = c(4, 5, 8), decimals = 0, use_seps = TRUE) %>%
  fmt_number(columns = Value, rows = c(6, 7), decimals = 3) %>%
  fmt_number(columns = Value, rows = c(1, 2, 3), decimals = 0, use_seps = FALSE) %>%
  cols_label(
    Metric = "Metric",
    Value = "Value"
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = cell_text(align = "right"),
    locations = cells_body(columns = Value)
  ) %>%
  opt_table_font(font = "Arial") %>%
  tab_options(
    table.width = pct(60),
    column_labels.background.color = "#f8f9fa",
    table_body.hlines.color = "#e9ecef",
    heading.border.bottom.color = "#dee2e6"
  )
Overall Volume Statistics
Metric Value
Total ETFs 354
ETFs with Zero Volume 0
ETFs with Some Volume 354
Median Average Volume 12,897
Mean Average Volume 9,231,415
Median Trading Frequency 1.000
Mean Trading Frequency 1.000
Total Volume (All ETFs) 205,116,148,576

Summary Statistics by Category

Distribution by Average Daily Volume Category

summary_adv <- volume_categorized %>%
  group_by(volume_category_adv) %>%
  summarise(
    count = n(),
    pct_of_total = round(100 * n() / nrow(volume_categorized), 1),
    avg_avg_volume = round(mean(avg_volume, na.rm = TRUE), 2),
    median_avg_volume = round(median(avg_volume, na.rm = TRUE), 2),
    .groups = "drop"
  ) %>%
  arrange(desc(avg_avg_volume))

summary_adv %>%
  gt() %>%
  tab_header(
    title = "Distribution by Average Daily Volume Category"
  ) %>%
  fmt_number(columns = avg_avg_volume, decimals = 0, use_seps = TRUE) %>%
  fmt_number(columns = median_avg_volume, decimals = 0, use_seps = TRUE) %>%
  fmt_number(columns = pct_of_total, decimals = 1, pattern = "{x}%") %>%
  cols_label(
    volume_category_adv = "Category",
    count = "Count",
    pct_of_total = "% of Total",
    avg_avg_volume = "Avg Volume",
    median_avg_volume = "Median Volume"
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = cell_text(align = "right"),
    locations = cells_body(columns = c(count, pct_of_total, avg_avg_volume, median_avg_volume))
  ) %>%
  opt_table_font(font = "Arial") %>%
  tab_options(
    table.width = pct(100),
    column_labels.background.color = "#f8f9fa",
    table_body.hlines.color = "#e9ecef",
    heading.border.bottom.color = "#dee2e6"
  )
Distribution by Average Daily Volume Category
Category Count % of Total Avg Volume Median Volume
Very High Volume (Top 10%) 36 10.2% 90,240,258 2,667,339
High Volume (75-90%) 53 15.0% 282,958 222,050
Above Average Volume (50-75%) 88 24.9% 41,003 33,100
Below Average Volume (25-50%) 88 24.9% 6,629 5,742
Low Volume (10-25%) 53 15.0% 1,468 1,435
Very Low Volume (Bottom 10%) 36 10.2% 150 121

Top and Bottom ETFs

Top 20 ETFs by Average Daily Volume

top_20 <- volume_categorized %>%
  head(20) %>%
  select(stock_code, stock_short_name, issuer, asset_class,
         avg_volume, total_volume, trading_frequency, volume_category_combined)

top_20 %>%
  gt() %>%
  tab_header(
    title = "Top 20 ETFs by Average Daily Volume"
  ) %>%
  fmt_number(columns = avg_volume, decimals = 0, use_seps = TRUE) %>%
  fmt_number(columns = total_volume, decimals = 0, use_seps = TRUE) %>%
  fmt_number(columns = trading_frequency, decimals = 3) %>%
  cols_label(
    stock_code = "Stock Code",
    stock_short_name = "Name",
    issuer = "Issuer",
    asset_class = "Asset Class",
    avg_volume = "Avg Volume",
    total_volume = "Total Volume",
    trading_frequency = "Trading Frequency",
    volume_category_combined = "Category"
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = cell_text(align = "right"),
    locations = cells_body(columns = c(avg_volume, total_volume, trading_frequency))
  ) %>%
  opt_table_font(font = "Arial") %>%
  tab_options(
    table.width = pct(100),
    column_labels.background.color = "#f8f9fa",
    table_body.hlines.color = "#e9ecef",
    heading.border.bottom.color = "#dee2e6"
  )
Top 20 ETFs by Average Daily Volume
Stock Code Name Issuer Asset Class Avg Volume Total Volume Trading Frequency Category
3033 CSOP HS TECH CSOP Asset Management Limited Equity 1,438,832,065 90,646,420,097 1.000 High Activity
7552 XI2CSOPHSTECH CSOP Asset Management Limited Equity 558,089,312 35,159,626,650 1.000 High Activity
2800 TRACKER FUND Hang Seng Investment Management Limited Equity 541,739,769 34,129,605,433 1.000 High Activity
7226 XL2CSOPHSTECH CSOP Asset Management Limited Equity 222,902,218 14,042,839,713 1.000 High Activity
7500 FI2 CSOP HSI CSOP Asset Management Limited Equity 172,591,598 10,873,270,704 1.000 High Activity
2828 HSCEI ETF Hang Seng Investment Management Limited Equity 85,423,320 5,381,669,175 1.000 High Activity
7200 FL2 CSOP HSI CSOP Asset Management Limited Equity 40,959,594 2,580,454,428 1.000 High Activity
3032 HSTECH ETF Hang Seng Investment Management Limited Equity 39,003,682 2,457,231,949 1.000 High Activity
3067 ISHARESHSTECH BlackRock Asset Management North Asia Limited Equity 35,304,585 2,224,188,855 1.000 High Activity
3416 A GX HSCEICC Mirae Asset Global Investments (Hong Kong) Limited Equity 28,130,622 1,772,229,208 1.000 High Activity
7709 XL2CSOPHYNIX CSOP Asset Management Limited Equity 22,522,740 991,000,565 1.000 High Activity
7568 FI2CSOPNASDAQ CSOP Asset Management Limited Equity 7,342,008 462,546,474 1.000 High Activity
3417 A GX HSTCC Mirae Asset Global Investments (Hong Kong) Limited Equity 6,590,576 415,206,276 1.000 High Activity
2823 ISHARES A50 BlackRock Asset Management North Asia Limited Equity 6,157,102 387,897,412 1.000 High Activity
3088 CAM HS TECH China Asset Management (Hong Kong) Limited Equity 5,573,622 351,138,205 1.000 High Activity
3466 HS HIGH DIV Hang Seng Investment Management Limited Equity 4,611,235 290,507,815 1.000 High Activity
2802 A CSOP HSCEICC CSOP Asset Management Limited Equity 3,013,900 15,069,500 1.000 High Activity
2822 CSOP A50 ETF CSOP Asset Management Limited Equity 2,790,634 175,809,938 1.000 High Activity
7288 FL2 CSOP HSCEI CSOP Asset Management Limited Equity 2,544,044 160,274,800 1.000 High Activity
7588 FI2 CSOP HSCEI CSOP Asset Management Limited Equity 2,435,730 153,451,004 1.000 High Activity

Bottom 20 ETFs by Average Daily Volume (Excluding Zero)

bottom_20 <- volume_categorized %>%
  filter(avg_volume > 0) %>%
  tail(20) %>%
  select(stock_code, stock_short_name, issuer, asset_class,
         avg_volume, total_volume, trading_frequency, volume_category_combined) %>%
  arrange(avg_volume)

bottom_20 %>%
  gt() %>%
  tab_header(
    title = "Bottom 20 ETFs by Average Daily Volume",
    subtitle = "Excluding ETFs with zero volume"
  ) %>%
  fmt_number(columns = avg_volume, decimals = 0, use_seps = TRUE) %>%
  fmt_number(columns = total_volume, decimals = 0, use_seps = TRUE) %>%
  fmt_number(columns = trading_frequency, decimals = 3) %>%
  cols_label(
    stock_code = "Stock Code",
    stock_short_name = "Name",
    issuer = "Issuer",
    asset_class = "Asset Class",
    avg_volume = "Avg Volume",
    total_volume = "Total Volume",
    trading_frequency = "Trading Frequency",
    volume_category_combined = "Category"
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = cell_text(align = "right"),
    locations = cells_body(columns = c(avg_volume, total_volume, trading_frequency))
  ) %>%
  opt_table_font(font = "Arial") %>%
  tab_options(
    table.width = pct(100),
    column_labels.background.color = "#f8f9fa",
    table_body.hlines.color = "#e9ecef",
    heading.border.bottom.color = "#dee2e6"
  )
Bottom 20 ETFs by Average Daily Volume
Excluding ETFs with zero volume
Stock Code Name Issuer Asset Class Avg Volume Total Volume Trading Frequency Category
3480 A VP USD MM Value Partners Hong Kong Limited Money Market 2 5 1.000 Mixed Activity
3471 A CAM HKDTMMF China Asset Management (Hong Kong) Limited Money Market 6 39 1.000 Mixed Activity
3472 A CAM USDTMMF China Asset Management (Hong Kong) Limited Money Market 20 40 1.000 Mixed Activity
83455 INVESCO QQQ-R Invesco Capital Management LLC Equity 26 763 1.000 Mixed Activity
3015 X TRNIFTY50 DWS Investment S.A. Equity 56 670 1.000 Mixed Activity
3072 AMOVA INET Amova Asset Management Hong Kong Limited Equity 59 762 1.000 Mixed Activity
9455 INVESCO QQQ-U Invesco Capital Management LLC Equity 64 1,605 1.000 Mixed Activity
83420 A VP RMB MM-R Value Partners Hong Kong Limited Money Market 70 279 1.000 Mixed Activity
9011 A ICBCCICCUSD-U China International Capital Corporation Hong Kong Asset Management Limited Money Market 70 2,029 1.000 Mixed Activity
9156 BOS 20 UST-U Bosera Asset Management (International) Co., Limited Fixed income 78 157 1.000 Mixed Activity
9078 PREMIA UST A -U Premia Partners Company Limited Fixed income 80 319 1.000 Mixed Activity
83196 A BOS USD MM-R Bosera Asset Management (International) Co., Limited Money Market 83 166 1.000 Mixed Activity
3192 A BOS RMB MM Bosera Asset Management (International) Co., Limited Money Market 91 1,360 1.000 Mixed Activity
82840 SPDR GOLD TRT-R SPDR GOLD TRUST Commodity 91 5,181 1.000 Mixed Activity
9077 PREMIA UST-U Premia Partners Company Limited Fixed income 93 465 1.000 Mixed Activity
83192 A BOS RMB MM-R Bosera Asset Management (International) Co., Limited Money Market 106 529 1.000 Mixed Activity
83053 A CSOP HKD MM-R CSOP Asset Management Limited Money Market 110 220 1.000 Mixed Activity
3011 A ICBCCICCUSD China International Capital Corporation Hong Kong Asset Management Limited Money Market 121 7,122 1.000 Mixed Activity
9196 A BOS USD MM-U Bosera Asset Management (International) Co., Limited Money Market 122 2,922 1.000 Mixed Activity
3077 PREMIA UST Premia Partners Company Limited Fixed income 122 3,425 1.000 Mixed Activity

Volume Concentration Analysis

top_10_pct_volume <- volume_categorized %>%
  arrange(desc(total_volume)) %>%
  head(round(nrow(volume_categorized) * 0.1)) %>%
  summarise(total_vol = sum(total_volume, na.rm = TRUE)) %>%
  pull(total_vol)

top_25_pct_volume <- volume_categorized %>%
  arrange(desc(total_volume)) %>%
  head(round(nrow(volume_categorized) * 0.25)) %>%
  summarise(total_vol = sum(total_volume, na.rm = TRUE)) %>%
  pull(total_vol)

concentration_df <- data.frame(
  Metric = c(
    "Top 10% of ETFs - Volume Share",
    "Top 25% of ETFs - Volume Share"
  ),
  Value = c(
    round(100 * top_10_pct_volume / overall_stats$total_volume_all_etfs, 1),
    round(100 * top_25_pct_volume / overall_stats$total_volume_all_etfs, 1)
  ),
  stringsAsFactors = FALSE
)

concentration_df %>%
  gt() %>%
  tab_header(
    title = "Volume Concentration Analysis",
    subtitle = "Percentage of total volume accounted for by top ETFs"
  ) %>%
  fmt_number(columns = Value, decimals = 1, pattern = "{x}%") %>%
  cols_label(
    Metric = "Metric",
    Value = "Percentage"
  ) %>%
  tab_style(
    style = cell_text(weight = "bold"),
    locations = cells_column_labels()
  ) %>%
  tab_style(
    style = cell_text(align = "right"),
    locations = cells_body(columns = Value)
  ) %>%
  opt_table_font(font = "Arial") %>%
  tab_options(
    table.width = pct(60),
    column_labels.background.color = "#f8f9fa",
    table_body.hlines.color = "#e9ecef",
    heading.border.bottom.color = "#dee2e6"
  )
Volume Concentration Analysis
Percentage of total volume accounted for by top ETFs
Metric Percentage
Top 10% of ETFs - Volume Share 99.5%
Top 25% of ETFs - Volume Share 99.9%

Report generated: 2025-12-18 10:36:54.234094