Packages
tai
0.0.48
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 price points for a venue's order book
"""
use GenServer
alias __MODULE__
defmodule ChangeSet do
@type venue_id :: Tai.Venue.id()
@type product_symbol :: Tai.Venues.Product.symbol()
@type side :: :bid | :ask
@type price :: number
@type size :: number
@type upsert :: {:upsert, side, price, size}
@type delete :: {:delete, side, price}
@type change :: upsert | delete
@type t :: %ChangeSet{
venue: venue_id,
symbol: product_symbol,
changes: [change],
last_received_at: DateTime.t(),
last_venue_timestamp: DateTime.t() | nil
}
@enforce_keys ~w(venue symbol changes last_received_at)a
defstruct ~w(venue symbol changes last_received_at last_venue_timestamp)a
end
@type venue_id :: Tai.Venue.id()
@type product :: Tai.Venues.Product.t()
@type product_symbol :: Tai.Venues.Product.symbol()
@type t :: %OrderBook{
venue_id: venue_id,
product_symbol: product_symbol,
bids: %{(price :: number) => size :: pos_integer},
asks: %{(price :: number) => size :: pos_integer},
last_received_at: DateTime.t(),
last_venue_timestamp: DateTime.t() | nil
}
@enforce_keys ~w(
venue_id
product_symbol
bids
asks
)a
defstruct ~w(
venue_id
product_symbol
bids
asks
last_received_at
last_venue_timestamp
)a
@spec start_link(product) :: GenServer.on_start()
def start_link(product) do
name = to_name(product.venue_id, product.symbol)
state = %OrderBook{
venue_id: product.venue_id,
product_symbol: product.symbol,
bids: %{},
asks: %{}
}
GenServer.start_link(__MODULE__, state, name: name)
end
@spec to_name(venue_id, product_symbol) :: atom
def to_name(venue, symbol), do: :"#{__MODULE__}_#{venue}_#{symbol}"
@spec replace(ChangeSet.t()) :: :ok
def replace(%OrderBook.ChangeSet{} = change_set) do
change_set.venue
|> OrderBook.to_name(change_set.symbol)
|> GenServer.cast({:replace, change_set})
end
@spec apply(ChangeSet.t()) :: term
def apply(%ChangeSet{} = change_set) do
change_set.venue
|> OrderBook.to_name(change_set.symbol)
|> GenServer.cast({:apply, change_set})
end
def init(state), do: {:ok, state}
def handle_cast({:replace, %OrderBook.ChangeSet{} = change_set}, state) do
new_state =
%{
state
| bids: %{},
asks: %{},
last_received_at: change_set.last_received_at,
last_venue_timestamp: change_set.last_venue_timestamp
}
|> apply_changes(change_set.changes)
change_set.venue
|> Tai.Markets.ProcessQuote.to_name(change_set.symbol)
|> GenServer.cast({:order_book_snapshot, new_state, change_set})
{:noreply, new_state}
end
def handle_cast({:apply, change_set}, state) do
new_state =
%{
state
| last_received_at: change_set.last_received_at,
last_venue_timestamp: change_set.last_venue_timestamp
}
|> apply_changes(change_set.changes)
change_set.venue
|> Tai.Markets.ProcessQuote.to_name(change_set.symbol)
|> GenServer.cast({:order_book_apply, new_state, change_set})
{:noreply, new_state}
end
defp apply_changes(book, changes) do
changes
|> Enum.reduce(
book,
fn
{:upsert, :bid, price, size}, acc ->
new_bids = acc.bids |> Map.put(price, size)
Map.put(acc, :bids, new_bids)
{:upsert, :ask, price, size}, acc ->
new_bids = acc.asks |> Map.put(price, size)
Map.put(acc, :asks, new_bids)
{:delete, :bid, price}, acc ->
new_bids = acc.bids |> Map.delete(price)
Map.put(acc, :bids, new_bids)
{:delete, :ask, price}, acc ->
new_asks = acc.asks |> Map.delete(price)
Map.put(acc, :asks, new_asks)
end
)
end
end