Mon test numérique de l'hypothèse des taux absolus

Bonjour, Habr!

Cette publication m'a semblé intéressante: nous obtenons des taux de change absolus à partir de taux de change croisés et j'ai voulu tester la capacité de trouver ce taux de change absolu par le biais d'une modélisation numérique, en abandonnant généralement l'algèbre linéaire.



Les résultats étaient intéressants.

L'expérience sera petite: 4 devises, 6 paires de devises. Pour chaque paire, une mesure de parcours.

Commençons donc


L'hypothèse est que la valeur de n'importe quelle devise peut être exprimée avec une certaine valeur qui tiendra compte de la valeur des autres devises dans lesquelles elle est cotée, tandis que les autres devises elles-mêmes seront exprimées dans la valeur de toutes les autres devises. Il s'agit d'une tâche récursive intéressante.

Il y a 4 devises:

  • USD
  • eur
  • chf
  • gbp

Pour eux, les paires de devises ont été composées:

  • eurusd
  • gbpusd
  • eurchf
  • eurgbp
  • gbpchf
  • usdchf

Veuillez noter que si le nombre de devises est n = 4, alors le nombre de paires est k = (n ^ 2 - n) / 2 = 6. Cela n'a aucun sens de chercher usdeur si eurusd est cité ...

Au temps t, le taux de change d'un des prestataires a été mesuré:



Des calculs seront effectués pour ces valeurs.

Math


Je résous le problème en prenant analytiquement le gradient de la fonction de perte, qui est essentiellement un système d'équations.

Le code de l'expérience sera en 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 permet d'utiliser stats :: D pour prendre un dérivé d'une fonction. Par exemple, si nous voulons différencier par la devise USD, nous obtenons l'expression:
2 * (eur / usd ^ 2 * (eurusd - eur / usd)) + 2 * (gbp / usd ^ 2 * (gbpusd -
gbp / usd)) - 2 * (1 / chf * (usdchf - usd / chf))
Pour réduire la valeur de la fonction express, nous allons effectuer une descente de gradient et il est immédiatement clair (nous voyons des différences carrées) que la valeur minimale sera nulle, ce dont nous avons besoin.

 -deriv_vals * lr 

Le pas de descente de gradient sera régulé par le paramètre lr et tout cela est pris avec un signe négatif.

Autrement dit, nous sélectionnons les taux de 4 devises de sorte que toutes les paires de devises de l'expérience reçoivent des valeurs égales aux valeurs initiales de ces paires. Mmm, résolvons le puzzle - dans le front!

Résultats


Afin de ne pas étirer, je vais immédiatement vous informer de ce qui suit: l'expérience dans son ensemble a réussi, le code a fonctionné, l'erreur s'est rapprochée, proche de zéro. Mais j'ai remarqué que les résultats sont toujours différents.

Une question pour les connaisseurs: il semble que cette tâche ait un nombre illimité de solutions, mais en cela je suis un zéro complet, je pense qu'ils me le diront dans les commentaires.

Pour vérifier la (dé) stabilité de la solution, j'ai simulé 1000 fois sans fixer la graine PRNG pour les valeurs de départ des valeurs monétaires.

Et voici l'image du kata: l'erreur atteint toujours 0,00001 et moins (l'optimisation est définie de cette façon), tandis que les valeurs des devises flottent de partout. Il s'avère qu'il y a toujours une décision différente, messieurs!

Encore une fois, cette image, l'axe des y dans les unités d'origine (pas log.):



Pour que vous puissiez répéter cela, ci-dessous, je joins le code complet.

Code
 # 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() 


Le code pour 1000 simulations fonctionne pendant environ une minute.

Conclusion


Voici ce qui ne me paraît pas clair:

  • Est-il possible de stabiliser la solution d'une manière mathématique délicate;
  • s'il y aura une convergence avec plus de devises et de paires de devises;
  • s'il n'y a pas de stabilité, alors pour chaque nouvel instantané de données, nos devises marcheront comme elles le souhaitent, si vous ne corrigez pas la graine PRNG, et c'est un échec.

L'idée globale semble très vague en l'absence de conditions préalables et de limitations intelligibles. Mais c'était intéressant!

Eh bien, je voulais aussi dire que l'on peut se passer d'OLS lorsque les données sont délicates, les matrices sont singulières, enfin, ou lorsque la théorie est mal connue (ehh ...).

Merci eavprog pour le message initial.

Salut!

Source: https://habr.com/ru/post/fr450874/


All Articles