水曜日のドル円は上昇する

  • 曜日によってドル円の上昇に差はあるのかを調べてみる。
  • いつものようにプロットまで一気に。
import pandas as pd
from pandas.plotting import autocorrelation_plot
import numpy as np
import scipy
from scipy import stats as st

from matplotlib import pylab as plt
import seaborn as sns
sns.set()
import statsmodels.api as sm
from statsmodels.stats.multicomp import pairwise_tukeyhsd
import heapq as hp
import math

import datetime
import time

fx_data = pd.read_csv("/Users/ユーザー名/Documents/FX/foreign_exchange_historical_data/USDJPY/USDJPY_DAY.csv")#データの読み込み
#pandasの計算は遅いので、全てnumpy配列に変換
date = np.array(fx_data["date"])
opening = np.array(fx_data["opening"])#始値
high = np.array(fx_data["high"])#高値
low = np.array(fx_data["low"])#低値
closing = np.array(fx_data["closing"])#終値

dict = {"Monday":0, "Tuesday":0, "Wednesday":0, "Thursday":0, "Friday":0}#値上がりする日の集計
dict_all = {"Monday":0, "Tuesday":0, "Wednesday":0, "Thursday":0, "Friday":0}#曜日ごとの日数

for i in range(len(date)):#dictもdict_allも一度に計算
    temp = datetime.datetime.strptime(date[i], "%Y/%m/%d")
    temp = temp.strftime("%A")
    dict_all[temp] += 1
    if closing[i] >= opening[i]:
        dict[temp] += 1

ratio_of_increase = [a/b for a, b in zip(dict.values(), dict_all.values())]
#割合に変換

# %%まとめて選択
plt.bar(dict.keys(), ratio_of_increase, color = "skyblue", alpha = 0.7)
plt.title("usdjpy")
plt.xlabel("week")
plt.ylabel("increase")
plt.savefig("/Users/ユーザー名/Desktop/week.png", format = "png", dpi = 300)
  • 図示はこちら。

f:id:denovor:20200910144329p:plain

  • 水曜日が若干上昇しやすい印象もあるが・・・。
  • χ二乗検定にかけてみる。
