defmodule Alpa do @moduledoc """ api wrapper for alpaca.markets """ @endpoint_paper Application.get_env(:alpa, :endpoint_paper) |> to_string() @endpoint_data Application.get_env(:alpa, :endpoint_data) |> to_string() require Jason @doc """ gets account info """ def account do get(@endpoint_paper,"/v2/account") end @doc """ places an order """ def place_order(symbol, qty, side, type, time_in_force, limit_price \\ nil, stop_price \\ nil) do body = Jason.encode!(%{ symbol: symbol, qty: qty, side: side, type: type, time_in_force: time_in_force, limit_price: limit_price, stop_price: stop_price}) post(@endpoint_paper, "/v2/orders", body) end @doc """ buy day market order wrapper """ def buy(symbol, qty) do place_order(symbol, qty, "buy", "market", "day") end @doc """ sell day market order wrapper """ def sell(symbol, qty) do place_order(symbol, qty, "sell", "market", "day") end @doc """ delete an order """ def delete_order(id) do delete(@endpoint_paper, "/v2/orders/#{id}") end @doc """ delete all orders """ def delete_all_orders do delete(@endpoint_paper, "/v2/orders") end @doc """ list assets """ def assets do get(@endpoint_paper, "/v2/assets") end @doc """ get asset by id or symbol """ def asset(id) do get(@endpoint_paper, "/v2/assets/#{id}") end @doc """ list open positions """ def positions do get(@endpoint_paper, "/v2/positions") end @doc """ get asset by id or symbol """ def position(id_or_symbol) do get(@endpoint_paper, "/v2/positions/#{id_or_symbol}") end @doc """ close all positions """ def close_positions do delete(@endpoint_paper, "/v2/positions") end @doc """ close a position by symbol """ def close_position(symbol) do delete(@endpoint_paper, "/v2/positions/$#{symbol}") end @doc """ list watchlists """ def watchlists do get(@endpoint_paper, "/v2/watchlists") end @doc """ get watchlists by id """ def watchlist(id) do get(@endpoint_paper, "/v2/watchlists/#{id}") end @doc """ delete a watchlist by id """ def delete_watchlist(id) do delete(@endpoint_paper, "/v2/watchlists/#{id}") end @doc """ create a watchlist """ def create_watchlist(name, symbols \\ []) do body = Jason.encode!(%{name: name, symbols: symbols}) post(@endpoint_paper, "/v2/watchlists", body) end @doc """ add an asset to a watchlist """ def watchlist_add_asset(id, symbol) do body = Jason.encode!(%{symbol: symbol}) post(@endpoint_paper, "/v2/watchlists/#{id}", body) end @doc """ gets id of watchlist by name returns: {:ok, id} on success {:error, reason} on failure """ def watchlist_id_by_name(name) do case watchlists() do {:ok, watchlists} -> result = watchlists |> Enum.find(fn w -> w["name"] == name end) case result do %{} -> {:ok, Map.fetch!(result, "id")} nil -> {:error, "No watchlist named #{name}"} end {:error, reason} -> reason end end @doc """ list bars paper money account, IEX data only (uses 'v1') https://docs.alpaca.markets/api-documentation/api-v2/market-data/bars/ Retrieves a list of bars for each requested symbol. It is guaranteed all bars are in ascending order by time. for alpaca api, timestamps are ISO 8601 ('2019-04-15T09:30:00-04:00') for Alpa, timestamps are Elixir DateTimes params: / timeframe : default = "1Min" One of minute, 1Min, 5Min, 15Min, day or 1D. minute is an alias of 1Min. Similarly, day is of 1D. symbols / (comma separated) limit : default = 100 (1000 max) start_time (cannot be used with :after) end_time (cannot be used with :until) after (cannot be used with :start) until (cannot be used with :until) note: :end, :after, :until -> :*_time due to Elixir keyword conflict returns Bars response (list of symbol with lists of bar objects) %{:ok, [ %{symbol: "AAPL", [ %{t: 1544129220, o: 172.26, h: 172.3, l: 172.16, c: 172.18, v: 3892, } ] } ] } t the beginning time of this bar as a Unix epoch in seconds o open price h high price l low price c close price v volume """ def bars( %{timeframe: timeframe, symbols: symbols, limit: limit, start_time: start_time, end_time: end_time, after_time: after_time, until_time: until_time}) do csv_symbols = Enum.join(symbols, ",") get(@endpoint_data,"/v1/bars/#{timeframe}?symbols=#{csv_symbols}&limit=#{limit}&start=#{start_time}&end=#{end_time}&after=#{after_time}&until=#{until_time}") end defp get(endpoint, path) do url = "#{endpoint}#{path}" IO.puts url HTTPoison.get(url, headers()) |> handle_response end defp post(endpoint, path, params) do url = "#{endpoint}#{path}" HTTPoison.post(url, params, headers()) |> handle_response end defp delete(endpoint, path) do url = "#{endpoint}#{path}" HTTPoison.delete(url, headers()) |> handle_response end defp handle_response(response) do case response do {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> {:ok, Jason.decode! body} {:ok, %HTTPoison.Response{status_code: 204}} -> # delete success {:ok, :success} {:ok, %HTTPoison.Response{status_code: 207}} -> # multipart success {:ok, :success} {:ok, %HTTPoison.Response{status_code: 403, body: body}} -> {:ok, Jason.decode!(body)} {:error, reason} -> {:error, reason} end end @doc """ auth headers for api """ def headers do [ "APCA-API-KEY-ID": Application.get_env(:alpa, :key), "APCA-API-SECRET-KEY": Application.get_env(:alpa, :secret) ] end end