Несколько дней назад я решил купить биткойн. Затем я заметил, что он сильно колеблется, несмотря на общую тенденцию к росту. Хммм … если бы я просто купил в нужный момент и продал в другой нужный момент, я мог бы заставить деньги выпасть из пустоты!
С тех пор я потерял 5 долларов, играя, и получил 30 долларов, оставив его в покое. Очевидно, я отстой в этом …
Я знаю! Давайте сделаем бот, который делает это! Низкочастотный торговый бот, это звучит как веселье. И давайте напишем это на Хаскеле , просто чтобы все было интересно. Объединение самых строгих языков с самыми грязными ресурсами — Интернетом … что может пойти не так?
Первый заказ бизнеса — клиент REST.
Куратор добавил это изображение для развлечения.
REST клиент
Прежде чем мой бот сможет заниматься любой торговой и сложной алгоритмической торговлей , он должен поговорить с выбранным рынком. Я выбрал Bitstamp, потому что они единственные, кто позволяет мне делать это без банковского счета в США.
Написание REST-клиента на большинстве языков очень просто. Чтение тикера Bitstamp в Python выглядит следующим образом:
import requests, json r = requests.get("https://www.bitstamp.net/api/ticker") print json.loads(r.content) # prints: {u'volume': u'17179.28558844', u'last': u'159.49', u'bid': u'159.49', u'high': u'161.00', u'low': u'139.00', u'ask': u'159.64'}
Вот и все. Все, что вам нужно для набора значений, доступно в виде словаря.
В Хаскеле, хорошо в Хаскеле, выяснение того, как это сделать, заняло у меня всю ночь, затем немного утра и, наконец, полезный твит от незнакомца, чтобы рассказать мне, как я неправильно использовал монады.
Прежде всего, нам понадобится куча импорта. Те , кого мы действительно заботимся о том, являются HTTP-канал библиотеки и эсон парсер JSON строки. Все остальное есть, потому что… ну, на самом деле я не уверен, но это кажется необходимым, иначе вещи не работают.
{-# LANGUAGE OverloadedStrings #-} import Network.HTTP.Conduit import Control.Monad.IO.Class import Data.ByteString.Lazy import Data.Aeson import Data.Attoparsec.Number import Control.Applicative import Control.Monad.Trans
Я не совсем уверен, что делает бит OverloadedStrings в верхней части. Это своего рода директива компилятора, и большинство библиотек haskell, которые я нахожу в дикой природе, говорят мне, что я буду намного счастливее, если я включу его. Shrug.
Все, что требуется сейчас, это сделать HTTP-запрос и проанализировать ответ как JSON. Просто, правда?
Ну, Хаскелл строг, и ты не можешь просто разобрать все, что бы ни случилось. Мы должны сказать анализатору , что мы ожидаем, что мы хотим сделать с результатом и что типа это будет. Не может быть просто мешок с вещами! Нет, должен быть четко определенный пакет вещей.
data Ticker = Ticker { high :: Number, last :: Number, bid :: Number, volume :: Number, low :: Number, ask :: Number } deriving Show
Отлично. У нас есть тип тикера, который имеет несколько чисел и несколько имен. Эта часть шоу, похоже, говорит, что мы сможем распечатать это на консоли. Smashing!
Этого недостаточно, но пришло время для некоторых странных иероглифов, которые говорят Эсону, как именно работает синтаксический анализ.
instance FromJSON Ticker where parseJSON (Object v) = Ticker v .: "high" v .: "last" v .: "bid" v .: "volume" v .: "low" v .: "ask"
Если я правильно понимаю, эти странные символы являются аппликативными. :. Делает … что — то … и <$> и <*> сделать что — то еще. Все дело в том, как определить, как преобразовать ключ в строке JSON в значение типа Ticker . Думаю.
Хорошо, давайте создадим функцию, которая будет общаться с сервером и возвращать объект Ticker . Может быть. Если все пойдет хорошо.
ticker::(MonadIO m) > m (Maybe Ticker) ticker = get "ticker" >>= return . decode
Довольно простые вещи. Возьмите что-нибудь из Интернета, аккуратно завернутый в MonadIO , немного разверните его , вставьте в декодирование , которое волшебным образом использует все вещи, которые мы определили ранее, и оберните это обратно в MonadIO и Maybe. Парсинг может не сработать, ты знаешь.
get::(MonadIO m) => String > m ByteString get url = simpleHttp $ "https://www.bitstamp.net/api/"++url
Это обобщенная функция get, которая общается с Bitstamp с помощью функции simpleHttp из http-проводника . Это выглядит просто, но я уверен, что за кулисами происходит много волосатых вещей.
Чтобы убедиться, что все работает, мы запускаем его.
main = do ticker >>= print
Ничего.
Да, результат, который мы получаем, Ничего. Именно в этот момент вы понимаете, что кто-то неправильно использует JSON, и все эти числа на самом деле являются строками. Строки. Как, черт возьми, вы говорите Haskell, чтобы автоматически преобразовать их в числа перед тем, как поместить их в объект Ticker?
Грязный грязный интернет.
Но эй! Получил Haskell, чтобы поговорить с REST API . Как это круто!?