dict_exp = [c//2 for c in dict_all.values()]#χ二乗検定の期待値は0.5とする。
dict_copy = [d for d in dict.values()]
stats.chisquare(dict_copy, f_exp = dict_exp)#χ二乗検定

dict_nega = [f-e for e, f in zip(dict.values(), dict_all.values())]
st.chi2_contingency(np.array([dict_copy, dict_nega]))#tableでも同様に

結果は以下。

Power_divergenceResult(statistic=1.2519046672629104, pvalue=0.8694811382649527)

(2.546521721041146,
 0.63632517975807,
 4,
 array([[347.39930955, 347.89988493, 348.4004603 , 348.4004603 ,
         347.89988493],
        [346.60069045, 347.10011507, 347.5995397 , 347.5995397 ,
         347.10011507]]))
  • 有意差はないようだ。
  • 解析期間(2007年4月2日から2020年8月15日)

dplyrの使い方(コードの簡略化)

denovor.hatenablog.com

  • この記事でのforの繰り返しをdplyrの関数で簡略化してみる。
  • 下記の記事を参考にした。

qiita.com

library(ggplot2)
library(dplyr)
library(RcppRoll)

d <- read.csv("~/Documents/FX/USDJPY/USDJPYD.csv")

N <- nrow(d) #データ数
D <- 245 #日数の最大値の設定
z1 <- array(rep(0, D)) #空の配列を用意、後で割合の集計に利用
z2 <- array(rep(0, D)) #空の配列を用意、後で割合の集計に利用

for (i in 1:D){
  d <- d %>%
    #ローリング関数で区間中の最大値・最小値を計算
    dplyr::mutate(rmax = roll_max(d$high, i, weights = NULL, 
                                  align = "left", fill = NA, na.rm = FALSE)) %>%
    dplyr::mutate(rmin = roll_min(d$low, i, weights = NULL, 
                                  align = "left", fill = NA, na.rm = FALSE)) %>%
    #条件設定
    dplyr::mutate(ask = if_else(high < rmax, 1, 0)) %>%
    dplyr::mutate(bid = if_else(low > rmin, 1, 0)) %>%
    #累積関数
    dplyr::mutate(sum_ask = cumsum(ask)) %>%
    dplyr::mutate(sum_bid = cumsum(bid))
  
  z1[i] <- d$sum_ask[N-i]/(N-i)
  z2[i] <- d$sum_bid[N-i]/(N-i)
}

z <- data.frame(day = 1:D, z1, z2) #データフレームの作成

ggplot() + theme_set(theme_bw(base_size = 14))
g <- ggplot(z, aes(x = z$day))
g <- g + geom_line(aes(y = z$z1, colour = "ask"), size = 1)
g <- g + geom_line(aes(y = z$z2, colour = "bid"), size = 1)
g <- g + xlab("day") + ylab("probability")
g <- g + scale_x_continuous(breaks = seq(0, D, by = 30))
g <- g + labs(colour = "ask/bid")
g <- g + scale_y_continuous(breaks = seq(0.4, 1, by = 0.1), limits = c(0.4, 1))
plot(g)
  • プロットは省略。結局for文を使わずにコーディングすることは難しいか・・・。
  • ポイントは累積関数を用いているところ。なぜか
z1[i] <- sum(d$ask == 1)/(N-i)

とするとエラーが出て進まなかった。

  • 二重for文よりは遥かに早い。

変動値の従う分布(9)

  • これでは(高値-始値)、(始値-安値)、(終値-始値)、(翌日の始値-当日の始値)の4つの分布について考察してきた。
  • 残された分布は(高値-終値)および(終値-安値)であるが、単純に計算量が4つだからといって上記の4変数で表してはならない。
  • これまで見てきたとおり、上記4つの従う分布は異なり、再生性を持たないからである。
  • 例えば、(高値-終値)=(高値-始値)-(終値-始値)と変形できるが、(高値-始値)は指数分布あるいはガンマ分布に従い、(終値-始値)はt分布に従う(と仮定した)場合、(高値-終値)の分布については何も言明できないのである。

変動値の従う分布(8)

変動値の従う分布(6) - denovorのブログ

  • ここで、指数分布はガンマ分布の特殊な場合であるから、(高値-始値)および(安値-始値)のガンマ分布への当てはめを推定してみる。
  • 同様にrstanを用いる。
data {
  #データYの宣言
  int N;
  vector[N] Y;
}

parameters {
  #サンプリングしたいパラメータθの宣言
  real<lower = 0> alpha;
  real<lower = 0> theta;
}

model {
  #尤度p(Y|θ)の記述
  #事前分布p(θ)の記述
 #指数分布を仮定
      Y ~ gamma(alpha, theta);
}
  • R kickはこちら。
d <- read.csv("~/Documents/FX/USDJPY/USDJPYD.csv")

co <- d$closing-d$opening
ho <-d$high-d$opening
ol <- d$low-d$opening
oo <- diff(d$opening, lag = 1)

d <- data.frame(N = nrow(d), co, ho, ol)

N <- nrow(d)
data <- list(N = nrow(d), Y = d$ho)
fit <- stan(file = "~/Documents/FX/USDJPY/Rdata/distribution_model2.stan", data = data, seed = 1234)

ms <- rstan::extract(fit)

new_data <- data.frame(ho)

alpha <- mean(ms$alpha)
lambda <- 1/mean(ms$theta)

myfun <- function(x){
  1/gamma(alpha)/(lambda)^(alpha)*x^(alpha-1)*exp(-x/lambda)
}

ggplot() + theme_set(theme_bw(base_size = 14))
g <- ggplot(new_data)
g <- g + geom_histogram(aes(x = new_data$ho, y = ..density.., fill = "high-opening"), binwidth = 0.1)
g <- g + labs(fill = "high-opening")
g <- g + stat_function(fun = myfun, colour = c("#EE3B3B"), size = 1 )
g <- g + xlab("high-opening")
plot(g)
  • 結果と図示はこちら。なかなかいい感じではないだろうか。
nference for Stan model: distribution_model2.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

          mean se_mean   sd    2.5%     25%     50%     75%   97.5% n_eff
alpha     1.33    0.00 0.03    1.27    1.31    1.33    1.35    1.38  1248
lambda    3.05    0.00 0.08    2.89    2.99    3.05    3.11    3.21  1256
lp__   -434.06    0.02 0.95 -436.53 -434.48 -433.78 -433.36 -433.11  1587
       Rhat
alpha     1
lambda    1
lp__      1

f:id:denovor:20181105232715p:plain

  • 始値-安値)の方は結果のみ。
Inference for Stan model: distribution_model2.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

         mean se_mean   sd    2.5%     25%     50%     75%   97.5% n_eff
alpha    1.22    0.00 0.03    1.17    1.20    1.22    1.24    1.28  1078
theta    2.48    0.00 0.07    2.35    2.43    2.48    2.53    2.63  1090
lp__  -847.26    0.03 1.05 -850.10 -847.67 -846.92 -846.51 -846.23  1242
      Rhat
