Skip to content

Commit

Permalink
WIP models
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholasbair committed Oct 3, 2024
1 parent 04625c4 commit 3e59682
Show file tree
Hide file tree
Showing 24 changed files with 941 additions and 56 deletions.
54 changes: 23 additions & 31 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,26 @@ interfaces:
description: "Get a list of accounts."
http_method: "GET"
path: "/accounts"
response_schema: "ListAccounts"
- function_name: "find"
description: "Fetch an account."
http_method: "GET"
path: "/accounts/:account_id"
response_schema: "FindAccount"
arguments:
- "account_id"
- function_name: "summary"
description: "Get a summary of an account."
http_method: "GET"
path: "/accounts/:account_id/summary"
response_schema: "AccountSummary"
arguments:
- "account_id"
- function_name: "list_instruments"
description: "Get a list of tradeable instruments for the given account."
http_method: "GET"
path: "/accounts/:account_id/instruments"
response_schema: "AccountInstruments"
arguments:
- "account_id"
parameters:
Expand All @@ -42,7 +46,7 @@ interfaces:
arguments:
- "account_id"
parameters:
- name: "sinceTransactionID"
- name: "since_transaction_id"
type: "string"
doc: "ID of the Transaction to get Account changes since."
- module_name: "Instruments"
Expand Down Expand Up @@ -77,19 +81,19 @@ interfaces:
type: "boolean"
doc: "A flag that controls whether the candlestick is smoothed or not."
default: false
- name: "includeFirst"
- name: "include_first"
type: "boolean"
doc: "A flag that controls whether the candlestick that is covered by the from time should be included in the results."
default: true
- name: "dailyAlignment"
- name: "daily_alignment"
type: "non_neg_integer"
doc: "The hour of the day (in the specified timezone) to use for granularities that have daily alignments, minimum 0, maximum 23."
default: 17
- name: "alignmentTimezone"
- name: "alignment_timezone"
type: "string"
doc: "The timezone to use for the dailyAlignment parameter, note that the returned times will still be represented in UTC."
default: "America/New_York"
- name: "weeklyAlignment"
- name: "weekly_alignment"
type: "string"
doc: "The day of the week used for granularities that have weekly alignment."
default: "Friday"
Expand Down Expand Up @@ -143,7 +147,7 @@ interfaces:
type: "non_neg_integer"
doc: "The maximum number of Orders to return."
default: 50
- name: "beforeID"
- name: "before_id"
type: "string"
doc: "The maximum Order ID to return. If not provided the most recent Orders in the Account are returned."
- function_name: "list_pending"
Expand Down Expand Up @@ -205,7 +209,7 @@ interfaces:
type: "non_neg_integer"
doc: "The maximum number of Trades to return."
default: 50
- name: "beforeID"
- name: "before_id"
type: "string"
doc: "The maximum Trade ID to return. If not provided the most recent Trades in the Account are returned."
- function_name: "list_open"
Expand Down Expand Up @@ -291,7 +295,7 @@ interfaces:
- name: "to"
type: "string"
doc: "The ending time (inclusive) of the time range for the Transactions being queried. Defaults to current time."
- name: "pageSize"
- name: "page_size"
type: "non_neg_integer"
doc: "The number of Transactions to include in each page. [default=100, maximum=1000]."
default: 100
Expand Down Expand Up @@ -342,7 +346,7 @@ interfaces:
arguments:
- "account_id"
parameters:
- name: "candleSpecifications"
- name: "candle_specifications"
type: "string"
doc: "The specification of the candles to fetch."
required: true
Expand All @@ -354,15 +358,15 @@ interfaces:
type: "boolean"
doc: "A flag that controls whether the candlestick is smoothed or not."
default: false
- name: "dailyAlignment"
- name: "daily_alignment"
type: "non_neg_integer"
doc: "The hour of the day (in the specified timezone) to use for granularities that have daily alignments, minimum 0, maximum 23."
default: 17
- name: "alignmentTimezone"
- name: "alignment_timezone"
type: "string"
doc: "The timezone to use for the dailyAlignment parameter, note that the returned times will still be represented in UTC."
default: "America/New_York"
- name: "weeklyAlignment"
- name: "weekly_alignment"
type: "string"
doc: "The day of the week used for granularities that have weekly alignment."
default: "Friday"
Expand All @@ -380,11 +384,11 @@ interfaces:
- name: "since"
type: "string"
doc: "The time of the snapshot to fetch using either RFC 3339 or Unix format. If not specified, then the most recent snapshot is fetched."
- name: "includeUnitsAvailable"
- name: "include_units_available"
type: "boolean"
doc: "Flag that enables the inclusion of the unitsAvailable field in the returned Price objects."
default: false
- name: "includeHomeConversion"
- name: "include_home_conversion"
type: "boolean"
doc: "Flag that enables the inclusion of the homeConversions field in the returned response."
default: false
Expand Down Expand Up @@ -418,35 +422,23 @@ interfaces:
type: "boolean"
doc: "A flag that controls whether the candlestick is smoothed or not."
default: false
- name: "includeFirst"
- name: "include_first"
type: "boolean"
doc: "A flag that controls whether the candlestick that is covered by the from time should be included in the results."
default: true
- name: "dailyAlignment"
- name: "daily_alignment"
type: "non_neg_integer"
doc: "The hour of the day (in the specified timezone) to use for granularities that have daily alignments, minimum 0, maximum 23."
default: 17
- name: "alignmentTimezone"
- name: "alignment_timezone"
type: "string"
doc: "The timezone to use for the dailyAlignment parameter, note that the returned times will still be represented in UTC."
default: "America/New_York"
- name: "weeklyAlignment"
- name: "weekly_alignment"
type: "string"
doc: "The day of the week used for granularities that have weekly alignment."
default: "Friday"
- name: "units"
type: "string"
doc: "The number of units used to calculate the volume-weighted average bid and ask prices."
default: 1


