Packages
tai
0.0.1
0.0.75
0.0.74
0.0.73
0.0.72
0.0.71
0.0.70
0.0.69
0.0.68
0.0.67
0.0.66
0.0.65
0.0.64
0.0.63
0.0.62
0.0.61
0.0.60
0.0.59
0.0.58
0.0.57
0.0.56
0.0.55
0.0.54
0.0.53
0.0.52
0.0.51
0.0.50
0.0.49
0.0.48
0.0.47
0.0.46
0.0.45
0.0.44
0.0.43
0.0.42
0.0.41
0.0.40
0.0.39
0.0.38
0.0.37
0.0.36
0.0.35
0.0.34
0.0.33
0.0.32
0.0.31
0.0.30
0.0.29
0.0.28
0.0.27
0.0.26
0.0.25
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
A composable, real time, market data and trade execution toolkit
Current section
Files
Jump to
Current section
Files
lib/tai/markets/order_book.ex
defmodule Tai.Markets.OrderBook do
@moduledoc """
Manage and query the state for an order book for a symbol on a feed
"""
use GenServer
def start_link(feed_id: feed_id, symbol: symbol) do
GenServer.start_link(
__MODULE__,
%{bids: %{}, asks: %{}},
name: to_name(feed_id: feed_id, symbol: symbol)
)
end
def init(state) do
{:ok, state}
end
def handle_call({:quotes, depth: depth}, _from, state) do
{
:reply,
{
:ok,
%{
bids: state |> ordered_bids |> take(depth),
asks: state |> ordered_asks |> take(depth)
}
},
state
}
end
def handle_call(:bid, _from, state) do
{:reply, {:ok, state |> ordered_bids |> List.first}, state}
end
def handle_call({:bids, depth}, _from, state) do
{:reply, {:ok, state |> ordered_bids |> take(depth)}, state}
end
def handle_call(:ask, _from, state) do
{:reply, {:ok, state |> ordered_asks |> List.first}, state}
end
def handle_call({:asks, depth}, _from, state) do
{:reply, {:ok, state |> ordered_asks |> take(depth)}, state}
end
def handle_call({:replace, replacement}, _from, _state) do
{:reply, :ok, replacement}
end
def handle_call({:update, %{bids: bids, asks: asks}}, _from, state) do
new_state = state
|> update_side(:bids, bids)
|> update_side(:asks, asks)
{:reply, :ok, new_state}
end
def quotes(name, depth \\ :all) do
GenServer.call(name, {:quotes, depth: depth})
end
def bid(name) do
GenServer.call(name, :bid)
end
def bids(name, depth \\ :all) do
GenServer.call(name, {:bids, depth})
end
def ask(name) do
GenServer.call(name, :ask)
end
def asks(name, depth \\ :all) do
GenServer.call(name, {:asks, depth})
end
def replace(name, %{bids: _b, asks: _a} = replacement) do
GenServer.call(name, {:replace, replacement})
end
def update(name, %{bids: _b, asks: _a} = changes) do
GenServer.call(name, {:update, changes})
end
@doc """
Returns an atom that will identify the process
## Examples
iex> Tai.Markets.OrderBook.to_name(feed_id: :my_test_feed, symbol: :btcusd)
Tai.Markets.OrderBook_my_test_feed_btcusd
"""
def to_name(feed_id: feed_id, symbol: symbol) do
:"#{__MODULE__}_#{feed_id}_#{symbol}"
end
defp ordered_bids(state) do
state.bids
|> Map.keys
|> Enum.sort
|> Enum.reverse
|> to_keyword_list(state.bids)
end
defp ordered_asks(state) do
state.asks
|> Map.keys
|> Enum.sort
|> to_keyword_list(state.asks)
end
defp to_keyword_list(prices, price_levels) do
prices
|> Enum.map(fn price ->
{size, processed_at, server_changed_at} = price_levels[price]
[price: price, size: size, processed_at: processed_at, server_changed_at: server_changed_at]
end)
end
defp take(list, :all), do: list
defp take(list, depth) do
list
|> Enum.take(depth)
end
defp update_side(state, side, price_levels) do
new_side = state[side]
|> Map.merge(price_levels)
|> Map.drop(price_levels |> drop_prices)
state
|> Map.put(side, new_side)
end
defp drop_prices(price_levels) do
price_levels
|> Enum.filter(fn {_price, {size, _processed_at, _server_changed_at}} -> size == 0 end)
|> Enum.map(fn {price, _} -> price end)
end
end