Packages
tai
0.0.42
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/advisor.ex
defmodule Tai.Advisor do
@moduledoc """
A behavior for implementing a server that receives events such as market quotes.
It can be used to receive one or more quote streams to record data and create, update or cancel orders.
"""
defmodule State do
@type group_id :: Tai.AdvisorGroup.id()
@type id :: atom
@type product :: Tai.Venues.Product.t()
@type config :: struct | map
@type run_store :: map
@type t :: %State{
group_id: group_id,
advisor_id: id,
products: [product],
config: config,
store: run_store,
trades: list
}
@enforce_keys ~w(advisor_id config group_id products store trades)a
defstruct ~w(advisor_id config group_id market_quotes products store trades)a
end
alias Tai.Markets.Quote
@type group_id :: Tai.AdvisorGroup.id()
@type id :: State.id()
@type advisor_name :: atom
@type event :: term
@type run_store :: State.run_store()
@type state :: State.t()
@callback after_start(state) :: {:ok, run_store}
@callback handle_event(event, state) :: {:ok, run_store}
@spec to_name(group_id, id) :: advisor_name
def to_name(group_id, advisor_id), do: :"advisor_#{group_id}_#{advisor_id}"
defmacro __using__(_) do
quote location: :keep do
use GenServer
@behaviour Tai.Advisor
def start_link(
group_id: group_id,
advisor_id: advisor_id,
products: products,
config: config,
store: store,
trades: trades
) do
name = Tai.Advisor.to_name(group_id, advisor_id)
market_quotes = %Tai.Advisors.MarketQuotes{data: %{}}
state = %State{
group_id: group_id,
advisor_id: advisor_id,
products: products,
market_quotes: market_quotes,
config: config,
store: store,
trades: trades
}
GenServer.start_link(__MODULE__, state, name: name)
end
def init(state), do: {:ok, state, {:continue, :started}}
def handle_info({:tai, %Quote{} = event}, state) do
key = {event.venue_id, event.product_symbol}
new_data = Map.put(state.market_quotes.data, key, event)
new_market_quotes = Map.put(state.market_quotes, :data, new_data)
new_state = Map.put(state, :market_quotes, new_market_quotes)
{
:noreply,
new_state,
{:continue, {:execute_event, event}}
}
end
def handle_continue(:started, state) do
{:ok, new_run_store} = after_start(state)
new_state = Map.put(state, :store, new_run_store)
state.products
|> Enum.each(&Tai.PubSub.subscribe({:market_quote, &1.venue_id, &1.symbol}))
{:noreply, new_state}
end
def handle_continue({:execute_event, event}, state) do
new_state =
try do
with {:ok, new_store} <- handle_event(event, state) do
Map.put(state, :store, new_store)
else
unhandled ->
%Tai.Events.AdvisorHandleEventInvalidReturn{
advisor_id: state.advisor_id,
group_id: state.group_id,
event: event,
return_value: unhandled
}
|> Tai.Events.warn()
state
end
rescue
e ->
%Tai.Events.AdvisorHandleEventError{
advisor_id: state.advisor_id,
group_id: state.group_id,
event: event,
error: e,
stacktrace: __STACKTRACE__
}
|> Tai.Events.warn()
state
end
{:noreply, new_state}
end
def after_start(state), do: {:ok, state.store}
def handle_event(_, state), do: {:ok, state.store}
defoverridable after_start: 1, handle_event: 2
end
end
end