SBI証券の手数料プランはスタンダード/アクティブプランのどちらを選ぶべきか?

俺はSBI証券を使っているのだが、その手数料プラン(アクティブ or スタンダード)をどっちにするのかあまり真面目に考えてこなかったので、真面目に考えたい。

俺の俺による俺のためのデータサイエンスだ。

さて、国内株式の手数料を教えてくださいのページにあるように国内株式の手数料は下記のように与えられる。 f:id:teramonagi:20200325131548p:plain

この情報を元に

  • 一日の取引回数(The number of trading)
  • 一回の取引金額(Unit trading price)
  • 一回の取引金額のばらつき(これは一回の取引金額の50% ~ 150%になるように一様に振っている)

をある程度の幅で振ってシミュレーションさせた結果が以下になる。

図1 f:id:teramonagi:20200327092606p:plain

この表は

  • 横軸:一日の取引回数(The number of trading)
  • 縦軸:一回の取引金額(Unit trading price)

で、図中に出てくる数値はそれぞれ

  • a: アクティブプラン
  • s: スタンダードプラン

の ”一取引あたりの手数料(平均値)” の数値を表してて、水色背景だとアクティブプランを選ぶのが有利(手数料安い)、赤色背景だとスタンダードプラン有利という見方をする。

これを見るとざっくりとした傾向として

  • 一回の取引金額(Unit trading price)が少ないときはアクティブプラン
  • 一回の取引金額(Unit trading price)が大きいときはスタンダードプラン

と選べば良さそう。あとは意外と甲乙つけ難しというか取引回数に依存しそうだなということがわかった。 ちなみに "一回の取引金額のばらつき"を0にしても結論は変わらずな図になる。

おまけとして、一回の取引金額を固定して、一取引あたりの手数料分布の一日の取引回数依存性を見てみると以下のようになる。 この取引金額のレンジだと取引回数を増やせば増やすほどアクティブプランがお得になってるのがわかる。

図2:一回の取引金額が10万円の時の一取引あたりの手数料分布(横軸は一日の取引回数) f:id:teramonagi:20200327093723p:plain

図3:一回の取引金額が100万円の時の一取引あたりの手数料分布(横軸は一日の取引回数) f:id:teramonagi:20200327093720p:plain

結論

「月にO(10万円)、ETFで積み増しする」のをメインに、動かしても一日1~2百万レベルだとするとアクティブプランでいいんだろうな~。 ちなみに米国株でも似たような計算をしたが、明らかに手数料が高くてアレな気持ちになった。

スタンダードプランだと手数料に応じたポイント還元もあるようだが、還元率が1.1%なのであまり魅力的には見えない。

Reproducible Code

library("dplyr")
library("tidyr")
library("ggplot2")

fee_us <- function(x, usdjpy = 110){
  min(usdjpy * 20, 0.45 * 0.01 * x)
}
fee_active <- function(x){
  x <- x/10^4
  if(x <= 50){
    0
  } else if(x > 50 & x <= 100){
    762
  } else{
    762 + 400*ceiling((x - 100) / 100)
  }
}

fee_standard <- function(x){
  x <- x/10^4
  if(x <= 5){
    50
  } else if(x >   5 & x <=  10){
    90
  } else if(x >  10 & x <=  20){
    105
  } else if(x >  20 & x <=  50){
    250
  } else if(x >  50 & x <= 100){
    487
  } else if(x > 100 & x <= 150){
    582
  } else if(x > 150 & x <= 3000){
    921
  } else{
    973
  }
}

simulate_one <- function(size_mc, size_trading, mean_price, usdjpy, noize=c(min=0.5, max=1.5)){
  x <- list()
  for(i in seq_len(size_mc)){
    price <- rep(mean_price, size_trading)
    if(!is.null(noize)){
      price <- price*runif(size_trading, min=noize["min"], max=noize["max"])
    }
    active <- fee_active(sum(price))
    standard <- sum(purrr::map_dbl(price, fee_standard))
    us <- sum(purrr::map_dbl(price, ~ fee_us(.x, usdjpy)))
    total_cost <- c(active, standard)
    x[[i]] <- list(
      type = c("active", "standard"),
      total_cost = total_cost,
      cost_per_trade = total_cost/size_trading,
      total_amount = rep(sum(price), 2),
      amount_per_trade <- rep(mean(price), 2)
    )
  }
  cbind(size_mc, size_trading, mean_price, bind_rows(x))
}

parameter <- expand.grid(
  size_trading = c(1, 2, 3, 5, 10, 20, 50, 100), 
  mean_price   = c(1, 5, 10, 15, 20, 30, 50, 100, 200, 300, 500, 1000, 3500) * 10^4
)
size_mc <- 10^2
usdjpy <- 110
df <- purrr::pmap_dfr(parameter, function(...) {
    x <- tibble(...)
    #simulate_one(size_mc, x$size_trading, x$mean_price, usdjpy, noize=c(min=1, max=1))
    simulate_one(size_mc, x$size_trading, x$mean_price, usdjpy, noize=c(min=0.5, max=1.5))
}
)
dfs <- df %>% group_by(size_trading, mean_price, type) %>% 
  summarize(
    total_cost_mean     = mean(total_cost), 
    total_cost_sd       = sd(total_cost), 
    cost_per_trade_mean = mean(cost_per_trade), 
    cost_per_trade_sd   = sd(cost_per_trade)
  ) %>% 
  select(size_trading, mean_price, type, cost_per_trade_mean) %>%
  tidyr::pivot_wider(names_from = type, values_from = cost_per_trade_mean) %>%
  mutate(
    active_is_better = active < standard, 
    text = paste0("a: ", round(active), "\ns: ", round(standard)))
# 図1
ggplot(dfs, aes(factor(size_trading), factor(mean_price), fill=active_is_better)) +
  geom_tile(color = "gray") + 
  geom_text(aes(label = text), lineheight = 0.8) + 
  labs(y="Unit trading price", x = "The number of trading")
# 図2, 3
ggplot(filter(df, mean_price ==  10*10^4, size_trading <= 100), aes(x=factor(size_trading), y=cost_per_trade, fill=type)) + geom_violin(scale = "width") 
ggplot(filter(df, mean_price == 100*10^4, size_trading <= 100), aes(x=factor(size_trading), y=cost_per_trade, fill=type)) + geom_violin(scale = "width")