Речь пойдёт о дефекте округления, который долгое время существовал на одной крипто-бирже из числа Top-10, и позволял зарабатывать сладкий 1 цент с каждого исполненного ордера. Может и сейчас существует. Я там больше не торгую и поэтому поделюсь информацией.
«Зачем тебе этот жалкий 1 цент?», спросите вы. Я считаю, что высокочастотный трейдер должен быть коварным и пользоваться любой возможностью обойти конкурентов. Даже небольшое преимущество превращается в заметные цифры при большом количестве сделок.
Пруфы
Кусочек отчёта из личного кабинета:
Куда смотреть-то?!
Приглядитесь внимательно к столбцам «Balance change» первой таблицы и «vol» второй. Объясняю: биржа снимала со счёта на 1 цент меньше, чем должна была снимать.
Цена: $5809.9902
Кол-во: 0.02500003 BTC
Объем: 5809.9902*0.02500003=145.2499293
Т.е. мы купили BTC на $145.2499293, что ближе к $145.25, показанным в логе робота, но биржа сняла за этот ордер $145.24. Кстати, я торговал с нулевой комиссией (см. столбец «Fee» в первой таблице) 😛
Как это работает
А теперь словами.
1. Начинаем мы с того, что вычисляем цену (desiredBidPx) и размер ордера (desiredBidQty) в соответствии со своей моделью котирования.
2. Округляем объём ордера вверх до ближайшего цента:
2.1. Вычисляем объём ордера: (desiredBidPx * desiredBidQty).
2.2. Делим его на шаг изменения кэша ($0.01), чтобы округлять с точностью до цента: (desiredBidPx * desiredBidQty) / PricingMath.CASH_STEP;
2.3. Округляем полученное число вверх (т.е. 14524.99293 превратится в 14525, или 10.3 превратится в 11): Math.ceil((desiredBidPx * desiredBidQty) / PricingMath.CASH_STEP);
2.4. Преобразуем объём обратно в центы, умножая на шаг изменения кэша: desiredBidVolume=PricingMath.CASH_STEP * Math.ceil((desiredBidPx * desiredBidQty) / PricingMath.CASH_STEP);
3. Итак, у нас объём ордера (desiredBidVolume), который является «правильным», и именно столько кэша должна взять за наш ордер биржа. А теперь мы скорректируем размер нашего ордера (desiredBidQty), да так, чтобы биржа ошиблась!
3.1. Делим желаемый размер ордера на цену, по которой хотим его выставить: (desiredBidVolume / desiredBidPx);
3.2. Делим на шаг изменения размера ордера (0.0000001 BTC), чтобы округлить размер ордера с правильным шагом: (desiredBidVolume / desiredBidPx) / PricingMath.QTY_STEP;
3.3. Округляем полученное число вниз (т.е. 10.7 превратятся в 10): Math.floor((desiredBidVolume / desiredBidPx) / PricingMath.QTY_STEP);
3.4. Вычитаем 1. Так надо. Дальше это превратится в 1 шаг по объёму: (Math.floor((desiredBidVolume / desiredBidPx) / PricingMath.QTY_STEP) — 1.0);
3.5. Умножаем на шаг изменения размера ордера: desiredBidQty = PricingMath.QTY_STEP * (Math.floor((desiredBidVolume / desiredBidPx) / PricingMath.QTY_STEP) — 1.0).
Готово!
Таким образом на лишний цент, который с нас не возьмёт биржа, мы купим чуточку сатоши. На моем скриншоте вы можете видеть, что робот выставлял ордера, чуть-чуть отличающиеся от 0.025: всего на 3, 100 и 112 сатоши.
Комментируем, подписываемся, репостим, угадываем криптобиржу! Активнее, товарищи 😀