lib/xtb_client/messages/trade_transaction.ex

defmodule XtbClient.Messages.TradeTransaction do
  defmodule Command do
    alias XtbClient.Messages.{Operation, TradeType}

    @moduledoc """
    Info about command to trade the transaction.
    
    ## Parameters
    - `cmd` operation code, see `XtbClient.Messages.Operation`,
    - `customComment` the value the customer may provide in order to retrieve it later,
    - `expiration` pending order expiration time,
    - `offset` trailing offset,
    - `order` `0` or position number for closing/modifications,
    - `price` trade price,
    - `sl` stop loss,
    - `tp` take profit,
    - `symbol` trade symbol,
    - `type` trade transaction type, see `XtbClient.Messages.TradeType`,
    - `volume` trade volume.
    """

    @type t :: %__MODULE__{
            cmd: integer(),
            customComment: binary(),
            expiration: integer(),
            offset: integer(),
            order: integer(),
            price: float(),
            sl: float(),
            tp: float(),
            symbol: binary(),
            type: integer(),
            volume: float()
          }

    @derive Jason.Encoder
    defstruct cmd: nil,
              customComment: "",
              expiration: 0,
              offset: 0,
              order: 0,
              price: 0.0,
              sl: 0.0,
              tp: 0.0,
              symbol: "",
              type: nil,
              volume: 0.0

    def new(%{} = params) do
      params
      |> Enum.reduce(%__MODULE__{}, fn {key, value}, acc ->
        apply(__MODULE__, key, [acc, value])
      end)
    end

    def operation(%__MODULE__{} = params, operation) when is_atom(operation) do
      %{params | cmd: Operation.format(operation)}
    end

    def custom_comment(%__MODULE__{} = params, comment) when is_binary(comment) do
      %{params | customComment: comment}
    end

    def expiration(%__MODULE__{} = params, %DateTime{} = expiration) do
      %{params | expiration: DateTime.to_unix(expiration, :millisecond)}
    end

    def offset(%__MODULE__{} = params, offset) when is_integer(offset) do
      %{params | offset: offset}
    end

    def order(%__MODULE__{} = params, order) when is_integer(order) and order > 0 do
      %{params | order: order}
    end

    def price(%__MODULE__{} = params, price) when is_number(price) do
      %{params | price: price}
    end

    def stop_loss(%__MODULE__{} = params, sl) when is_number(sl) do
      %{params | sl: sl}
    end

    def take_profit(%__MODULE__{} = params, tp) when is_number(tp) do
      %{params | tp: tp}
    end

    def symbol(%__MODULE__{} = params, symbol) when is_binary(symbol) do
      %{params | symbol: symbol}
    end

    def type(%__MODULE__{} = params, type) when is_atom(type) do
      %{params | type: TradeType.format(type)}
    end

    def volume(%__MODULE__{} = params, volume) when is_number(volume) do
      %{params | volume: volume}
    end
  end

  @moduledoc """
  Info about realized trade transaction.
  
  ## Parameters
  - `order` holds info about order number, needed later for verification about order status.
  
  ## Handled Api methods
  - `tradeTransaction`
  """

  @type t :: %__MODULE__{
          order: integer()
        }

  @enforce_keys [:order]

  defstruct order: 0

  def new(%{"order" => order}) when is_integer(order) do
    %__MODULE__{
      order: order
    }
  end

  def match(method, data) when method in ["tradeTransaction"] do
    {:ok, __MODULE__.new(data)}
  end

  def match(_method, _data) do
    {:no_match}
  end
end