Current section
Files
Jump to
Current section
Files
lib/peatio_client.ex
require Logger
defmodule PeatioClient do
use HTTPoison.Base
use GenServer
#############################################################################
### PEATIO Public API
#############################################################################
def ticker(market) do
body = build_api_request("/tickers/#{market}.json") |> gogogo!
ticker = body |> Map.get("ticker") |> Enum.reduce %{}, fn
({key, val}, acc) ->
key = key |> filter_key |> String.to_atom
val = val |> Decimal.new
Map.put(acc, key, val)
end
Map.put ticker, :at, body["at"]
end
def trades(market) do
body = build_api_request("/trades.json") |> set_payload([market: market]) |> gogogo!
body |> Enum.map fn
(trade) ->
%{
id: trade["id"],
at: trade["at"],
price: Decimal.new(trade["price"]),
volume: Decimal.new(trade["volume"]),
side: String.to_atom(trade["side"]),
funds: Decimal.new(trade["funds"])
}
end
end
#############################################################################
### PEATIO Private API
#############################################################################
def me(account) do
GenServer.call account_name(account), :members_me
end
def bid(account, market, orders) do
orders = orders |> Enum.map fn {p, v} -> {:bid, p, v} end
entry(account, market, orders)
end
def ask(account, market, orders) do
orders = orders |> Enum.map fn {p, v} -> {:ask, p, v} end
entry(account, market, orders)
end
def entry(account, market, orders) do
orders = orders |> Enum.map fn
({:ask, price, volume}) ->
%{price: price, side: :sell, volume: volume}
({:bid, price, volume}) ->
%{price: price, side: :buy, volume: volume}
end
GenServer.call account_name(account), {:orders_multi, market, orders}
end
def cancel(account, id) when is_integer(id) do
GenServer.call account_name(account), {:orders_cancel, id}
end
def cancel_all(account) do
GenServer.call account_name(account), {:orders_cancel, :all}
end
def cancel_ask(account) do
GenServer.call account_name(account), {:orders_cancel, :ask}
end
def cancel_bid(account) do
GenServer.call account_name(account), {:orders_cancel, :bid}
end
#############################################################################
### GenServer Callback
#############################################################################
def start_link(account, key, secret) do
opts = [name: account_name(account)]
GenServer.start_link(__MODULE__, %{key: key, secret: secret}, opts)
end
def init(auth) do
{:ok, %{auth: auth}}
end
def handle_call(:members_me, _, state = %{auth: auth}) do
body = build_api_request("/members/me")
|> sign_request(auth)
|> gogogo!
{:reply, body, state}
end
def handle_call({:orders_multi, market, orders}, _, state = %{auth: auth}) do
payload = [market: market]
payload = orders |> Enum.reduce payload, fn
(%{price: p, side: s, volume: v}, acc) ->
acc = acc ++ [{:"orders[][price]", p}]
acc = acc ++ [{:"orders[][volume]", v}]
acc ++ [{:"orders[][side]", s}]
end
body = build_api_request("/orders/multi", :post)
|> set_payload(payload)
|> sign_request(auth)
|> gogogo!
{:reply, body, state}
end
def handle_call({:orders_cancel, id}, _, state = %{auth: auth}) when is_integer(id) do
body = build_api_request("/order/delete", :post)
|> set_payload([id: id])
|> sign_request(auth)
|> gogogo!
{:reply, body, state}
end
def handle_call({:orders_cancel, side}, _, state = %{auth: auth}) do
payload = case side do
:ask -> [side: "sell"]
:bid -> [side: "buy"]
_ -> []
end
body = build_api_request("/orders/clear", :post)
|> set_payload(payload)
|> sign_request(auth)
|> gogogo!
{:reply, body, state}
end
#############################################################################
### HTTPoison Callback and Helper
#############################################################################
defp process_url(url) do
host = Application.get_env(:peatio_client, :host) || "https://app.peatio.com"
host <> url
end
defp process_response_body(body) do
body |> Poison.decode!
end
#############################################################################
### Helper and Private
#############################################################################
defp api_uri(path) do
"/api/v2" <> path
end
defp filter_key(key) do
case key do
"buy" -> "bid"
"sell" -> "ask"
_ -> key
end
end
def account_name(account) do
String.to_atom "#{account}.api.peatio.com"
end
defp build_api_request(path, verb \\ :get, tonce \\ nil) when verb == :get or verb == :post do
uri = api_uri(path)
tonce = tonce || :os.system_time(:milli_seconds)
%{uri: uri, tonce: tonce, verb: verb, payload: nil}
end
defp set_payload(req = %{payload: nil}, payload) do
%{req | payload: payload}
end
defp set_payload(req = %{payload: payload}, new_payload) when is_list(payload) do
%{req | payload: payload ++ new_payload}
end
# REF: https://app.peatio.com/documents/api_v2#!/members/GET_version_members_me_format
defp sign_request(req, %{key: key, secret: secret}) do
verb = req.verb |> Atom.to_string |> String.upcase
payload = (req.payload || [])
|> Dict.put(:access_key, key)
|> Dict.put(:tonce, req.tonce)
query = Enum.sort(payload) |> Enum.map_join("&", fn {k, v} -> "#{k}=#{v}" end)
to_sign = [verb, req.uri, query] |> Enum.join("|")
signature = :crypto.hmac(:sha256, secret, to_sign) |> Base.encode16 |> String.downcase
%{req | payload: Dict.put(payload, :signature, signature)}
end
defp gogogo!(%{uri: uri, verb: :get, payload: payload}) when is_list(payload) do
Logger.debug "GET #{uri} #{inspect payload}"
payload = payload |> Enum.map(fn({k, v}) -> "#{k}=#{v}" end) |> Enum.join("&")
get!(uri <> "?" <> payload).body
end
defp gogogo!(%{uri: uri, verb: :get, payload: _}) do
Logger.debug "GET #{uri}"
get!(uri).body
end
defp gogogo!(%{uri: uri, verb: :post, payload: payload}) do
Logger.debug "POST #{uri} #{inspect payload}"
post!(uri, {:form, payload}).body
end
end