diff --git a/CHANGELOG.md b/CHANGELOG.md index 49009b2..50233da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Support for extended format for symbols. + +### Changed + +- `subscribe` and `unsubscribe` now pass symbols as is (as string or array of objects). + SubscriptionsManager can still handle a list of strings in Elixir, + and turn it into a comma-separated string for Twelve Data. + ## [0.2.1] - 2023-03-16 -## Fixed +### Fixed - Removed some unneeded logs. diff --git a/lib/ex_twelve_data/real_time_prices.ex b/lib/ex_twelve_data/real_time_prices.ex index 08f4ddb..797bc20 100644 --- a/lib/ex_twelve_data/real_time_prices.ex +++ b/lib/ex_twelve_data/real_time_prices.ex @@ -8,6 +8,15 @@ defmodule ExTwelveData.RealTimePrices do require Logger alias ExTwelveData.RealTimePrices.Handler + alias ExTwelveData.Symbol + + @typedoc """ + Symbols passed to subscribe/unsubscribe. + + It can either be an array of objects (extended format), + or a comma-delimited string with multiple symbols. + """ + @type symbols_list :: String.t() | [Symbol.t()] @type options :: [option] @@ -61,13 +70,13 @@ defmodule ExTwelveData.RealTimePrices do Subsequent calls will append new symbols to the list. See `unsubscribe/2` and `reset/1` to remove """ - @spec subscribe(pid, [String.t()]) :: {:error, any} | {:ok} + @spec subscribe(pid, symbols_list()) :: {:error, any} | {:ok} def subscribe(client, symbols) do msg = Jason.encode!(%{ action: "subscribe", params: %{ - symbols: Enum.join(symbols, ",") + symbols: symbols } }) @@ -80,13 +89,13 @@ defmodule ExTwelveData.RealTimePrices do Twelve Data will stop sending updates. """ - @spec unsubscribe(pid, [String.t()]) :: {:error, any} | {:ok} + @spec unsubscribe(pid, symbols_list()) :: {:error, any} | {:ok} def unsubscribe(client, symbols) do msg = Jason.encode!(%{ action: "unsubscribe", params: %{ - symbols: Enum.join(symbols, ",") + symbols: symbols } }) diff --git a/lib/ex_twelve_data/real_time_prices/subscriptions_manager.ex b/lib/ex_twelve_data/real_time_prices/subscriptions_manager.ex index 7385ab1..c2c97d1 100644 --- a/lib/ex_twelve_data/real_time_prices/subscriptions_manager.ex +++ b/lib/ex_twelve_data/real_time_prices/subscriptions_manager.ex @@ -25,7 +25,8 @@ defmodule ExTwelveData.RealTimePrices.SubscriptionsManager do GenServer.option() | {:pid, pid()} | {:provider, SubscriptionsManager.Provider} - | {:max_subscriptions, integer} + | {:max_subscriptions, integer()} + | {:symbols_extended, boolean()} # 1 event / 600ms -> 100 events per minute @clock_period_ms 600 @@ -39,11 +40,18 @@ defmodule ExTwelveData.RealTimePrices.SubscriptionsManager do pid = Keyword.fetch!(opts, :pid) provider = Keyword.fetch!(opts, :provider) max_subscriptions = Keyword.fetch!(opts, :max_subscriptions) + symbols_extended = Keyword.get(opts, :symbols_extended, false) schedule_next_message() {:ok, - %{tracked: MapSet.new(), pid: pid, provider: provider, max_subscriptions: max_subscriptions}} + %{ + tracked: MapSet.new(), + pid: pid, + provider: provider, + max_subscriptions: max_subscriptions, + symbols_extended: symbols_extended + }} end def handle_call(_msg, _from, state) do @@ -65,7 +73,8 @@ defmodule ExTwelveData.RealTimePrices.SubscriptionsManager do tracked: current, pid: pid, provider: provider, - max_subscriptions: max_subscriptions + max_subscriptions: max_subscriptions, + symbols_extended: symbols_extended } = state new = provider.get_symbols() @@ -76,11 +85,13 @@ defmodule ExTwelveData.RealTimePrices.SubscriptionsManager do state {:add, to_add, new_tracked} -> - RealTimePrices.subscribe(pid, MapSet.to_list(to_add)) + symbols = to_add |> MapSet.to_list() |> join_symbols_if_needed(symbols_extended) + RealTimePrices.subscribe(pid, symbols) %{state | tracked: new_tracked} {:remove, to_remove, new_tracked} -> - RealTimePrices.unsubscribe(pid, MapSet.to_list(to_remove)) + symbols = to_remove |> MapSet.to_list() |> join_symbols_if_needed(symbols_extended) + RealTimePrices.unsubscribe(pid, symbols) %{state | tracked: new_tracked} end @@ -88,6 +99,14 @@ defmodule ExTwelveData.RealTimePrices.SubscriptionsManager do {:noreply, new_state} end + defp join_symbols_if_needed(symbols_set, extended) do + if extended do + symbols_set + else + Enum.join(symbols_set, ",") + end + end + defp schedule_next_message do Process.send_after(self(), :clock, @clock_period_ms) end diff --git a/lib/ex_twelve_data/symbol.ex b/lib/ex_twelve_data/symbol.ex new file mode 100644 index 0000000..b1ad6e2 --- /dev/null +++ b/lib/ex_twelve_data/symbol.ex @@ -0,0 +1,24 @@ +defmodule ExTwelveData.Symbol do + @derive Jason.Encoder + defstruct [ + :exchange, + :mic_code, + :symbol, + :type + ] + + @typedoc """ + Supports: Stock, Index, ETF, REIT + """ + @type instrument_types :: String.t() + + @typedoc """ + Extended format for Twelve Data symbols. + """ + @type t :: %__MODULE__{ + exchange: String.t() | nil, + mic_code: String.t() | nil, + symbol: String.t(), + type: instrument_types + } +end