Skip to content

Commit

Permalink
Merge branch 'final-stretch'
Browse files Browse the repository at this point in the history
  • Loading branch information
Cinderella-Man committed Nov 2, 2023
2 parents 90cc96f + 0692774 commit 26a1145
Show file tree
Hide file tree
Showing 9 changed files with 45 additions and 32 deletions.
6 changes: 4 additions & 2 deletions 19-idiomatic-elixir.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,14 @@ As each worker will need to subscribe to the PubSub's `"TRADE_EVENTS:#{symbol}"`

Following the pattern established by the `Naive.Trader`, we use the module's attributes(with values based on the configuration) instead of hardcoded module names.

Additionally, we used the `Core.PubSub` module(and we will use other `core` module's structs down below) so we need to add the `core` application to the dependencies list of the `indicator` application:
Additionally, we've used the `Core.PubSub` and `Phoenix.PubSub`(indirectly) modules so we need to add them to the dependencies list of the `indicator` application:

```{r, engine = 'elixir', eval = FALSE}
# /apps/indicator/mix.exs
defp deps do
[
{:core, in_umbrella: true} # <= added
{:core, in_umbrella: true}, # <= added
{:phoenix_pubsub, "~> 2.0"} # <= added
...
```

Expand All @@ -98,6 +99,7 @@ As we subscribed to the PubSub, we need to provide a callback that will handle t
# add this at the top
alias Core.Struct.TradeEvent
def handle_info(%TradeEvent{} = trade_event, ohlc) do
{:noreply, Indicator.Ohlc.process(ohlc, trade_event)}
end
Expand Down
14 changes: 8 additions & 6 deletions 20-idiomatic-trading-strategy.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,6 @@ We need all the `positions` to iterate through them, deciding and executing appr

Additionally, we updated the `case` match to expect a list of updated positions which we will assign to the `Trader`'s state.

\newpage

Now we can modify the `Naive.Strategy` to handle multiple positions:

```{r, engine = 'elixir', eval = FALSE}
Expand All @@ -374,6 +372,8 @@ Now we can modify the `Naive.Strategy` to handle multiple positions:
end
```

\newpage

We need to write most of the functions used above, but we can already see the idea. We will map each of the decisions that we generate to async tasks that execute them. Next, we wait for all of them to finish and parse the results.

First, we are calling a new function `generate_decisions/4`, which is a recursive function on top of the existing `generate_decision/2`:
Expand Down Expand Up @@ -401,8 +401,6 @@ First, we are calling a new function `generate_decisions/4`, which is a recursiv

At this moment, the `generate_decisions/4` can look like overengineered `Enum.map/2` function, but we are actually preparing the ground for the consequent updates later in this chapter(to get the rest of the functionality running).

\newpage

It's important to note that we are now passing four arguments into the `generate_decision` function - we added `current_positions` and `settings` - those will be required in the further updates as it was mentioned above. At this moment though, we will update **all** the `generate_decision/2` clauses to include two additional arguments:

```{r, engine = 'elixir', eval = FALSE}
Expand All @@ -417,6 +415,8 @@ It's important to note that we are now passing four arguments into the `generate
) do
```

\newpage

Now back to the main `execute/3` function where we are calling `execute_decision/3`, which we need to update as well(**all** clauses):

```{r, engine = 'elixir', eval = FALSE}
Expand Down Expand Up @@ -584,8 +584,6 @@ We will start by modifying the `DynamicTraderSupervisor` where we will update th
end
```

\newpage

Now, the Trader will handle updating the settings, which we will add next, but before we do that, we should move the `update_status/2` function into the `Naive.Strategy` as it will be used from both the `DynamicTraderSupervisor` and the `Naive.Strategy`:

```{r, engine = 'elixir', eval = FALSE}
Expand Down Expand Up @@ -807,6 +805,8 @@ We will start by updating the `generate_decision/4` clause responsible for match

Now we need to deal with the `:rebuy` decision(previously, we removed the logic notifying the `Naive.Leader` about the rebuy being triggered).

\newpage

In case of rebuy decision we need to add a new position to the positions list which can be done by modifying the `generate_decisions/4` function:

```{r, engine = 'elixir', eval = FALSE}
Expand Down Expand Up @@ -851,6 +851,8 @@ The final step will be to update the `execute_decision/3` clause that matches th

We updated the whole function body as now it deals with initialising a new position instead of just flipping the `rebuy_triggered` flag inside the original position.

\newpage

We can now run the strategy to confirm that rebuy starts new positions:

```{r, engine = 'bash', eval = FALSE}
Expand Down
18 changes: 11 additions & 7 deletions 21-layers-of-abstraction.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,13 @@ As we are pointing to `Repo` instead of `Naive.Repo`, we need to add an alias at
alias Naive.Repo
```

[Note: At this moment, we will apply the above changes to the `Indicator.Ohlc.Worker` and `Indicator.Ohlc`(

`apps/indicator/lib/indicator/ohlc/worker.ex` and `apps/indicator/lib/indicator/ohlc.ex`) to avoid breaking the application when we will clean
up the config in the next step.]

\newpage

That finishes our conversion to hardcoded module names. As we are no longer basing our module on the configuration, we can remove all redundant configuration keys from the main `config.exs` file:

```{r, engine = 'elixir', eval = FALSE}
Expand Down Expand Up @@ -812,8 +819,6 @@ end

