我对绝对利率假设的数值检验

哈Ha!

该出版物对我来说似乎很有趣: 我们从成对的交叉货币汇率中获得绝对汇率,并且我想测试通过数值模型找到这种绝对汇率的能力,通常放弃线性代数。



结果很有趣。

实验将很小:4个货币,6个货币对。 对于每一对,进行一次疗程测量。

所以我们开始吧


假设是,任何一种货币的价值都可以表示为一种值,该值将考虑其所引用的其他货币的价值,而其他货币本身将以所有其他货币的价值表示。 这是一个有趣的递归任务。

有4种货币:

  • 美元
  • 欧元
  • CHF
  • gbp

对于他们,拨打了货币对:

  • 欧元
  • gbpusd
  • eurchf
  • urg
  • gbpchf
  • 美元汇率

请注意,如果货币数量为n = 4,则货币对数量为k =(n ^ 2-n)/ 2 =6。如果引用欧元,则寻找usdeur是没有意义的...

在时间t,测量其中一个提供者的汇率:



将针对这些值进行计算。

数学


我通过分析损失函数的梯度来解决问题,该函数本质上是一个方程系统。

实验代码将在R中:

#set.seed(111) usd <- runif(1) eur <- runif(1) chf <- runif(1) gbp <- runif(1) # snapshot of values at time t eurusd <- 1.12012 gbpusd <- 1.30890 eurchf <- 1.14135 eurgbp <- 0.85570 gbpchf <- 1.33373 usdchf <- 1.01896 ## symbolic task ------------ express <- expression( (eurusd - eur / usd) ^ 2 + (gbpusd - gbp / usd) ^ 2 + (eurchf - eur / chf) ^ 2 + (eurgbp - eur / gbp) ^ 2 + (gbpchf - gbp / chf) ^ 2 + (usdchf - usd / chf) ^ 2 ) eval(express) x = 'usd' D(express, x) eval(D(express, x)) 

R允许使用stats :: D接受函数的派生。 例如,如果我们想用美元货币区分,我们得到表达式:
2 *(欧元/美元^ 2 *(欧元-欧元/美元))+ 2 *(英镑/美元^ 2 *(英镑-
gbp / usd))-2 *(1 / CHF *(USDDCHF-USD / CHF))
为了减小express函数的值,我们将执行梯度下降,并且立即清楚(我们看到平方差)最小值将为零,这正是我们所需要的。

 -deriv_vals * lr 

梯度下降步骤将由参数lr控制,并且所有这些都带有负号。

也就是说,以人类的话来说,我们选择4种货币的汇率,以使实验中的所有货币对都获得等于这些货币对初始值的值。 嗯,让我们解决这个难题-在额头上!

结果


为了避免延展,我将立即通知您以下信息:整个实验都成功了,代码起作用了,错误接近了,接近于零。 但是后来我注意到结果总是不同的。

鉴赏家的一个问题:似乎这项任务有无数个解决方案,但在此我是一个完整的零,我想他们会在评论中告诉我。

为了验证解决方案的(非)稳定性,我模拟了1000次,而没有为货币值的起始值固定PRNG种子。

这就是kata的图片:误差总是达到0.00001或更低(以这种方式设置优化),而货币的价值则使魔鬼浮出水面,知道了哪里。 事实证明,总会有一个不同的决定,先生们!

再次显示此图片,y轴为原始单位(不是对数):



为了使您可以重复此操作,在下面附上完整的代码。

代号
 # clear environment rm(list = ls()); gc() ## load libs library(data.table) library(ggplot2) library(magrittr) ## set WD -------------------------------- # your dir here ... ## set vars ------------- currs <- c( 'usd', 'eur', 'chf', 'gbp' ) ############ ## RUN SIMULATION LOOP ------------------------------- simuls <- 1000L simul_dt <- data.table() for( s in seq_len(simuls) ) { #set.seed(111) usd <- runif(1) eur <- runif(1) chf <- runif(1) gbp <- runif(1) # snapshot of values at time t eurusd <- 1.12012 gbpusd <- 1.30890 eurchf <- 1.14135 eurgbp <- 0.85570 gbpchf <- 1.33373 usdchf <- 1.01896 ## symbolic task ------------ express <- expression( (eurusd - eur / usd) ^ 2 + (gbpusd - gbp / usd) ^ 2 + (eurchf - eur / chf) ^ 2 + (eurgbp - eur / gbp) ^ 2 + (gbpchf - gbp / chf) ^ 2 + (usdchf - usd / chf) ^ 2 ) ## define gradient and iterate to make descent to zero -------------- iter_max <- 1e+3 lr <- 1e-3 min_tolerance <- 0.00001 rm(grad_desc_func) grad_desc_func <- function( lr, curr_list ) { derivs <- character(length(curr_list)) deriv_vals <- numeric(length(curr_list)) grads <- numeric(length(curr_list)) # symbolic derivatives derivs <- sapply( curr_list, function(x){ D(express, x) } ) # derivative values deriv_vals <- sapply( derivs, function(x){ eval(x) } ) # gradient change values -deriv_vals * lr } ## get gradient values ---------- progress_list <- list() for( i in seq_len(iter_max) ) { grad_deltas <- grad_desc_func(lr, curr_list = currs) currency_vals <- sapply( currs , function(x) { # update currency values current_val <- get(x, envir = .GlobalEnv) new_delta <- grad_deltas[x] if(new_delta > -1 & new_delta < 1) { new_delta = new_delta } else { new_delta = sign(new_delta) } new_val <- current_val + new_delta if(new_val > 0 & new_val < 2) { new_val = new_val } else { new_val = current_val } names(new_val) <- NULL # change values of currencies by gradient descent step in global env assign(x, new_val , envir = .GlobalEnv) # save history of values for later plotting new_val } ) progress_list[[i]] <- c( currency_vals, eval(express) ) if( eval(express) < min_tolerance ) { break('solution was found') } } ## check results ---------- # print( # paste0( # 'Final error: ' # , round(eval(express), 5) # ) # ) # # print( # round(unlist(mget(currs)), 5) # ) progress_dt <- rbindlist( lapply( progress_list , function(x) { as.data.frame(t(x)) } ) ) colnames(progress_dt)[length(colnames(progress_dt))] <- 'error' progress_dt[, steps := 1:nrow(progress_dt)] progress_dt_melt <- melt( progress_dt , id.vars = 'steps' , measure.vars = colnames(progress_dt)[colnames(progress_dt) != 'steps'] ) progress_dt_melt[, simul := s] simul_dt <- rbind( simul_dt , progress_dt_melt ) } ggplot(data = simul_dt) + facet_wrap(~ variable, scales = 'free') + geom_line( aes( x = steps , y = value , group = simul , color = simul ) ) + scale_y_log10() + theme_minimal() 


1000个模拟的代码可以工作大约一分钟。

结论


这仍然是我不清楚的地方:

  • 是否可以通过棘手的数学方法来稳定解决方案;
  • 是否将有更多的货币和货币对汇合;
  • 如果没有稳定性,那么对于每个新数据快照,如果您不修复PRNG种子,我们的货币将按照自己的意愿行事,这是失败的。

在没有任何可理解的前提条件和限制的情况下,整个想法似乎非常模糊。 但这很有趣!

好吧,我也想说,当数据比较棘手,矩阵很奇异或理论不为人所知时,您可以不使用OLS。

感谢eavprog的初始消息。

再见!

Source: https://habr.com/ru/post/zh-CN450874/


All Articles