# schemas:
# - module_name: "Account"
# description: "Schema for Oanda account"
# properties:
# - name: "id"
# type: "string"
# description: "The account id"
# - name: "name"
# type: "string"
# description: "The account name"
default: 1
34 changes: 26 additions & 8 deletions lib/code_gen/code_generator.ex
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ defmodule ExOanda.CodeGenerator do
end

defp generate_function(config) do
%{function_name: name, description: desc, http_method: method, path: path, arguments: args, parameters: parameters} = config
%{function_name: name, description: desc, http_method: method, path: path, arguments: args, parameters: parameters, response_schema: response_schema} = config
formatted_args = format_args(args)
formatted_params = format_params(parameters)
arg_types = generate_arg_types(args)
model = generate_module_name(response_schema)

quote do
@doc"""
Expand Down Expand Up @@ -130,11 +131,11 @@ defmodule ExOanda.CodeGenerator do
path_params: path_params,
method: unquote(method),
headers: API.base_headers(),
params: params
params: ExOanda.CodeGenerator.convert_params(params)
)
|> API.maybe_attach_telemetry(conn)
|> Req.request(conn.options)
|> API.handle_response()
|> API.handle_response(unquote(model))

{:error, reason} ->
{:error, reason}
Expand Down Expand Up @@ -169,6 +170,19 @@ defmodule ExOanda.CodeGenerator do
|> String.to_atom()
end

@doc false
def convert_params(params) do
params
|> Enum.into(%{})
|> Recase.Enumerable.convert_keys(&Recase.to_camel/1)
|> Enum.map(fn {k, v} ->
case String.ends_with?(k, "Id") do
true -> {String.replace(k, "Id", "ID"), v}
false -> {k, v}
end
end)
end

defp generate_module_name(module_name), do: Module.concat([ExOanda, module_name])

defp format_args(args) do
Expand All @@ -177,11 +191,15 @@ defmodule ExOanda.CodeGenerator do

defp format_params(params) do
Enum.reduce(params, [], fn %{name: name, type: type, required: required, default: default, doc: doc}, acc ->
acc
|> Keyword.put(
String.to_atom(name),
[type: String.to_atom(type), required: required, default: default, doc: doc]
)
params_list = [
type: String.to_atom(type),
required: required,
default: default,
doc: doc
]

filtered_params = Enum.reject(params_list, fn {_, value} -> is_nil(value) end)
Keyword.put(acc, String.to_atom(name), filtered_params)
end)
end

Expand Down
3 changes: 2 additions & 1 deletion lib/code_gen/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ defmodule ExOanda.Config do
field(:http_method, :string)
field(:path, :string)
field(:arguments, {:array, :string}, default: [])
field(:response_schema, :string)

embeds_many :parameters, Parameters, primary_key: false do
field(:name, :string)
Expand Down Expand Up @@ -54,7 +55,7 @@ defmodule ExOanda.Config do

defp functions_changeset(struct, params) do
struct
|> cast(params, [:function_name, :description, :http_method, :path, :arguments])
|> cast(params, [:function_name, :description, :http_method, :path, :arguments, :response_schema])
|> validate_required([:function_name, :description, :http_method, :path])
|> cast_embed(:parameters, with: &embedded_changeset/2)
end
Expand Down
136 changes: 136 additions & 0 deletions lib/models/response/accounts/account_changes.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
defmodule ExOanda.AccountChanges do
@moduledoc """
Schema for Oanda account changes response.
"""

use TypedEctoSchema
import Ecto.Changeset
alias ExOanda.{
Order,
Position,
TradeSummary,
Transaction
}

@primary_key false

typed_embedded_schema do
embeds_many :changes, Change, primary_key: false do
embeds_many :orders_created, Order
embeds_many :orders_cancelled, Order
embeds_many :orders_filled, Order
embeds_many :orders_triggered, Order

embeds_many :trades_opened, TradeSummary
embeds_many :trades_reduced, TradeSummary
embeds_many :trades_closed, TradeSummary

embeds_many :positions, Position

embeds_many :transactions, Transaction
end

embeds_one :state, AccountState, primary_key: false do
field(:unrealized_pl, :float)
field(:nav, :float)
field(:margin_used, :float)
field(:margin_available, :float)
field(:position_value, :float)
field(:margin_closeout_unrealized_pl, :float)
field(:margin_closeout_nav, :float)
field(:margin_closeout_margin_used, :float)
field(:margin_closeout_percent, :float)
field(:margin_closeout_position_value, :float)
field(:withdrawal_limit, :float)
field(:margin_call_margin_used, :float)
field(:margin_call_percent, :float)
field(:balance, :float)
field(:pl, :float)
field(:resettable_pl, :float)
field(:financing, :float)
field(:commission, :float)
field(:dividend_adjustment, :float)
field(:guaranteed_execution_fees, :float)
field(:margin_call_enter_time, :utc_datetime_usec)
field(:margin_call_extension_count, :integer)
field(:last_margin_call_extension_time, :utc_datetime_usec)
field(:last_transaction_id, :string)

embeds_many :orders, OrderState, primary_key: false do
field(:id, :string)
field(:trailing_stop_value, :float)
field(:trigger_distance, :float)
field(:is_trigger_distance_exact, :boolean)
end

embeds_many :trades, TradeState, primary_key: false do
field(:id, :string)
field(:unrealized_pl, :float)
field(:margin_used, :float)
end

embeds_many :positions, PositionState, primary_key: false do
field(:instrument, :string)
field(:net_unrealized_pl, :float)
field(:long_unrealized_pl, :float)
field(:short_unrealized_pl, :float)
field(:margin_used, :float)
end
end

field(:last_transaction_id, :string)
end

@doc false
def changeset(struct, params) do
struct
|> cast(params, [:last_transaction_id])
|> cast_embed(:changes, with: &changes_changeset/2)
|> cast_embed(:state, with: &state_changeset/2)
end

defp changes_changeset(struct, params) do
struct
|> cast(params, [])
|> cast_embed(:orders_created)
|> cast_embed(:orders_cancelled)
|> cast_embed(:orders_filled)
|> cast_embed(:orders_triggered)
|> cast_embed(:trades_opened)
|> cast_embed(:trades_reduced)
|> cast_embed(:trades_closed)
|> cast_embed(:positions)
|> cast_embed(:transactions)
end

defp state_changeset(struct, params) do
struct
|> cast(params, [
:unrealized_pl, :nav, :margin_used, :margin_available, :position_value,
:margin_closeout_unrealized_pl, :margin_closeout_nav, :margin_closeout_margin_used,
:margin_closeout_percent, :margin_closeout_position_value, :withdrawal_limit,
:margin_call_margin_used, :margin_call_percent, :balance, :pl, :resettable_pl,
:financing, :commission, :dividend_adjustment, :guaranteed_execution_fees,
:margin_call_enter_time, :margin_call_extension_count, :last_margin_call_extension_time,
:last_transaction_id
])
|> cast_embed(:orders, with: &order_state_changeset/2)
|> cast_embed(:trades, with: &trade_state_changeset/2)
|> cast_embed(:positions, with: &position_state_changeset/2)
end

defp order_state_changeset(struct, params) do
struct
|> cast(params, [:id, :trailing_stop_value, :trigger_distance, :is_trigger_distance_exact])
end

defp trade_state_changeset(struct, params) do
struct
|> cast(params, [:id, :unrealized_pl, :margin_used])
end

defp position_state_changeset(struct, params) do
struct
|> cast(params, [:instrument, :net_unrealized_pl, :long_unrealized_pl, :short_unrealized_pl, :margin_used])
end
end
Loading

0 comments on commit 3e59682

Please sign in to comment.