Подготовка: логарифмы, или почему бот складывает, а не умножает

Короткий урок с нуля для курса по арбитражу. Что логарифм делает с цепочкой курсов, почему сумма логарифмов заменяет произведение и как читать знак минус. Небольшой код на Python с выводом, который можно проверить вручную.

Вспомогательный урок к курсу "Собери своего арбитражного бота". В уроках с 5 по 8 курсы рассматриваются как граф и используется −log(rate). Эта страница объясняет почему, с самого начала.

Задача: цепочки перемножаются

Вы меняете доллары на евро по курсу 0.90, евро на фунты по 0.88, затем фунты обратно на доллары по 1.30. На каждом шаге ваши деньги умножаются. После трех шагов: 1 × 0.90 × 0.88 × 1.30 = 1.0296. Ваша прибыль составляет 2.96 процента.

Умножение подходит для трех шагов. Алгоритмы из уроков с 6 по 8 созданы для другой задачи: они складывают числа вдоль пути ("самый дешевый путь имеет наименьшую сумму"). Нужен прием, который превращает умножение в сложение.

Прием

Логарифм (записывается как log, здесь используется натуральный) является функцией с одним особым свойством:

log(a × b) = log(a) + log(b)

При умножении входных чисел результаты складываются. Нужны еще два факта:

  • log(1) = 0. Курс, равный ровно 1 (нет прибыли и нет убытка), превращается в 0.
  • Если число больше 1, его log положителен. Если оно меньше 1, его log отрицателен.

Проверьте это на цепочке выше:

import math
rates = [0.9, 0.88, 1.30]
prod = 1.0
for r in rates:
    prod *= r
print("product", round(prod, 4))
logs = [math.log(r) for r in rates]
print("logs", [round(x, 4) for x in logs])
print("sum of logs", round(sum(logs), 4), "-> exp", round(math.exp(sum(logs)), 4))
print("negative logs sum", round(-sum(logs), 4))

Вывод:

product 1.0296
logs [-0.1054, -0.1278, 0.2624]
sum of logs 0.0292 -> exp 1.0296
negative logs sum -0.0292

Сумма логарифмов равна 0.0292. Функция exp является обратной к log, и exp(0.0292) снова дает 1.0296, тот же ответ, что и произведение.

Почему нужен знак минус

Алгоритм поиска пути ищет наименьшую сумму. Нам нужен круг, который дает наибольшую прибыль. Прибыль означает произведение больше 1, а значит, сумму логарифмов больше 0. Чтобы превратить "наибольшее" в "наименьшее", меняем знак: используем −log(rate) как длину каждой дороги.

Теперь круг с общей длиной меньше 0 является прибыльным. В примере общая длина равна −0.0292, то есть она меньше 0. Круг прибыльный и это в точности отрицательный цикл из урока 6.

Малые числа, которые полезно помнить

  • log(2) равно 0.6931, а log(0.5) равно −0.6931: удвоение и деление пополам зеркальны.
  • При малых изменениях log(1 + x) близок к x. Прибыль 1 процента, log(1.01), равна 0.0100 (после округления). Поэтому малые значения логарифма читаются как проценты.

Ваше упражнение

  1. Запустите код. Измените один курс так, чтобы произведение стало меньше 1. Какой станет сумма логарифмов?
  2. Вручную: три сделки с курсами 1.02, 1.03 и 0.95. Сначала перемножьте курсы, затем сложите логарифмы на калькуляторе и сравните.
  3. Почему курс, равный ровно 1, не влияет на общую длину пути?

Далее: вспомогательный урок о графах (arbitrage-prereq-2-graphs).