Packages

Elixir client library for the Alpaca Trading API. Supports trading, market data, and account management.

Current section

Files

Jump to
alpa_ex lib alpa crypto trading.ex
Raw

lib/alpa/crypto/trading.ex

defmodule Alpa.Crypto.Trading do
@moduledoc """
Cryptocurrency trading operations for the Alpaca Trading API.
Alpaca supports commission-free crypto trading for select pairs.
## Supported Pairs
Common pairs include BTC/USD, ETH/USD, and others.
Use `Alpa.assets(asset_class: "crypto")` to get the full list.
## Notes
- Crypto trades 24/7
- Fractional trading is supported
- Order types: market, limit
- Time in force: gtc, ioc, fok
"""
alias Alpa.Client
alias Alpa.Models.{Asset, Order, Position}
@doc """
Get all available crypto assets.
## Examples
iex> Alpa.Crypto.Trading.assets()
{:ok, [%Alpa.Models.Asset{class: :crypto, symbol: "BTC/USD", ...}]}
"""
@spec assets(keyword()) :: {:ok, [Asset.t()]} | {:error, Alpa.Error.t()}
def assets(opts \\ []) do
params = Map.put(%{}, :asset_class, "crypto")
case Client.get("/v2/assets", Keyword.put(opts, :params, params)) do
{:ok, data} when is_list(data) -> {:ok, Enum.map(data, &Asset.from_map/1)}
{:error, _} = error -> error
end
end
@doc """
Get a specific crypto asset.
## Examples
iex> Alpa.Crypto.Trading.asset("BTC/USD")
{:ok, %Alpa.Models.Asset{...}}
"""
@spec asset(String.t(), keyword()) :: {:ok, Asset.t()} | {:error, Alpa.Error.t()}
def asset(symbol, opts \\ []) do
case Client.get("/v2/assets/#{URI.encode_www_form(symbol)}", opts) do
{:ok, data} -> {:ok, Asset.from_map(data)}
{:error, _} = error -> error
end
end
@doc """
Get all crypto positions.
## Examples
iex> Alpa.Crypto.Trading.positions()
{:ok, [%Alpa.Models.Position{...}]}
"""
@spec positions(keyword()) :: {:ok, [Position.t()]} | {:error, Alpa.Error.t()}
def positions(opts \\ []) do
case Client.get("/v2/positions", opts) do
{:ok, data} when is_list(data) ->
crypto_positions =
data
|> Enum.filter(fn p -> p["asset_class"] == "crypto" end)
|> Enum.map(&Position.from_map/1)
{:ok, crypto_positions}
{:ok, unexpected} ->
{:error, Alpa.Error.invalid_response(unexpected)}
{:error, _} = error ->
error
end
end
@doc """
Get a specific crypto position.
## Examples
iex> Alpa.Crypto.Trading.position("BTC/USD")
{:ok, %Alpa.Models.Position{...}}
"""
@spec position(String.t(), keyword()) :: {:ok, Position.t()} | {:error, Alpa.Error.t()}
def position(symbol, opts \\ []) do
case Client.get("/v2/positions/#{URI.encode_www_form(symbol)}", opts) do
{:ok, data} -> {:ok, Position.from_map(data)}
{:error, _} = error -> error
end
end
@doc """
Place a crypto order.
## Required Parameters
* `:symbol` - Crypto pair symbol (e.g., "BTC/USD")
* `:side` - "buy" or "sell"
* `:type` - "market" or "limit"
* `:time_in_force` - "gtc", "ioc", or "fok"
## Quantity (one required)
* `:qty` - Quantity in base currency (e.g., 0.5 for 0.5 BTC)
* `:notional` - Dollar amount to trade
## Optional Parameters
* `:limit_price` - Required for limit orders
* `:client_order_id` - Custom order ID
## Examples
# Buy $100 worth of Bitcoin
iex> Alpa.Crypto.Trading.place_order(
...> symbol: "BTC/USD",
...> notional: "100",
...> side: "buy",
...> type: "market",
...> time_in_force: "gtc"
...> )
{:ok, %Alpa.Models.Order{...}}
# Limit order to buy 0.1 BTC at $40,000
iex> Alpa.Crypto.Trading.place_order(
...> symbol: "BTC/USD",
...> qty: "0.1",
...> side: "buy",
...> type: "limit",
...> limit_price: "40000",
...> time_in_force: "gtc"
...> )
{:ok, %Alpa.Models.Order{...}}
"""
@spec place_order(keyword()) :: {:ok, Order.t()} | {:error, Alpa.Error.t()}
def place_order(params) when is_list(params) do
body =
params
|> Keyword.take([
:symbol,
:qty,
:notional,
:side,
:type,
:time_in_force,
:limit_price,
:client_order_id
])
|> Map.new()
case Client.post("/v2/orders", body, params) do
{:ok, data} -> {:ok, Order.from_map(data)}
{:error, _} = error -> error
end
end
@doc """
Buy crypto with a market order.
## Examples
# Buy 0.01 BTC
iex> Alpa.Crypto.Trading.buy("BTC/USD", "0.01")
{:ok, %Alpa.Models.Order{...}}
"""
@spec buy(String.t(), String.t(), keyword()) :: {:ok, Order.t()} | {:error, Alpa.Error.t()}
def buy(symbol, qty, opts \\ []) do
place_order(
Keyword.merge(opts,
symbol: symbol,
qty: qty,
side: "buy",
type: "market",
time_in_force: "gtc"
)
)
end
@doc """
Sell crypto with a market order.
## Examples
iex> Alpa.Crypto.Trading.sell("BTC/USD", "0.01")
{:ok, %Alpa.Models.Order{...}}
"""
@spec sell(String.t(), String.t(), keyword()) :: {:ok, Order.t()} | {:error, Alpa.Error.t()}
def sell(symbol, qty, opts \\ []) do
place_order(
Keyword.merge(opts,
symbol: symbol,
qty: qty,
side: "sell",
type: "market",
time_in_force: "gtc"
)
)
end
@doc """
Buy crypto by dollar amount.
## Examples
# Buy $50 worth of ETH
iex> Alpa.Crypto.Trading.buy_notional("ETH/USD", "50")
{:ok, %Alpa.Models.Order{...}}
"""
@spec buy_notional(String.t(), String.t(), keyword()) :: {:ok, Order.t()} | {:error, Alpa.Error.t()}
def buy_notional(symbol, amount, opts \\ []) do
place_order(
Keyword.merge(opts,
symbol: symbol,
notional: amount,
side: "buy",
type: "market",
time_in_force: "gtc"
)
)
end
@doc """
Sell crypto by dollar amount.
## Examples
iex> Alpa.Crypto.Trading.sell_notional("ETH/USD", "50")
{:ok, %Alpa.Models.Order{...}}
"""
@spec sell_notional(String.t(), String.t(), keyword()) :: {:ok, Order.t()} | {:error, Alpa.Error.t()}
def sell_notional(symbol, amount, opts \\ []) do
place_order(
Keyword.merge(opts,
symbol: symbol,
notional: amount,
side: "sell",
type: "market",
time_in_force: "gtc"
)
)
end
end