Comments 70
Возможно, нужно дополнительное условие, того, что любая валюта представляет ценность, то есть, стремится к какому-нибудь среднему по больнице значению, а не нулю
У меня ошибка стремится к нулю, а ценность валюты инициализируется от 0 до 1 с равномерным распределением (в каждой итерации симуляции). А конечная стоимость валют тоже получается в этом же диапазоне, ну, может, с другой плотностью конечного решения, не равномерным…
Чуть более подробно в следующем комменте.
Кратко: без матана никак
Еще бы вы сразу сказали, что именно никак, я не понял какой вопрос адресуется, спасибо.
1. У вас тут метод наименьших квадратов, который предполагает нормальное (гауссово) распределение шумов (ошибок в начальных данных). Всем хорошо нормальное распределение, формулы короткие, считать и доказывать теоремки просто, за что его любят преподы математики и авторы учебников. Только вот с реальностью оно дружит в большинстве случаев примерно никак. Гауссово распределение в некотором смысле максимально компактное, у него самые тощие хвосты из всех возможных. Из-за этого любая ошибка во входных или промежуточных данных, которая не укладывается в рамки гауссова распределения (а большинство — не укладываются), вызывает «панику-панику» и сносит ваш алгоритм в неведомые дали. Что вы, собственно, и наблюдаете. Решение: разбираться с распределениями (строить гистограммы шумов, аппроксимировать их разными стат. моделями и считать критерий Колмогорова-Смирнова). А затем подбирать вид штрафной функции исходя из наиболее подходящего распределения.
2. Задача в вашей постановке существенно некорректно поставлена, в том смысле, что любое малое возмущение во входных данных может привести к сколько угодно большой ошибке на выходе. Наивными алгоритмами такое не решается от слова совсем, нужно как минимум гладить (т.е. на каждом шаге домножать вектор текущего решения на некую матрицу B слева и на TB справа). Определитель матрицы В должен быть равен строго 1, по диагонали должны стоять числа чуть меньше 1, а в остальных ячейках должны стоять некие малые числа. Какие конкретно это должны быть числа — нужно смотреть по результатам решения вопроса номер 1.
У вас тут метод наименьших квадратов
Простите, но нет. Я же прямо написал, что линейную алгебру не использую.
У меня решение через аналитический градиент.
Всем хорошо нормальное распределение, формулы короткие, считать и доказывать теоремки просто, за что его любят преподы математики и авторы учебников.
Предположение о нормальности ошибок не используется совсем здесь — не нужно.
Задача в вашей постановке существенно некорректно поставлена, в том смысле, что любое малое возмущение во входных данных может привести к сколько угодно большой ошибке на выходе.
Я не знаю, что такое наивный алгоритм в вашем понимании. Но действительно симуляция показывает зависимость решения от начальных условий.
Простите, но нет. Я же прямо написал, что линейную алгебру не использую.
Простите, но да.
Вот это ваш код?
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
)
Это МНК в незамутнённом первозданном виде.
Мой код.
Тот факт, что я минимизирую сумму квадратов отклонений не делает метод МНК, я ее использую теорию матриц вообще, я делаю то, что делает нейронная сеть...
Попрошу вас немного прояснить ваш аргумент
Градиентный спуск просто добавляет в вашу систему уравнений дополнительные переменные — разностные производные — чтобы понизить степень уравнения.
Если вы принципиально не готовы курить матан, сделайте хотя бы следующее:
1. Добавьте коэффициенты перед квадратами, т.е. в сумме будет не просто (eurusd — eur/usd)², а типа 1.05*(eurusd...)² + 0.89*(...)² Сами числа возьмите методом научного тыка из неких внешних данных, типа средних объёмов торгов или денежной массы М2. Арифметическое среднее ваших коэффициентов должно быть равно 1. Это сменит «вшитое» в МНК распределение с гауссова на хи-квадрат, которое всё же намного более гибкое в плане шумов.
2. Когда вы берёте начальные значения для тестов из ГСПЧ… Не знаю как сделан ГСПЧ в R, но думаю как везде — с равномерным распределением. Берите вместо просто random() арифметическое среднее от 6 идущих подряд вызовов random(). Эффект должен быть совершенно волшебным.
3. Не знаю, как у вас там дальше всё сделано, но данные всё же нужно гладить на каждом шаге. Это совершенно элементарно делается, только можно загладить насмерть: система будет устойчиво сходиться к единичному вектору, т.е. к состоянию «все валюты равны».
Это называется ordinary least squares, МНК. Если я решаю регрессионную задачу и использую квадраты невязок, через градиентный спуск, бред говорить об МНК, другие расчеты, другая сложность, другие результаты могут быть, в конце концов… Я мог выбрать фитнеса функцию как сумму модулей невязок, например… Понятно, я надеюсь…
1) производные не разностные, в тексте написано ясно (вы читали его?):
2 * (eur/usd^2 * (eurusd — eur/usd)) + 2 * (gbp/usd^2 * (gbpusd —
gbp/usd)) — 2 * (1/chf * (usdchf — usd/chf))
Это производная от дифференцмруемой ф-и. Дальше она умножается на скорость обучения, и все, классика жанра… Я ещё сделал gradient clipping [0;1].
Если вы принципиально не готовы курить матан, сделайте хотя бы следующее
Я решаю задачу БЕЗ линейной алгебры, то есть, без матана. Я исследую результат.
Это сменит «вшитое» в МНК распределение с гауссова на хи-квадрат, которое всё же намного более гибкое в плане шумов.
Это generalized linear model или что? Я вот не понял сейчас почему веса тут нужны, вот от слова совсем не понял…
Я вообще, мне не ясно как свести задачу к МНК, не ясен дизайн матрицы, я бы и по матану попробовал решить, и через GLM…
Гауссово распределение в некотором смысле максимально компактное, у него самые тощие хвосты из всех возможных. Из-за этого любая ошибка во входных или промежуточных данных, которая не укладывается в рамки гауссова распределения (а большинство — не укладываются), вызывает «панику-панику» и сносит ваш алгоритм в неведомые дали. Что вы, собственно, и наблюдаете. Решение: разбираться с распределениями (строить гистограммы шумов, аппроксимировать их разными стат. моделями и считать критерий Колмогорова-Смирнова). А затем подбирать вид штрафной функции исходя из наиболее подходящего распределения.</blockquot
У мня невязки уходят в ноль, задача решается на 100%, я не то делаю, что думаете вы. Тут нет выбросов, смещающих решение. Просто бесконечно много решений. В МНК это может быть возможно, если число независимых переменных намного больше числа наблюдений, не понимаю как у меня это получается.
- Вы совершенно бескомпромиссно пишете чушь. Из того, что автор в качестве метрики выбрал квадрат евклидова расстояния, совершенно не следует никакого предположения о нормальности чего-либо. По факту, у него вообще статистики и распределений нет. Статистика и распределения появились бы, если бы автор начал оценивать погрешность своих результатов. Но этого нет, есть просто поиск оптимума некоторой непрерывной и дифференцируемой функции.
- Неустойчивость решения надо доказывать. Никаких предпосылок для "малое возмущение во входных данных может привести к сколько угодно большой ошибке на выходе" тут нет.
Как ниже верно замечено, достаточно очевидно, что решение здесь определено с точностью до множителя, поэтому оно не единственное. Надо накладывать еще какие-нибудь ограничения.
2. Нет, это устойчивость решения для абсолютно любого метода надо доказывать. Более того, доказано, что в данном случае решение заведомо неустойчиво. У многомерной поверхности второго порядка примерно всегда более одного экстремума, а соответственно есть седловые точки между ними. Любое сколь угодно малое отклонение сваливает решение или в один экстремум, или в другой, или вообще непонятно куда.
То, что в задаче ещё и недостаточно информации (нужно накладывать ограничения на сумму или произведение координат), делает ситуацию только хуже.
- У автора в постановке задачи нет шумов. Поэтому никакие предположения об их распределении тут неуместны. Если же говорить в отрыве от статьи… Когда вы предполагаете нормальное распределение величины и ищите параметры этого распределения, то центр распределения можно найти с помощью МНК. В обратную сторону это не работает никак. Если вы что-то ищете с помощью метода наименьших квадратов, то никаких гарантий каких-либо распределений это не дает. Но, еще раз повторю, у автора нет распределений, поэтому все предположения на этот счет бессмысленны и нерелевантны.
- Целевая функция в этой задаче квадратично зависит от начальных данных. В окрестностях решения она дифференцируема и раскладывается в ряд Тейлора, следовательно малым изменениям начальных данных соответствуют малые изменения решения. Или у вас какое-то свое новое определение устойчивости? Напишите его здесь в формальной форме, типа "Решение называется устойчивым, если для любого эпсилон больше нуля и т. д. и т. п.", чтобы можно было предметно обсудить...
А вообще, если почитать ниже, то эта задача логарифмированием сводится к линейной. Так что все ваши рассуждения про неустойчивость и множество экстремумов ну совсем не в кассу.
… + (usd^2 + eur^2 + chf^2 + grb^2)*0.1
С порядком множителя (0.1) можно смело поиграться
alpha <- 1e-4
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 +
(usd^2 + eur^2 + chf^2 + gbp^2) * alpha
) * chf^2 * gbp^2 * usd^2 * eur^2
)
Результат:
Финальные решения похоже стали более равномерно распологаться в [0;1]. Ошибка опускается до 0,05 или выше.
Я бы указал на три фактора из области экономики:
1. Объем обмена по каждой из валюты. То есть все эти же характеристики могли бы учитываться с некоторыми весами.
2. Конвертируемость. Есть валюты свободно конвертируемые или твердые. И для таких валют категория курс имеет смысл. И есть валюты не конвертируемые (с разной степенью не конвертируемости) — для них курс носит почти случайный, ситуационный характер.
3. Курс валюты (как одно число), который устанавливается государством по своим правилам является величиной которая используется для различных расчетов но с точки зрения экономики не является ни курсом покупки, ни курсом продажи (это два числа). Этот курс конечно соотносится как правило с курсом покупки/продажи и даже пытается оказывать на него регулирующее влияние. Но иногда расхождения могут быть существенные (напрмер во время кризисов)
Пытаясь выделить своеобразный «первичный» курс в котором можно выразить все другие валюты — система делает все валюты конвертируемыми а это не так. И также уходит разница между курсом покупки/продажи. А это как раз один из важных факторов которые нужно учитывать.
по которым есть хоть какой-то объём торгов (TOD тикер)
BYN_RUB
CHF_RUB
EUR_RUB
GBP_RUB
HKD_RUB
JPY_RUB
KZT_RUB
TRY_RUB
GBP_USD
KZT_USD
UAH_USD
USD_KZT
Есть мнение что по остальным парам вообще ничего не будет.
Вот и весь экономический смысл.
Для валютных графиков интерполяций придумано множество, многие очень изощрённо прячутся во всяких нейросетках и ген-алгоритмах. Но вот универсальных экстраполяционных моделей так и не придумали (и, кажется, даже доказуемо это невозможно).
Если есть время и желание чуть дальше «покопать», то можно попробовать взять минутные данные за день, представить каждую минуту некоторым средним значением курса с учетом волатильности за выбранный период инструмента и сгенерировать gif/video по набору результатов, фрейм по каждой минуте. Будет ли заметно некоторое уменьшение всех вариантов решений? Может будет некоторый узкий диапазон решений, которые наиболее часто происходят (устойчивый узкий диапазон)? И что в моменты существования сужения диапазона будет происходить с ценовой волатильностью участвующих инструментов (снижается)? Может какой-то из инструментов будет чаще через волатильность кросса (объемы?) определять вектор движения «абсолютной» цены? И может тогда смотреть за динамикой ценовых волатильностей, есть ли где взаимосвязь (минутные задержки), а не пытаться найти «абсолютную» цену валют?
Если хочется хардкора с поиском внутри минут — можно взять не средние значения цены, а сразу тики. К примеру, здесь: www.truefx.com/?page=downloads. Ну или у иных агрегаторов/брокеров/бирж, в сети при желании это все можно найти.
В целом, я думаю что какого-то «среднего» в соотношении пар стабильно вряд ли возможно найти, так, чтобы на этом где-то получить хорошее матожидание. Слишком сложная система получается, со слабопрогнозируемым и неустойчивым во времени результатом. Возможно где-то внутри коротких промежутков времени (1… 5 минут) и получится что-то разглядеть, но такое больше удел HFTшников и там гораздо сложнее инструментарий.
Торговля/цены сильно завязаны на объем торгов набора инструментов по каждой валюте (кроссы/опционы/фьючерсы/CFD/etc), объемы в свою очередь завязаны на время торгов («сезонность» внутри дня, недели, месяца, внутри периодов действия деривативов, год к году и прочее), периоды торгов со временем также изменяются (летние/зимние времена, праздники и прочее).
При отсутствии данных по совокупным объемам торгов на биржах можно брать в расчет величину ценовой волатильности по инструментам, но такое допущение работает только на ликвидных мировых инструментах и только в некоторые пиковые по объемам часы (не более 1-2 часа в сутки) торгов, когда нет пересечения по кроссам с рынками других регионов (биржи азии/европы/сша) и нет новостного «шторма».
С большой долей вероятности все стандартные подходы наподобие методики из статьи либо уже не работают, либо очень редко работают в определенные моменты (в основном — при росте волатильности, пробой цены при сильном и стабильном росте объемов торгов).
Избежать этого можно положив, например, usd + eur + chf + gbp = 1, но тогда метод оптимизации должен быть другой.
Избежать этого можно положив, например, usd + eur + chf + gbp = 1, но тогда метод оптимизации должен быть другой.
Можно оптимизировать через конечно-разностный метод с ограничениями или, например, вообще gradient-free.
Но вопрос вот в чем, является ли надежным предположением, что ценность валют вообще находится в рамках какой-то плотности вероятности, диапазона и т.д.
Интересно: если взять USD/JPY, курс будет уже на других порядках, так как йена миллионами исчисляется в быту. А если взять белорусский зайчик, там будут еще другие порядки. То есть, валюта зайчик вообще будет иметь ценность 0.000… от основных валют. А например Индекс доллара (взвешенный курс) гуляет около 100…
Как выше заметили, в вашем подходе абсолютные валюта определена с точностью до множителя, поэтому получается много решений. Исходя из здравого смысла я наложил дополнительное условие, чтобы валюта была как можно ближе к доллару. Вместо аналитического градиента я использовал встроенную функцию optim
. Код и результат такие:
obs_rate = c(
eurusd = 1.12012,
gbpusd = 1.30890,
eurchf = 1.14135,
eurgbp = 0.85570,
gbpchf = 1.33373,
usdchf = 1.01896,
usdusd = 1
)
loss = function(vec, rate){
sum((rate - c(
vec["eur"]/ vec["usd"],
vec["gbp"]/ vec["usd"],
vec["eur"]/ vec["chf"],
vec["eur"]/ vec["gbp"],
vec["gbp"]/ vec["chf"],
vec["usd"]/ vec["chf"],
vec["usd"])
)^2)
}
initial = c(eur = 1, usd = 1, gbp = 1, chf = 1)
set.seed(123)
res = optim(initial, loss, rate = obs_rate)
res$par
# eur usd gbp chf
# 1.1200905 0.9999775 1.3089043 0.9814043
Видно, что почти доллар и получается. Тут надо бы больше данных, чтобы было интереснее. Ну и дополнительные проверки на тестовой выборке с другими парами валют.
Спасибо! Я посмотрю.
Исходя из здравого смысла я наложил дополнительное условие, чтобы валюта была как можно ближе к доллару.
Это не совсем понял. Судя по вашему коду (который у меня тоже также отработал), вы говорите алгоритму, чтобы доллар был ближе к 1, что и есть вроде как единственное ограничение, да?
Получается, мы хотим, чтобы доллар был единицей всегда, а остальные курсы выражались через эту единицу (что будет выполняться, даже если в паре доллара нет)? И тогда в динамике доллар будет иметь константную ценность? Это разумно?
Получается, мы хотим, чтобы доллар был единицей всегда, а остальные курсы выражались через эту единицу (что будет выполняться, даже если в паре доллара нет)?
Да, наложено условие, чтобы обменный курс к доллару был максимально близок к единице. Насчёт разумности — имхо, ничем не хуже любого другого ограничения, но результат более понятный.
Дополнительное условие здесь нужно, чтобы можно было выбрать единственное решение из множества. На самом деле для результата даже не очень важно, какое именно это условие. Можно вместо этого положить, чтобы сумма всех курсов была равна 100. Тогда будет так:
obs_rate = c(
eurusd = 1.12012,
gbpusd = 1.30890,
eurchf = 1.14135,
eurgbp = 0.85570,
gbpchf = 1.33373,
usdchf = 1.01896
)
loss = function(vec, rate){
# eur + usd + gbp + chf = 100
# chf = 100 - eur - usd - gbp
chf = 100 - vec["eur"] - vec["usd"] - vec["gbp"]
sum((rate - c(
vec["eur"]/ vec["usd"],
vec["gbp"]/ vec["usd"],
vec["eur"]/ chf,
vec["eur"]/ vec["gbp"],
vec["gbp"]/ chf,
vec["usd"]/ chf
)
)^2)
}
initial = c(eur = 1, usd = 1, gbp = 1)
set.seed(123)
res = optim(initial, loss, rate = obs_rate)
res$par
# eur usd gbp
# 25.39684 22.67382 29.67731
# соотношение с прошлыми результатами для всех пар практически одинаковое
# > 25.39684/1.1200905
# [1] 22.67392
# > 22.67382/0.9999775
# [1] 22.67433
# > 29.67731/1.3089043
# [1] 22.6734
# > (100 - 25.39684 - 22.67382 - 29.67731)/0.9814043
# [1] 22.67366
Видно, что от прошлых курсов отличие в постоянном множителе.
А вот это
Да, наложено условие, чтобы обменный курс к доллару был максимально близок к единице
все таки не совсем понятно. Мне в коде читается так, что просто доллар = 1. А обменные курсы это пары, они восстанавливаются в оригинальном значении, но такими, чтобы везде usd = 1. То есть, ценность доллара единица, и это априорная гипотеза.
У нас же результат оптимизации — это курс некоей абсолютной валюты к известным валютам. vec["usd"]
— это курс доллара к абсолютной валюте. В функции потерь дополнительная компонента (1 - vec["usd"])^2
, которая задаёт условие, чтобы этот курс абсолютной валюты к доллару был максимально близок к единице, т. е. желательно, но не обязательно, чтобы они менялись один к одному. Или я что-то неправильно понимаю?
У нас же результат оптимизации — это курс некоей абсолютной валюты к известным валютам.
Можно сказать и так…
Но, вообще, мне ближе концепция, что нет никакой абсолютной валюты, а есть абсолютные курсы валют. Они измеряются в неизвестных попугаях, но эти попугаи для всех валют одинаковые. Допустим, это что-то типа золота (в период до реформы Бреттон-Вудской системы, кажется, так).
И тогда результат оптимизации — это абсолютные курсы валют (но лучше сказать, учитывающие все взаимоотношения валют в валютных парах). Так вот, приравнивая usd к 1 внутри лосс-функции мы будем всегда получать, что доллар почти ей и равен, а, учитывая вашу постановку задачи, если все валюты выражаются через Абсолютную валюту, то она и будет долларом. то есть все меряем через доллар?
Да, вот эти попугаи я и называю абсолютной валютой. Но сами-то попугаи, как вы понимаете, абсолютно произвольны. И это возвращает нас к множителю, который нам надо выбрать. Можно мерить в микропопугаях, а можно в килопопугаях. Ну а можно в долларах, суть от этого не изменится, это просто выбор единицы измерения.
Тогда все будет меняться, например, так:
В этом эксперименте оптимизация без ограничений но со слабенькой регуляризацией, а начальные значения валют положены равными 1 (пальцем в небо). Дальше они меняются от своих предыдущих значений. Получается warm-starts метод.
Полный код: github code
Если подключить исторические данные, то в качестве единицы измерения будет доллар в какой-то один момент времени времени. Все последующие курсы доллары уже не равны единице, а будут выражаться, допустим, через доллар на 2018-12-01.
Попробовал посчитать и от идеи привязки к курсу доллара за первый период пришлось отказаться — медленно сходится и сильно скачут значения курсов. Зато хорошо работает ограничение, чтобы в каждый период времени средний курс был равен единице. Этому условию соответсвует такая функция потерь:
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 +
(cadchf - cad / chf) ^ 2 +
(gbpcad - gbp / cad) ^ 2 +
(usdcad - usd / cad) ^ 2 +
(eurcad - eur / cad) ^ 2 +
(usd^2 + eur^2 + chf^2 + gbp^2 + cad^2) * alpha +
(1 - (usd + eur + chf + gbp + cad)/5)^2
)
Мой код тут, я опять использовал готовый оптимизатор: https://gist.github.com/gdemin/2f6e3c51383c66b1789e904185b75f40
Но на самом деле все равно остаётся некоторая проблема в постановке задачи — фактически у нас курсы в каждый период времени не связаны с предыдущими. И не очень понятно, как задать подобную связь времён. Наложение требования, чтобы средний курс был равен единице в каждый период времени хоть как-то увязывает периоды, но все равно выглядит несколько искусственным.
Я сделал так в приложенном коде, что курс связан через итерации. Первый раз можно привести курсы к любому желаемому значению. Например вашим оптимизатором. Далее используется последнее полученное значение. Это теплый старт. Начинаем с уже готового решения. Поэтому получается без скачков. Но и доп.условий нет никаких.
Я немножко помедитировал на условия этой задачи и оказалось, что она легко сводится к обычной линейной регрессии. Рассматривать будем случай из вашей статьи, без динамики. Для одной пары мы имеем: eurusd = eur / usd
. Можно взять логарифм от обоих частей, тогда получим log(eurusd) = log(eur) - log(usd)
. Далее это выражение можно представить таким образом: log(eurusd) = 1*log(eur) - 1*log(usd) + 0*log(gdp) + 0*log(chf)
. Это уже уравнение обычной линейной регрессии, где в роли коэффициентов выступают наши абсолютные курсы. Тут уже пора перейти к коду:
obs_rates = c(
eurusd = 1.12012,
gbpusd = 1.30890,
eurchf = 1.14135,
eurgbp = 0.85570,
gbpchf = 1.33373,
usdchf = 1.01896
)
l_obs_rates = log(obs_rates)
if(FALSE){
## псевдокод - не запускать
# l_* обозначает логарифм величины
l_eurusd = 1*l_eur - 1*l_usd + 0*l_gdp + 0*l_chf
l_gbpusd = 0*l_eur - 1*l_usd + 1*l_gdp + 0*l_chf
l_eurchf = 1*l_eur + 0*l_usd + 0*l_gdp - 1*l_chf
l_eurgbp = 1*l_eur + 0*l_usd - 1*l_gdp + 0*l_chf
l_gbpchf = 0*l_eur + 0*l_usd + 1*l_gdp - 1*l_chf
l_usdchf = 0*l_eur + 1*l_usd + 0*l_gdp - 1*l_chf
}
coef_matr = rbind(
c(1, - 1, + 0, + 0),
c(0, - 1, + 1, + 0),
c(1, + 0, + 0, - 1),
c(1, + 0, - 1, + 0),
c(0, + 0, + 1, - 1),
c(0, + 1, + 0, - 1)
)
colnames(coef_matr) = c("eur", "usd", "gdp", "chf")
regr_dat = data.frame(l_obs_rates, coef_matr)
# + 0 в уравнении регрессии, так как у нас нет свободного члена
res = lm(l_obs_rates ~ . + 0, data = regr_dat)
summary(res)
exp(coef(res))
# абсолютные курсы
# eur usd gdp chf
# 1.141333 1.018961 1.333749 NA
# погрешность
obs_rates - exp(predict(res))
# eurusd gbpusd eurchf eurgbp gbpchf usdchf
# 2.540224e-05 -3.079330e-05 1.744148e-05 -3.248295e-05 -1.925139e-05 -8.634867e-07
Используется классическая регрессия, однако очевидно, что применима вся мощь современных подходов с регуляризацией.
Швейцарский франк у нас выпал, так как является линейной комбинацией других валют. Его абсолютный курс легко рассчитать: chf = eur/eurchf
. Интересно, что он близок к единице. Я поэкспериментировал, используя в регрессии переменные в другом порядке и всегда получалось, что выпадающий курс практически равен 1.
Еще некоторые дополнительные замечания:
- У нас на четыре переменных шесть кейсов. Это совсем не круто и добавление истории эту проблему не решает. Её решит только добавление новых пар валют — кол-во переменных растет медленнее, чем кол-во пар.
- Для динамики все сложнее. Наивный способ, когда на каждом временном интервале своя регрессия, не очень хорош. Тут имеет смысл смотреть на иерархические регрессионные модели (например, пакет lme4).
> res$residuals
eurusd gbpusd eurchf eurgbp gbpchf usdchf
2.267840e-05 -2.352581e-05 1.528156e-05 -3.795996e-05 -1.443414e-05 -8.474192e-07
Да в принципе не должен получаться 100% фит. У нас шесть кейсов, три линейно-независимых переменных, тут однозначное решение может быть только в очень специальных случаях.
Ну, можно вытащить все возможные пары на один момент времени. Их около 90. Значит, будет 14 валют.
Может, пригодится. По списку валют вытягивает все их пары и конструирует матрицу:
currs <- c(
'usd',
'eur',
'chf',
'gbp',
'cad',
'jpy',
'rub',
'cny',
'aud'
)
## download and bind historic rates for pairs --------------
rate.env <- new.env()
for(i in seq_along(currs)) for(j in seq_along(currs)[-seq_len(i)]){
cat(currs[i], currs[j], "\n")
quantmod::getFX(
toupper(
paste0(currs[i]
, '/'
, currs[j]
)
)
, from = as.Date("2019-04-29")
, to = as.Date("2019-04-29")
, env = rate.env
)
}
obs_rates = unlist(as.list(rate.env))
l_obs_rates = log(obs_rates)
currs = toupper(currs)
coef_matr = do.call(cbind, lapply(currs, function(currency){
ifelse(grepl(paste0("^", currency), names(l_obs_rates)), 1,
ifelse(grepl(paste0(currency, "$"), names(l_obs_rates)), -1,
0
))
}))
colnames(coef_matr) = currs
regr_dat = data.frame(l_obs_rates, coef_matr)
res = lm(l_obs_rates ~ . + 0, data = regr_dat)
summary(res)
# clear environment
rm(list = ls()); gc()
## load libs
library(data.table)
library(ggplot2)
library(magrittr)
library(quantmod)
library(scales)
currs <- c(
'eur',
'chf',
'gbp',
'cad',
'jpy',
'rub',
'cny',
'cnh',
'aud',
'nok',
'sek',
'nzd',
'try',
'czk',
'xau',
'hkd',
'mxn',
'usd'
)
## download and bind historic rates for pairs --------------
rate.env <- new.env()
for(i in seq_along(currs)) for(j in seq_along(currs)[-seq_len(i)]){
cat(currs[i], currs[j], "\n")
quantmod::getFX(
toupper(
paste0(currs[i]
, '/'
, currs[j]
)
)
, from = as.Date("2019-04-29")
, to = as.Date("2019-04-29")
, env = rate.env
)
}
obs_rates = unlist(as.list(rate.env))
l_obs_rates = log(obs_rates)
currs = toupper(currs)
coef_matr = do.call(cbind, lapply(currs, function(currency){
ifelse(grepl(paste0("^", currency), names(l_obs_rates)), 1,
ifelse(grepl(paste0(currency, "$"), names(l_obs_rates)), -1,
0
))
}))
colnames(coef_matr) = currs
regr_dat = data.frame(l_obs_rates, coef_matr)
# lm
lm_mod = lm(l_obs_rates ~ . + 0, data = regr_dat)
summary(lm_mod)
lm_coefs <- exp(coef(lm_mod))
lm_resids <- exp(lm_mod$fitted.values) / obs_rates - 1
lm_resids[which.max(abs(lm_resids))]
head(sort(abs(lm_resids), decreasing = T))
plot(lm_resids)
# glm
glm_mod = glm(l_obs_rates ~ . + 0, data = regr_dat, family = gaussian(link = "identity"))
summary(glm_mod)
exp(coef(lm_mod))
glm_coefs <- exp(coef(glm_mod))
glm_resids <- exp(glm_mod$fitted.values) / obs_rates - 1
glm_resids[which.max(abs(glm_resids))]
plot(glm_resids)
Я на самом деле довольно далек от трейдинга, поэтому не совсем понимаю, как в данном контексте использовать корреляции. Чехии же в Евросоюзе — довольно логично, что крона жестко завязано с евро. Мне интересным показалось на вашем графике с остатками большая невязка предсказанного и реального курса золота к рублю. Можно ли из этого сделать вывод, что выгодно покупать золото за рубли и перепродавать за другую валюту?
Привет! Перечитывал тут старое. Корреляции могут дать повод для арбитражной торговли, когда приращения разошлись по знаку, если корреляция положительная. Делаются разнонаправленные позиции по обоим (парам) валютам.
Швейцарский франк у нас выпал, так как является линейной комбинацией других валют.
Можно подробнее, откуда взялось это утверждение? Оно же противоречит реальности.
У нас шесть пар курсов и четыре абсолютных курса. Зная любые три абсолютных курса, можно получить четвёртый абсолютный курс через исходные шесть парных курсов. Например, для швейцарского франка: абсолютный курс франка = абсолютный курс евро/курс евро к франку
.
Как я понимаю, необходимо было найти некий абсолютный курс, через который можно бы было выразить все остальные курсы.
Это и была задача исследования. А конечная цель (у автора оригинальной статьи) — применить метод портфельной оптимизации к абс.валютам.
Я бы размышлял следующим образом:
1. С точки зрения экономики абсолютный курс — есть универсальная мера стоимости любого блага, доступного для приобретения. Много экономистов сломали копья на тот счет, что именно может служить такой универсальной мерой. Например, рассматриваются такие эквиваленты стоимости как энергия, человеческий труд и пр. В настоящее время поиски этого экономического грааля продолжаются и существуют интересные теории стоимости с каноническим примером цены бутылки воды, превышающей цену бриллианта в условиях жажды в пустыне.
2. С математической точки зрения абсолютная мера любого товара или труда, да что там – любой величины — могла бы быть выведена как евклидово расстояние в ортогональном пространстве базисных координат. Ортогональность в таком случае будет означать, что энергию нельзя линейно выразить через труд, основные фонды и, например, землепользование.
3. Следуя предложенной идеологии и переходя к курсам валют, следует также определить некий ортогональный базис, но очевидно, что в текущей ситуации базис вырождается до одного измерения в виде американского доллара. Действительно, все базисные товары торгуются в этой валюте, а не какой либо другой. Другими словами все курсы валют коллинеарные единичному вектору в виде доллара. Действительно, взяв три пары валют:
eurusd 1.12012
gbpusd 1.3089
eurgbp 0.8557
Выразим стоимость gbpusd через eurusd и eurgbp. Получим значение 1.30901017, что очень близко к установленному 1.3089. Небольшую разницу как раз можно отнести к флуктуациям вокруг равновесия определения курсов, за счет которой живут трейдеры. Собственно в примере выше было именно это и показано.
В целом абсолютные курсы валют гипотетически будут иметь смысл в том случае, когда мировая торговля перейдет на торговлю экспортируемых товаров в локальных валютах. В этом случае каждая валюта может иметь абсолютный курс, который будет выражать меру спроса/предложения на объемы экспортируемых благ страны-экспортера.
Моя численная проверка гипотезы «Абсолютных курсов»