Packages
tai
0.0.65
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()
@type advisor_spec :: Tai.Advisors.Spec.t()
@type terminate_reason :: :normal | :shutdown | {:shutdown, term} | term
@callback after_start(state) :: {:ok, run_store}
@callback on_terminate(terminate_reason, state) :: term
@callback handle_event(event, state) :: {:ok, run_store}
@spec process_name(group_id, id) :: advisor_name
def process_name(group_id, advisor_id), do: :"advisor_#{group_id}_#{advisor_id}"
@spec child_spec(advisor_spec) :: Supervisor.child_spec()
def child_spec(advisor_spec) do
run_store = advisor_spec.run_store || %{}
trades = advisor_spec.trades || []
name = process_name(advisor_spec.group_id, advisor_spec.advisor_id)
start_opts = [
group_id: advisor_spec.group_id,
advisor_id: advisor_spec.advisor_id,
products: advisor_spec.products,
config: advisor_spec.config,
store: run_store,
trades: trades
]
%{
id: name,
start: {advisor_spec.mod, :start_link, [start_opts]},
restart: advisor_spec.restart,
shutdown: advisor_spec.shutdown,
type: :worker
}
end
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.process_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
@impl true
def init(state) do
Process.flag(:trap_exit, true)
{:ok, state, {:continue, :started}}
end
@impl true
def terminate(reason, state) do
on_terminate(reason, state)
end
@impl true
def handle_info({:market_quote_store, :after_put, %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
@impl true
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.SystemBus.subscribe({:market_quote_store, {&1.venue_id, &1.symbol}}))
{:noreply, new_state}
end
@impl true
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
}
|> TaiEvents.warn()
state
end
rescue
e ->
%Tai.Events.AdvisorHandleEventError{
advisor_id: state.advisor_id,
group_id: state.group_id,
event: event,
error: e,
stacktrace: __STACKTRACE__
}
|> TaiEvents.warn()
state
end
{:noreply, new_state}
end
@impl true
def after_start(state), do: {:ok, state.store}
@impl true
def on_terminate(_, _), do: :ok
@impl true
def handle_event(_, state), do: {:ok, state.store}
defoverridable after_start: 1, on_terminate: 2, handle_event: 2
end
end
end