Skip to content

Commit 7fb100a

Browse files
authored
reviewing against franton code (#19)
* reviewing against franton code * reducing credo issues
1 parent 76eb176 commit 7fb100a

16 files changed

Lines changed: 252 additions & 79 deletions

File tree

apps/binance_mock/lib/binance_mock.ex

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,29 @@ defmodule BinanceMock do
77

88
alias Decimal
99
alias Core.Struct.TradeEvent
10+
alias Binance.Order
11+
alias Binance.OrderResponse
12+
alias Core.Struct.TradeEvent
13+
14+
@type symbol :: binary
15+
@type quantity :: binary
16+
@type price :: binary
17+
@type time_in_force :: binary
18+
@type timestamp :: non_neg_integer
19+
@type order_id :: non_neg_integer
20+
@type orig_client_order_id :: binary
21+
@type recv_window :: binary
22+
23+
@callback order_limit_buy(symbol, quantity, price, time_in_force) ::
24+
{:ok, %OrderResponse{}} | {:error, term}
25+
26+
@callback order_limit_sell(symbol, quantity, price, time_in_force) ::
27+
{:ok, %OrderResponse{}} | {:error, term}
28+
29+
@callback get_order(symbol, timestamp, order_id) ::
30+
{:ok, %Order{}} | {:error, term}
31+
32+
@callback get_exchange_info() :: {:ok, Binance.ExchangeInfo} | {:error, term}
1033

1134
require Logger
1235

@@ -51,6 +74,42 @@ defmodule BinanceMock do
5174
GenServer.call(__MODULE__, {:get_order, symbol, time, order_id})
5275
end
5376

77+
def generate_fake_order(order_id, symbol, quantity, price, side)
78+
when is_binary(symbol) and is_binary(quantity) and is_binary(price) and
79+
(side == "BUY" or side == "SELL") do
80+
current_timestamp = :os.system_time(:millisecond)
81+
client_order_id = :crypto.hash(:md5, "#{order_id}") |> Base.encode16()
82+
83+
Binance.Order.new(%{
84+
symbol: symbol,
85+
order_id: order_id,
86+
client_order_id: client_order_id,
87+
price: price,
88+
orig_qty: quantity,
89+
executed_qty: "0.00000000",
90+
cummulative_quote_qty: "0.00000000",
91+
status: "NEW",
92+
time_in_force: "GTC",
93+
type: "LIMIT",
94+
side: side,
95+
stop_price: "0.00000000",
96+
iceberg_qty: "0.00000000",
97+
time: current_timestamp,
98+
update_time: current_timestamp,
99+
is_working: true
100+
})
101+
end
102+
103+
def convert_order_to_order_response(%Binance.Order{} = order) do
104+
%{
105+
struct(
106+
Binance.OrderResponse,
107+
order |> Map.to_list()
108+
)
109+
| transact_time: order.time
110+
}
111+
end
112+
54113
def handle_cast(
55114
{:add_order, %Binance.Order{symbol: symbol} = order},
56115
%State{order_books: order_books, subscriptions: subscriptions} = state
@@ -104,7 +163,7 @@ defmodule BinanceMock do
104163
|> Enum.drop(length(filled_sell_orders))
105164

106165
order_books =
107-
Map.replace(order_books, :"#{trade_event.symbol}", %{
166+
Map.replace!(order_books, :"#{trade_event.symbol}", %{
108167
buy_side: remaining_buy_orders,
109168
sell_side: remaining_sell_orders,
110169
historical: filled_buy_orders ++ filled_sell_orders ++ order_book.historical
@@ -114,7 +173,10 @@ defmodule BinanceMock do
114173
end
115174

116175
defp order_limit(symbol, quantity, price, side) do
117-
%Binance.Order{} = fake_order = generate_fake_order(symbol, quantity, price, side)
176+
%Binance.Order{} =
177+
fake_order =
178+
generate_fake_order(GenServer.call(__MODULE__, :generate_id), symbol, quantity, price, side)
179+
118180
GenServer.cast(__MODULE__, {:add_order, fake_order})
119181
{:ok, convert_order_to_order_response(fake_order)}
120182
end
@@ -159,43 +221,6 @@ defmodule BinanceMock do
159221
Map.put(order_books, :"#{symbol}", order_book)
160222
end
161223

162-
defp generate_fake_order(symbol, quantity, price, side)
163-
when is_binary(symbol) and is_binary(quantity) and is_binary(price) and
164-
(side == "BUY" or side == "SELL") do
165-
current_timestamp = :os.system_time(:millisecond)
166-
order_id = GenServer.call(__MODULE__, :generate_id)
167-
client_order_id = :crypto.hash(:md5, "#{order_id}") |> Base.encode16()
168-
169-
Binance.Order.new(%{
170-
symbol: symbol,
171-
order_id: order_id,
172-
client_order_id: client_order_id,
173-
price: price,
174-
orig_qty: quantity,
175-
executed_qty: "0.00000000",
176-
cummulative_quote_qty: "0.00000000",
177-
status: "NEW",
178-
time_in_force: "GTC",
179-
type: "LIMIT",
180-
side: side,
181-
stop_price: "0.00000000",
182-
iceberg_qty: "0.00000000",
183-
time: current_timestamp,
184-
update_time: current_timestamp,
185-
is_working: true
186-
})
187-
end
188-
189-
defp convert_order_to_order_response(%Binance.Order{} = order) do
190-
%{
191-
struct(
192-
Binance.OrderResponse,
193-
order |> Map.to_list()
194-
)
195-
| transact_time: order.time
196-
}
197-
end
198-
199224
defp convert_order_to_event(%Binance.Order{} = order, time) do
200225
%TradeEvent{
201226
event_type: order.type,

apps/binance_mock/mix.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ defmodule BinanceMock.MixProject do
2727
defp deps do
2828
[
2929
{:binance, "~> 1.0"},
30-
{:decimal, "~> 2.0"},
31-
{:phoenix_pubsub, "~> 2.0"},
3230
{:core, in_umbrella: true},
33-
{:jason, "~> 1.2"}
31+
{:decimal, "~> 2.0"},
32+
{:jason, "~> 1.2"},
33+
{:phoenix_pubsub, "~> 2.0"}
3434
]
3535
end
3636
end

apps/core/lib/core/test.ex

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
defmodule Core.Test do
2+
@moduledoc """
3+
Module for mocking external dependies in which we do not have
4+
control of.
5+
"""
6+
7+
defmodule PubSub do
8+
@moduledoc """
9+
Pub sub mock
10+
"""
11+
12+
@type t :: atom
13+
@type topic :: binary
14+
@type message :: term
15+
16+
@callback subscribe(t, topic) :: :ok | {:error, term}
17+
@callback broadcast(t, topic, message) :: :ok | {:error, term}
18+
end
19+
20+
defmodule Logger do
21+
@moduledoc """
22+
Logger mock
23+
"""
24+
25+
@type message :: binary
26+
27+
@callback info(message) :: :ok
28+
end
29+
end

apps/data_warehouse/mix.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ defmodule DataWarehouse.MixProject do
2727
defp deps do
2828
[
2929
{:binance, "~> 1.0"},
30+
{:core, in_umbrella: true},
3031
{:ecto_sql, "~> 3.0"},
3132
{:ecto_enum, "~> 1.4"},
3233
{:phoenix_pubsub, "~> 2.0"},
33-
{:postgrex, ">= 0.0.0"},
34-
{:core, in_umbrella: true}
34+
{:postgrex, ">= 0.0.0"}
3535
]
3636
end
3737
end

apps/naive/lib/naive/leader.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ defmodule Naive.Leader do
77
alias Naive.Repo
88
alias Naive.Schema.Settings
99
alias Naive.Trader
10-
1110
require Logger
1211

12+
@type event_type :: atom
13+
@callback notify(event_type, %Trader.State{}) :: :ok
14+
1315
@binance_client Application.compile_env(:naive, :binance_client)
1416

1517
defmodule State do

apps/naive/lib/naive/trader.ex

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ defmodule Naive.Trader do
55

66
use GenServer, restart: :temporary
77

8-
require Logger
9-
108
alias Core.Struct.TradeEvent
9+
require Logger
1110

1211
@binance_client Application.compile_env(:naive, :binance_client)
12+
@leader Application.compile_env(:naive, :leader)
13+
@logger Application.compile_env(:core, :logger)
14+
@pubsub_client Application.compile_env(:core, :pubsub_client)
1315
defmodule State do
1416
@moduledoc """
1517
Trader State
@@ -47,9 +49,9 @@ defmodule Naive.Trader do
4749
def init(%State{id: id, symbol: symbol} = state) do
4850
symbol = String.upcase(symbol)
4951

50-
Logger.info("Initializing new trader(#{id}) for #{symbol}")
52+
@logger.info("Initializing new trader(#{id}) for #{symbol}")
5153

52-
Phoenix.PubSub.subscribe(Core.PubSub, "TRADE_EVENTS:#{symbol}")
54+
@pubsub_client.subscribe(Core.PubSub, "TRADE_EVENTS:#{symbol}")
5355

5456
{:ok, state}
5557
end
@@ -69,7 +71,7 @@ defmodule Naive.Trader do
6971
price = calculate_buy_price(price, buy_down_interval, tick_size)
7072
quantity = calculate_quantity(budget, price, step_size)
7173

72-
Logger.info(
74+
@logger.info(
7375
"The trader(#{id}) is placing a BUY order " <>
7476
"for #{symbol} @ #{price}, quantity: #{quantity}"
7577
)
@@ -80,7 +82,7 @@ defmodule Naive.Trader do
8082
:ok = broadcast_order(order)
8183

8284
new_state = %{state | buy_order: order}
83-
Naive.Leader.notify(:trader_state_updated, new_state)
85+
@leader.notify(:trader_state_updated, new_state)
8486

8587
{:noreply, new_state}
8688
end
@@ -125,7 +127,7 @@ defmodule Naive.Trader do
125127
if buy_order.status == "FILLED" do
126128
sell_price = calculate_sell_price(buy_price, profit_interval, tick_size)
127129

128-
Logger.info(
130+
@logger.info(
129131
"The trader(#{id}) is placing a SELL order for " <>
130132
"#{symbol} @ #{sell_price}, quantity: #{quantity}."
131133
)
@@ -137,12 +139,12 @@ defmodule Naive.Trader do
137139

138140
{:ok, %{state | buy_order: buy_order, sell_order: order}}
139141
else
140-
Logger.info("Trader's(#{id} #{symbol} buy order got partially filled")
142+
@logger.info("Trader's(#{id} #{symbol} buy order got partially filled")
141143

142144
{:ok, %{state | buy_order: buy_order}}
143145
end
144146

145-
Naive.Leader.notify(:trader_state_updated, new_state)
147+
@leader.notify(:trader_state_updated, new_state)
146148

147149
{:noreply, new_state}
148150
end
@@ -169,12 +171,12 @@ defmodule Naive.Trader do
169171
sell_order = %{sell_order | status: current_sell_order.status}
170172

171173
if sell_order.status == "FILLED" do
172-
Logger.info("Trader(#{id}) finished trade cycle for #{symbol}")
174+
@logger.info("Trader(#{id}) finished trade cycle for #{symbol}")
173175
{:stop, :normal, state}
174176
else
175-
Logger.info("Trader's(#{id} #{symbol} SELL order got partially filled")
177+
@logger.info("Trader's(#{id} #{symbol} SELL order got partially filled")
176178
new_state = %{state | sell_order: sell_order}
177-
Naive.Leader.notify(:trader_state_updated, new_state)
179+
@leader.notify(:trader_state_updated, new_state)
178180
{:noreply, new_state}
179181
end
180182
end
@@ -190,9 +192,9 @@ defmodule Naive.Trader do
190192
} = state
191193
) do
192194
if trigger_rebuy?(buy_price, current_price, rebuy_interval) do
193-
Logger.info("Rebuy triggered for #{symbol} by the trader(#{id})")
195+
@logger.info("Rebuy triggered for #{symbol} by the trader(#{id})")
194196
new_state = %{state | rebuy_notified: true}
195-
Naive.Leader.notify(:rebuy_triggered, new_state)
197+
@leader.notify(:rebuy_triggered, new_state)
196198
{:noreply, new_state}
197199
else
198200
{:noreply, state}
@@ -252,7 +254,7 @@ defmodule Naive.Trader do
252254
end
253255

254256
defp broadcast_order(%Binance.Order{} = order) do
255-
Phoenix.PubSub.broadcast(Core.PubSub, "ORDERS:#{order.symbol}", order)
257+
@pubsub_client.broadcast(Core.PubSub, "ORDERS:#{order.symbol}", order)
256258
end
257259

258260
defp convert_to_order(%Binance.OrderResponse{} = response) do

apps/naive/mix.exs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ defmodule Naive.MixProject do
1515
elixirc_options: [
1616
warnings_as_errors: true
1717
],
18-
aliases: aliases()
18+
aliases: aliases(),
19+
elixirc_paths: elixirc_paths(Mix.env())
20+
]
21+
end
22+
23+
defp aliases() do
24+
[
25+
seed: ["run priv/seed_settings.exs"]
1926
]
2027
end
2128

@@ -39,13 +46,11 @@ defmodule Naive.MixProject do
3946
{:ecto_enum, "~> 1.4"},
4047
{:phoenix_pubsub, "~> 2.0"},
4148
{:postgrex, ">= 0.0.0"},
42-
{:sobelow, "~> 0.13", only: [:dev, :test], runtime: false}
49+
{:sobelow, "~> 0.13", only: [:dev, :test], runtime: false},
50+
{:mox, "~> 1.0", only: [:test, :integration]}
4351
]
4452
end
4553

46-
defp aliases() do
47-
[
48-
seed: ["run priv/seed_settings.exs"]
49-
]
50-
end
54+
defp elixirc_paths(:test), do: ["test/support", "lib"]
55+
defp elixirc_paths(_), do: ["lib"]
5156
end

0 commit comments

Comments
 (0)