alpha    1
theta    1
lp__     1

変動値の従う分布(7)

  • 最後は(終値-始値)で行う。
  • rstanでStudentのt分布を仮定し、自由度、平均(ロケーション)、スケールの推定を行う。
  • stanのコードは以下。
data {
  //データYの宣言
  int N;
  vector[N] Y;
}

parameters {
  //サンプリングしたいパラメータθの宣言
  real df;
  real mu;
  real sigma;
}

model {
  //尤度p(Y|θ)の記述
  //事前分布p(θ)の記述
      Y ~ student_t(df, mu, sigma);
}
  • これをRでキック。プロットまで一気に行う。t分布の密度関数は自分でセコセコ打ち込まなくてはいけないのが悲しい*1
d <- read.csv("~/Documents/FX/USDJPY/USDJPYD.csv")

co <- d$closing-d$opening
ho <-d$high-d$opening
ol <- d$low-d$opening
oo <- diff(d$opening, lag = 1)

d <- data.frame(N = nrow(d), co, ho, ol)

library(rstan)

N <- nrow(d)
data <- list(N = nrow(d), Y = d$co)
fit <- stan(file = "~/Documents/FX/USDJPY/Rdata/distribution_model.stan", data = data, seed = 1234)

ms <- rstan::extract(fit)

new_data <- data.frame(co)

v <- mean(ms$df)
mu <- mean(ms$mu)
sigma <- mean(ms$sigma)

myfun <- function(x){
  gamma((v+1)/2)*(1+(x-mu)^2/(v*(sigma)^2))^(-(v+1)/2)/(sigma*gamma(v/2))/sqrt(v*(pi))
}

ggplot() + theme_set(theme_bw(base_size = 14))
g <- ggplot(new_data)
g <- g + geom_histogram(aes(x = new_data$co, y = ..density.., fill = "closing-opening"), binwidth = 0.1)
g <- g + labs(fill = "closing-opening")
g <- g + stat_function(fun = myfun, colour = c("#EE3B3B"), size = 1 )
g <- g + xlab("high-opening")
plot(g)
  • 結果は以下。当てはまりは悪くはないが、尖度が今ひとつといったところ。

f:id:denovor:20181104224307p:plain

  • 拡大してみる。

f:id:denovor:20181104225050p:plain

  • より拡大するとやはり単純にt分布に従うとは言えないと考えられる。

f:id:denovor:20181104225326p:plain

  • (翌日の始値-当日の始値)についても結果のみを記す。
Inference for Stan model: distribution_model.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

          mean se_mean   sd     2.5%      25%      50%      75%    97.5% n_eff
df        3.65    0.01 0.27     3.16     3.46     3.64     3.83     4.23  2259
mu        0.01    0.00 0.01    -0.01     0.00     0.01     0.01     0.03  2651
sigma     0.47    0.00 0.01     0.44     0.46     0.47     0.47     0.49  2310
lp__  -1125.89    0.03 1.22 -1128.99 -1126.46 -1125.57 -1124.98 -1124.50  1878
      Rhat
df       1
mu       1
sigma    1
lp__     1

*1:なにか良い方法があれば追記

変動値の従う分布(6)

  • ドル円の変動値が従う分布を推定してみる。t分布や指数分布のパラメータは固定するものとする。
  • まずは(高値-始値)および(始値-安値)の従う指数分布のパラメータ\betaから推定してみる。
  • 推定にはrstanを用いる。
data {
  #データYの宣言
  int N;
  vector[N] Y;
}

parameters {
  #サンプリングしたいパラメータθの宣言
  real beta;
}

model {
  #尤度p(Y|θ)の記述
  #事前分布p(θ)の記述
 #指数分布を仮定
      Y ~ exponential(beta);
}
  • 読み込みは次の様になる。
d <- read.csv("~/Documents/FX/USDJPY/USDJPYD.csv")

co <- d$closing-d$opening
ho <-d$high-d$opening
ol <- d$low-d$opening
oo <- diff(d$opening, lag = 1)

d <- data.frame(N = nrow(d), co, ho, ol)

library(rstan)

N <- nrow(d)
data <- list(N = nrow(d), Y = d$ho)
fit <- stan(file = "~/Documents/FX/USDJPY/Rdata/distribution_model.stan", data = data, seed = 1234)
ms <- rstan::extract(fit)
  • 結果は以下。
        mean se_mean   sd    2.5%     25%     50%     75%   97.5%
beta    2.30    0.00 0.04    2.21    2.27    2.30    2.33    2.38
lp__ -503.01    0.02 0.74 -505.16 -503.19 -502.73 -502.53 -502.48
     n_eff Rhat
