FixAlchemy.Portfolio behaviour (FIXAlchemy v0.2.0)
View SourceBase for a per-session portfolio subscriber.
use FixAlchemy.Portfolio gives a GenServer that owns positions, orders,
and account state for one FIX session. It subscribes to the session's
execution, position, and reject message types on the FixAlchemy.Client
dispatch bus and processes each off the socket loop, so trading state and the
TCP session survive independently of any single message's decoding.
The defaults implement plain-FIX portfolio tracking: working orders from ExecutionReports, net-per-symbol positions from fills and PositionReports, and an account summary derived from the configured/discovered account id.
A broker adapter overrides the callbacks with @impl FixAlchemy.Portfolio to
add proprietary identity (e.g. a position id tag), collateral handling, or
extra message types, while reusing the get/broadcast machinery:
defmodule MyBroker.Portfolio do
use FixAlchemy.Portfolio
@impl FixAlchemy.Portfolio
def subscribed_types, do: super() ++ ["BA"]
@impl FixAlchemy.Portfolio
def handle_message("BA", raw, meta, state), do: apply_collateral(raw, state)
def handle_message(type, raw, meta, state), do: super(type, raw, meta, state)
endAdapter state
State the base does not model — a trade blotter, per-asset-class bookkeeping —
goes in the :extra map, read back with get_extra/2,3:
def handle_message("AE", raw, _meta, state) do
trade = raw |> FixAlchemy.Portfolio.decode(state) |> Map.delete(:raw)
update_in(state.extra, &Map.update(&1, :trades, [trade], fn ts -> [trade | ts] end))
end:extra is deliberately outside the change-detection that broadcasts positions,
orders, and the account summary, so holding state there costs nothing per
message. An adapter that wants its own updates published calls
FixAlchemy.Portfolio.broadcast/3.
Summary
Callbacks
Fold an ExecutionReport into the positions map.
Fold a PositionReport into the positions map.
Build the backend-neutral account summary from current state.
Handle a dispatched message, returning the new state.
Message types this portfolio subscribes to (default execution/position/reject).
Normalize the tracked orders map for broadcast.
Functions
Publish message on topic_prefix <> connection_id.
Decode a raw message into a field map using the session's dictionary.
Adapter-owned state the base does not model.
Net signed quantity from a position report's long and short quantities.
Parse a FIX numeric field into a float, defaulting to 0.0.
Callbacks
@callback apply_execution_report( positions :: map(), msg :: map(), order :: map(), order_id :: binary() | nil, state :: term() ) :: map()
Fold an ExecutionReport into the positions map.
Fold a PositionReport into the positions map.
Build the backend-neutral account summary from current state.
@callback handle_message( type :: binary(), raw :: binary(), meta :: map(), state :: term() ) :: term()
Handle a dispatched message, returning the new state.
@callback subscribed_types() :: [binary()]
Message types this portfolio subscribes to (default execution/position/reject).
Normalize the tracked orders map for broadcast.
Functions
Publish message on topic_prefix <> connection_id.
For adapter-owned updates the base does not detect; positions, orders, and the account summary are already broadcast on change. No-op without a PubSub module.
Decode a raw message into a field map using the session's dictionary.
@spec get_account(binary(), FixAlchemy.SessionConfig.name()) :: binary() | nil
@spec get_account_summary(binary(), FixAlchemy.SessionConfig.name()) :: map() | nil
@spec get_collateral(binary(), FixAlchemy.SessionConfig.name()) :: map() | nil
@spec get_extra(binary(), FixAlchemy.SessionConfig.name()) :: map()
Adapter-owned state the base does not model.
Returns the whole :extra map, or the value under key when given.
@spec get_extra(binary(), FixAlchemy.SessionConfig.name(), term()) :: term()
@spec get_order(binary(), FixAlchemy.SessionConfig.name(), binary()) :: map() | nil
@spec get_orders(binary(), FixAlchemy.SessionConfig.name()) :: map()
@spec get_position(binary(), FixAlchemy.SessionConfig.name(), binary()) :: map() | nil
@spec get_positions(binary(), FixAlchemy.SessionConfig.name()) :: map()
@spec get_positions_by_symbol(binary(), FixAlchemy.SessionConfig.name(), binary()) :: [map()]
Net signed quantity from a position report's long and short quantities.
Parse a FIX numeric field into a float, defaulting to 0.0.