As we will stub dependencies using `mimic`, we need to `use` it inside the test module.

\newpage

We will be testing the primary entry function, the `Naive.Strategy.execute/3`, where we will first focus on placing a buy order scenario:

```{r, engine = 'elixir', eval = FALSE}
Expand All @@ -824,6 +829,8 @@ We will be testing the primary entry function, the `Naive.Strategy.execute/3`, w
end
```

\newpage

The simplest scenario will be to pass hardcoded settings, a fresh position(based on those settings) and a trade event(that will trigger the buy order):

```{r, engine = 'elixir', eval = FALSE}
Expand Down Expand Up @@ -903,8 +910,6 @@ ExUnit.start()

We removed all references to the `mox` module and replaced them with calls to the `Mimic.copy/1` function.

\newpage

We are now ready to run our new test:

```{r, engine = 'bash', eval = FALSE}
Expand Down Expand Up @@ -967,7 +972,6 @@ ExUnit provides a helper function for that case as well. We will modify our test
end)
assert log =~ "0.8"
...
```

In the above code, we wrapped our call to the `Naive.Strategy.execute/3` into an anonymous function that we passed as an argument to the `with_log/1` function. The `with_log/1` function returns a tuple containing the result of the passed function and the generated log message(s). We can then assert that the logged message contains the expected value, strengthening our test. We can now rerun our unit test:
Expand All @@ -993,8 +997,6 @@ We can't use the `mimic` package to swap the implementation as this is a "runnin

Please note that most of the applications won't need to change the implementation based on the environment as the 3rd party library/package itself will provide some flags to enable/disable the functionality(like sending emails to newly registered users).

\newpage

Let's get back to the `Naive.Strategy` module, where we will bring back the attribute-based `@binance_client`:

```{r, engine = 'elixir', eval = FALSE}
Expand All @@ -1014,6 +1016,8 @@ Let's get back to the `Naive.Strategy` module, where we will bring back the attr

We need to update all references to the Binance module's functions to use the `@binance_client` module's attribute.

\newpage

As we are already modifying the `Naive` application, we can follow up by removing the `@logger` and `@pubsub_client` module's attributes in the `Naive.Trader`:

```{r, engine = 'elixir', eval = FALSE}
Expand Down
14 changes: 8 additions & 6 deletions docs/idiomatic-otp.html
Original file line number Diff line number Diff line change
Expand Up @@ -543,20 +543,22 @@ <h2><span class="header-section-number">19.3</span> Initial implementation<a hre
<span id="cb421-22"><a href="idiomatic-otp.html#cb421-22" tabindex="-1"></a> <span class="fu">{</span><span class="va">:ok</span>, <span class="fu">{</span>symbol, duration<span class="fu">}}</span></span>
<span id="cb421-23"><a href="idiomatic-otp.html#cb421-23" tabindex="-1"></a> <span class="kw">end</span></span></code></pre></div>
<p>Following the pattern established by the <code>Naive.Trader</code>, we use the module’s attributes(with values based on the configuration) instead of hardcoded module names.</p>
<p>Additionally, we used the <code>Core.PubSub</code> module(and we will use other <code>core</code> module’s structs down below) so we need to add the <code>core</code> application to the dependencies list of the <code>indicator</code> application:</p>
<p>Additionally, we’ve used the <code>Core.PubSub</code> and <code>Phoenix.PubSub</code>(indirectly) modules so we need to add them to the dependencies list of the <code>indicator</code> application:</p>
<div class="sourceCode" id="cb422"><pre class="sourceCode elixir"><code class="sourceCode elixir"><span id="cb422-1"><a href="idiomatic-otp.html#cb422-1" tabindex="-1"></a><span class="co"># /apps/indicator/mix.exs</span></span>
<span id="cb422-2"><a href="idiomatic-otp.html#cb422-2" tabindex="-1"></a> <span class="kw">defp</span> deps <span class="kw">do</span></span>
<span id="cb422-3"><a href="idiomatic-otp.html#cb422-3" tabindex="-1"></a> <span class="ot">[</span></span>
<span id="cb422-4"><a href="idiomatic-otp.html#cb422-4" tabindex="-1"></a> <span class="fu">{</span><span class="va">:core</span>, <span class="va">in_umbrella:</span> <span class="cn">true</span><span class="fu">}</span> <span class="co"># &lt;= added</span></span>
<span id="cb422-5"><a href="idiomatic-otp.html#cb422-5" tabindex="-1"></a> <span class="op">...</span></span></code></pre></div>
<span id="cb422-4"><a href="idiomatic-otp.html#cb422-4" tabindex="-1"></a> <span class="fu">{</span><span class="va">:core</span>, <span class="va">in_umbrella:</span> <span class="cn">true</span><span class="fu">}</span>, <span class="co"># &lt;= added</span></span>
<span id="cb422-5"><a href="idiomatic-otp.html#cb422-5" tabindex="-1"></a> <span class="fu">{</span><span class="va">:phoenix_pubsub</span>, <span class="st">&quot;~&gt; 2.0&quot;</span><span class="fu">}</span> <span class="co"># &lt;= added</span></span>
<span id="cb422-6"><a href="idiomatic-otp.html#cb422-6" tabindex="-1"></a> <span class="op">...</span></span></code></pre></div>
<p>As we subscribed to the PubSub, we need to provide a callback that will handle the incoming trade events:</p>
<div class="sourceCode" id="cb423"><pre class="sourceCode elixir"><code class="sourceCode elixir"><span id="cb423-1"><a href="idiomatic-otp.html#cb423-1" tabindex="-1"></a><span class="co"># /apps/indicator/lib/indicator/ohlc/worker.ex</span></span>
<span id="cb423-2"><a href="idiomatic-otp.html#cb423-2" tabindex="-1"></a> <span class="co"># add this at the top</span></span>
<span id="cb423-3"><a href="idiomatic-otp.html#cb423-3" tabindex="-1"></a> <span class="im">alias</span> <span class="cn">Core</span><span class="op">.</span><span class="cn">Struct</span><span class="op">.</span><span class="cn">TradeEvent</span></span>
<span id="cb423-4"><a href="idiomatic-otp.html#cb423-4" tabindex="-1"></a></span>
<span id="cb423-5"><a href="idiomatic-otp.html#cb423-5" tabindex="-1"></a> <span class="kw">def</span> handle_info<span class="fu">(</span>%<span class="cn">TradeEvent</span><span class="fu">{}</span> <span class="op">=</span> trade_event, ohlc<span class="fu">)</span> <span class="kw">do</span></span>
<span id="cb423-6"><a href="idiomatic-otp.html#cb423-6" tabindex="-1"></a> <span class="fu">{</span><span class="va">:noreply</span>, <span class="cn">Indicator</span><span class="op">.</span><span class="cn">Ohlc</span><span class="op">.</span>process<span class="fu">(</span>ohlc, trade_event<span class="fu">)}</span></span>
<span id="cb423-7"><a href="idiomatic-otp.html#cb423-7" tabindex="-1"></a> <span class="kw">end</span></span></code></pre></div>
<span id="cb423-5"><a href="idiomatic-otp.html#cb423-5" tabindex="-1"></a></span>
<span id="cb423-6"><a href="idiomatic-otp.html#cb423-6" tabindex="-1"></a> <span class="kw">def</span> handle_info<span class="fu">(</span>%<span class="cn">TradeEvent</span><span class="fu">{}</span> <span class="op">=</span> trade_event, ohlc<span class="fu">)</span> <span class="kw">do</span></span>
<span id="cb423-7"><a href="idiomatic-otp.html#cb423-7" tabindex="-1"></a> <span class="fu">{</span><span class="va">:noreply</span>, <span class="cn">Indicator</span><span class="op">.</span><span class="cn">Ohlc</span><span class="op">.</span>process<span class="fu">(</span>ohlc, trade_event<span class="fu">)}</span></span>
<span id="cb423-8"><a href="idiomatic-otp.html#cb423-8" tabindex="-1"></a> <span class="kw">end</span></span></code></pre></div>
<p>To avoid mixing our business logic with the GenServer boilerplate(as discussed in the last chapter), we will place it in a new module. First, we need to create a new file <code>/apps/indicator/lib/indicator/ohlc.ex</code> and the <code>Indicator.Ohlc</code> module inside it:</p>
<div class="sourceCode" id="cb424"><pre class="sourceCode elixir"><code class="sourceCode elixir"><span id="cb424-1"><a href="idiomatic-otp.html#cb424-1" tabindex="-1"></a><span class="co"># /apps/indicator/lib/indicator/ohlc.ex</span></span>
<span id="cb424-2"><a href="idiomatic-otp.html#cb424-2" tabindex="-1"></a><span class="kw">defmodule</span> <span class="cn">Indicator</span><span class="op">.</span><span class="cn">Ohlc</span> <span class="kw">do</span></span>
Expand Down
Loading

0 comments on commit 26a1145

Please sign in to comment.