beta  1468    1
lp__  1886    1
  • きちんと収束している。図示は次。
myfun <- function(x){
  mean(ms$beta)*exp(-mean(ms$beta)*x)
}

new_data <- data.frame(ho, ol)

ggplot() + theme_set(theme_bw(base_size = 14))
g <- ggplot(new_data)
g <- g + geom_histogram(aes(x = new_data$ho, y = ..density.., fill = "high-opening"), binwidth = 0.1)
g <- g + labs(fill = "high_low")
g <- g + stat_function(fun = myfun, colour = c("#EE2C2C"), size = 1 )
plot(g)
  • ggplot2でヒストグラムを描写する時は、countとdensityの2種類のどちらかを指定できるが、ここではdensityを指定しておかないとうまく重ね書きができない。

f:id:denovor:20181103234555p:plain

  • 当てはまりは良好そうである。(始値-安値)でも推定すると、\betaの平均値は2.03となる。

変動値の従う分布(5)

  • 分布を調べるのに今度はghypパッケージを使用してみる。
  • AICをもとに一般化双曲型分布、normal inverse Gaussian分布、Variance Gamma分布、歪んだt分布などへの当てはまりを調べることができる。

https://cran.r-project.org/web/packages/ghyp/ghyp.pdf

d <- read.csv("~/Documents/FX/USDJPY/USDJPYD.csv")

co <- d$closing-d$opening
ho <-d$high-d$opening
ol <- d$low-d$opening
oo <- diff(d$opening, lag = 1)

stepAIC.ghyp(co)
  • まずはstepAICで最も当てはまりの良い分布を探索。
$fit.table
   model symmetric       lambda alpha.bar           mu     sigma
8    NIG      TRUE -0.500000000 0.7295650  0.002865157 0.6549768
7    hyp      TRUE  1.000000000 0.2404656 -0.002839829 0.6481153
9     VG      TRUE  1.140484515 0.0000000 -0.006562022 0.6470659
6   ghyp      TRUE  0.130331659 0.6745057  0.001558661 0.6522863
3    NIG     FALSE -0.500000000 0.7326138  0.012276449 0.6546704
4     VG     FALSE  1.133472700 0.0000000 -0.011106709 0.6473282
2    hyp     FALSE  1.000000000 0.2356592 -0.005071144 0.6482965
1   ghyp     FALSE  0.008506488 0.7012681  0.008752815 0.6525317
10     t      TRUE -1.821723585 0.0000000  0.004649114 0.6809921
5      t     FALSE -1.831107876 0.0000000  0.017310757 0.6796988
11 gauss      TRUE           NA       Inf -0.001664452 0.6555834
          gamma      aic       llh converged n.iter
8   0.000000000 5573.093 -2783.546      TRUE    122
7   0.000000000 5573.315 -2783.658      TRUE    146
9   0.000000000 5573.386 -2783.693      TRUE     78
6   0.000000000 5574.444 -2783.222      TRUE    287
3  -0.013992090 5574.642 -2783.321      TRUE    149
4   0.009427421 5574.985 -2783.492      TRUE    373
2   0.003389183 5575.284 -2783.642      TRUE    233
1  -0.010626758 5576.207 -2783.104     FALSE    502
10  0.000000000 5582.168 -2788.084      TRUE     86
5  -0.019432190 5583.372 -2787.686      TRUE    193
11  0.000000000 6003.187 -2999.593      TRUE      0
  • NIGはnormal inverse Gaussian分布のこと。

Normal-inverse Gaussian distribution - Wikipedia

  • 高値、安値でも同様にstepAICを行う。結果は省略するが、optim(尤度関数への当てはめ)が収束している分布の中ではやはりNIGが最も上位にあがる。
  • (翌日の始値-当日の始値)ではVariance Gammaが最上位に。ただし、NIGも3番目と悪くない。
  • データのまとめも簡単で、以下のコードで済む*1
fit1 <- fit.NIGuv(co)
summary(fit1)
hist(fit1)
  • 結果はこちら。
Asymmetric Normal Inverse Gaussian Distribution:

Parameters:
  alpha.bar          mu       sigma       gamma 
 0.73261378  0.01227645  0.65467042 -0.01399209 

Call:
fit.NIGuv(data = co)

Optimization information:
log-Likelihood:                -2783.321 
AIC:                           5574.642 
Fitted parameters:             alpha.bar, mu, sigma, gamma;  (Number: 4)
Number of iterations:          149 
Converged:                     TRUE 


f:id:denovor:20181102162550p:plain

*1:例えば分布に対称性を課す場合は、symmetric = TRUE とすれば良い