FixAlchemy.Trading (FIXAlchemy v0.2.4)
View SourceGeneric FIX order-entry and account-read API.
Every function takes the session's FixAlchemy.Client pid, resolves its
{connection_id, session_name}, and either sends a FIX message through the
engine or reads state a subscriber (FixAlchemy.Portfolio /
FixAlchemy.MarketData) has accumulated. Outbound orders use named atom
fields resolved to tags through the connection's dictionary; only standard FIX
fields are built here.
Venues that require their own tags on an order pass them as :extra_fields, a
list of {tag, value} appended verbatim:
FixAlchemy.Trading.order(conn, "EUR/USD", 1000, :sell,
position_effect: "C",
extra_fields: [{9041, "74615492"}]
)This is the baseline :fix backend's order entry. Broker adapters reuse it and
supply their own position identity and close semantics on top.
Summary
Functions
Attach stop loss and/or take profit to an open position.
Place an entry order with attached stop loss and/or take profit.
Cancel a working order (35=F).
Close every position for a symbol with offsetting orders.
Close all positions for a symbol by sending offsetting orders.
Close a position by id with an offsetting order (position effect close).
The account this session's orders and account reads use.
Place a limit order (35=D, OrdType=2).
The accounts this session holds, in the order the venue first named them.
Amend a working order's price, stop price, or quantity (35=G).
Send a market or price-typed order (35=D).
Send a contingency order group (35=E NewOrderList).
Request a fresh CollateralInquiry (35=BB).
Fetch historical candles over the session (see FixAlchemy.MarketData).
Request the security list (35=x).
Request open orders (35=AF).
Request a position report (35=AN).
Select the account this session's orders and account reads use.
Place a stop order (35=D, OrdType=3).
Subscribe to streaming market data for instruments (35=V).
Unsubscribe from streaming market data for instruments (35=V).
Run fun with the account an order is to be sent for.
Functions
@spec attach_protection(GenServer.server(), map(), keyword()) :: :ok | {:error, :no_protection_given}
Attach stop loss and/or take profit to an open position.
Both given → an OCO pair; one given → a single closing order. Orders carry
PositionEffect close; a venue that identifies the position with a proprietary
tag passes it as position[:extra_fields].
@spec bracket_order(GenServer.server(), binary(), integer(), atom(), keyword()) :: :ok | {:error, :no_protection_given}
Place an entry order with attached stop loss and/or take profit.
Options
:stop_loss/:take_profit- protective prices:entry_type-:market(default),:limit,:stop,:stop_limit:price/:entry_stop- entry prices:tif- time in force (default:gtc):contingency- group type (default:els)
@spec cancel_order(GenServer.server(), binary()) :: :ok | {:error, :order_not_found}
Cancel a working order (35=F).
@spec close_all_for_symbol(GenServer.server(), binary(), keyword()) :: :ok
Close every position for a symbol with offsetting orders.
@spec close_position(GenServer.server(), binary(), integer(), keyword()) :: :ok
Close all positions for a symbol by sending offsetting orders.
@spec close_position_by_id(GenServer.server(), binary(), integer() | nil, keyword()) :: :ok | {:error, term()}
Close a position by id with an offsetting order (position effect close).
Sends a market order on the opposite side for the position's quantity. A broker adapter overrides this to use proprietary close semantics.
@spec get_account(GenServer.server()) :: binary() | nil
The account this session's orders and account reads use.
@spec get_account_summary(GenServer.server()) :: map() | nil
@spec get_capabilities(GenServer.server()) :: FixAlchemy.Capabilities.t()
@spec get_collateral(GenServer.server()) :: map() | nil
@spec get_instrument(GenServer.server(), binary()) :: map() | nil
@spec get_order(GenServer.server(), binary()) :: map() | nil
@spec get_orders(GenServer.server()) :: map()
@spec get_position(GenServer.server(), binary()) :: map() | nil
@spec get_positions(GenServer.server()) :: map()
@spec get_positions_by_symbol(GenServer.server(), binary()) :: [map()]
@spec limit_order( GenServer.server(), binary(), integer(), atom() | binary(), binary(), keyword() ) :: :ok | {:error, :account_unknown}
Place a limit order (35=D, OrdType=2).
@spec list_accounts(GenServer.server()) :: [binary()]
The accounts this session holds, in the order the venue first named them.
Any of them may be passed as set_active_account/2's account, or as an
order's :account option.
@spec list_instruments(GenServer.server()) :: [binary()]
@spec modify_order(GenServer.server(), binary(), keyword()) :: :ok | {:error, :order_not_found}
Amend a working order's price, stop price, or quantity (35=G).
@spec order(GenServer.server(), binary(), integer(), atom() | binary(), keyword()) :: :ok | {:error, :account_unknown}
Send a market or price-typed order (35=D).
Returns {:error, :account_unknown} if the account is not yet known — orders
are gated on account discovery.
@spec order_list(GenServer.server(), atom() | integer(), [map()]) :: :ok | {:error, :account_unknown}
Send a contingency order group (35=E NewOrderList).
contingency is :oco, :oto, :els, or a raw integer. Orders are maps
with :symbol, :quantity, :side, and optionally :type (default
:market), :price, :stop_price, :tif (default :gtc),
:position_effect, and :extra_fields. All members share one ClOrdLinkID.
@spec request_collateral(GenServer.server()) :: :ok
Request a fresh CollateralInquiry (35=BB).
@spec request_historical_candles(GenServer.server(), binary(), keyword()) :: {:ok, [map()]} | {:error, term()}
Fetch historical candles over the session (see FixAlchemy.MarketData).
@spec request_instruments(GenServer.server()) :: :ok
Request the security list (35=x).
@spec request_orders(GenServer.server()) :: :ok
Request open orders (35=AF).
@spec request_positions( GenServer.server(), keyword() ) :: :ok
Request a position report (35=AN).
@spec set_active_account(GenServer.server(), binary()) :: :ok | {:error, :unknown_account}
Select the account this session's orders and account reads use.
account must be one of list_accounts/1.
@spec stop_order( GenServer.server(), binary(), integer(), atom() | binary(), binary(), keyword() ) :: :ok | {:error, :account_unknown}
Place a stop order (35=D, OrdType=3).
@spec subscribe_market_data(GenServer.server(), binary() | [binary()]) :: :ok
Subscribe to streaming market data for instruments (35=V).
Each instrument is registered as a logon-persistent send on the engine
(FixAlchemy.Client.register_logon_send/3), so the MarketDataRequest is sent
once the session is logged in and re-sent on every reconnect — the retry lives
in the engine, not here.
@spec unsubscribe_market_data(GenServer.server(), binary() | [binary()]) :: :ok
Unsubscribe from streaming market data for instruments (35=V).
@spec with_account(GenServer.server(), keyword(), (binary() -> result)) :: result | {:error, :account_unknown} when result: term()
Run fun with the account an order is to be sent for.
The account is opts[:account] when the caller names one, else the account
selected with set_active_account/2, else the first the venue named, else the
:account the session was started with. Returns
{:error, :account_unknown} when none of those is known, which is how order
entry is gated on account discovery.