<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rosa Richter</title>
    <description>The latest articles on DEV Community by Rosa Richter (@cantido).</description>
    <link>https://dev.to/cantido</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F492416%2F841bc3d0-bccb-498c-ba33-21b93ccdda9a.jpeg</url>
      <title>DEV Community: Rosa Richter</title>
      <link>https://dev.to/cantido</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cantido"/>
    <language>en</language>
    <item>
      <title>Quick candlestick summaries with Elixir's Explorer</title>
      <dc:creator>Rosa Richter</dc:creator>
      <pubDate>Mon, 22 Aug 2022 20:03:09 +0000</pubDate>
      <link>https://dev.to/cantido/quick-candlestick-summaries-with-elixirs-explorer-332l</link>
      <guid>https://dev.to/cantido/quick-candlestick-summaries-with-elixirs-explorer-332l</guid>
      <description>&lt;p&gt;One of the main visualizations of any kind of financial market is the candlestick chart. In a candlestick chart, trades are summarized into time periods with that period's high price, low price, price at the beginning of the time period, and the price at the end of the time period. Here's an example, taken from &lt;a href="https://www.tradingview.com/"&gt;TradingView&lt;/a&gt;, a popular website that provides a lot of charts and tools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YU2lpov3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9wc8bga3pv1k9i6auoe5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YU2lpov3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9wc8bga3pv1k9i6auoe5.png" alt="Candlestick chart" width="880" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of the boxes represent the asset price at the beginning and ending of the time period, and the lines above and below indicate the maximum and minimum price over that period. It's a very succinct way of summarizing a lot of data.&lt;/p&gt;

&lt;p&gt;I have a toy project where I'm making a cryptocurrency exchange website, and I've been playing with the Elixir Nx project's &lt;a href="https://github.com/elixir-nx/explorer"&gt;Explorer&lt;/a&gt; library to manipulate this data. I found that you can summarize trade data into candlestick data extremely easily. This transformation is also very fast, because Explorer uses a Rust-based backend called &lt;a href="https://github.com/pola-rs/polars"&gt;Polars&lt;/a&gt; for the actual data manipulation.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/elixir-nx"&gt;
        elixir-nx
      &lt;/a&gt; / &lt;a href="https://github.com/elixir-nx/explorer"&gt;
        explorer
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Series (one-dimensional) and dataframes (two-dimensional) for fast data exploration in Elixir
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/elixir-nx/explorerexplorer.png"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hoxAe-pX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/elixir-nx/explorerexplorer.png" alt="Explorer"&gt;&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://hex.pm/packages/explorer" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/79e532dae6eb305e15d624103edfa3be5908ad1c94aafb1076450b39d673ce80/68747470733a2f2f696d672e736869656c64732e696f2f686578706d2f762f6578706c6f7265722e737667" alt="Package"&gt;&lt;/a&gt; &lt;a href="https://hexdocs.pm/explorer" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/05db0b150ed7f487f3ac9970adf9602e0a5d0a2394497beda38a98c433a88cfb/687474703a2f2f696d672e736869656c64732e696f2f62616467652f6865782e706d2d646f63732d677265656e2e7376673f7374796c653d666c6174" alt="Documentation"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer" href="https://github.com/elixir-nx/explorer/actions/workflows/ci.yml/badge.svg"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Do6GA6VU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/elixir-nx/explorer/actions/workflows/ci.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Explorer brings series (one-dimensional) and dataframes (two-dimensional) for fast data exploration to Elixir. Its high-level features are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Simply typed series: &lt;code&gt;:float&lt;/code&gt;, &lt;code&gt;:integer&lt;/code&gt;, &lt;code&gt;:boolean&lt;/code&gt;, &lt;code&gt;:string&lt;/code&gt;, &lt;code&gt;:date&lt;/code&gt;, and &lt;code&gt;:datetime&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A powerful but constrained and opinionated API, so you spend less time looking for the right
function and more time doing data manipulation.&lt;/li&gt;
&lt;li&gt;Pluggable backends, providing a uniform API whether you're working in-memory or (forthcoming) on
remote databases or even Spark dataframes.&lt;/li&gt;
&lt;li&gt;The first (and default) backend is based on NIF bindings to the blazing-fast
&lt;a href="https://docs.rs/polars" rel="nofollow"&gt;polars&lt;/a&gt; library.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The API is heavily influenced by &lt;a href="https://vita.had.co.nz/papers/tidy-data.pdf" rel="nofollow"&gt;Tidy Data&lt;/a&gt; and
borrows much of its design from &lt;a href="https://dplyr.tidyverse.org" rel="nofollow"&gt;dplyr&lt;/a&gt;. The philosophy is heavily
influenced by this passage from &lt;code&gt;dplyr&lt;/code&gt;'s documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;By constraining your options, it helps you think about your data manipulation challenges.&lt;/li&gt;
&lt;li&gt;It provides simple “verbs”, functions that correspond to the most common data manipulation
tasks, to help you translate…&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/elixir-nx/explorer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The raw data for my candlestick chart comes from the trades table in my database, which looks like this (with some extraneous fields omitted):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;price&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;quantity&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;executed_at&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;2022-08-22T12:47:17.123Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;99&lt;/td&gt;
&lt;td&gt;1000&lt;/td&gt;
&lt;td&gt;2022-08-22T12:47:17.456Z&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;101&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2022-08-22T12:47:17.789Z&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For simplicity, &lt;code&gt;price&lt;/code&gt; and &lt;code&gt;quantity&lt;/code&gt; are just integers. You can insert your decimal places as appropriate for a given currency.&lt;/p&gt;

&lt;p&gt;The main challenge in summarizing this data into candlesticks is to group it by time period. This is a place where you'll need to iterate through all the query results, which you'd need to do anyway in order to transform the &lt;code&gt;DateTime&lt;/code&gt;s from your Ecto query into &lt;code&gt;NaiveDateTime&lt;/code&gt;s, which are the only time type that Explorer understands. To group this data by time period, you have to find the beginning of your time period when given the trade's timestamp. If you are creating one-second-long candlesticks, you'll use the &lt;a href="https://hexdocs.pm/elixir/NaiveDateTime.html#truncate/2"&gt;&lt;code&gt;NaiveDateTime.truncate/2&lt;/code&gt;&lt;/a&gt; function for this. So you'll prepare your data for an &lt;code&gt;Explorer.DataFrame&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;   &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Trade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;     &lt;span class="n"&gt;naive_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_naive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;     &lt;span class="n"&gt;open_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;NaiveDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;naive_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;     &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:executed_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;naive_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;     &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:open_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;open_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;   &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;%{&lt;/span&gt;
    &lt;span class="ss"&gt;open_time:&lt;/span&gt; &lt;span class="sx"&gt;~N[2022-08-22 12:47:17.000]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;executed_at:&lt;/span&gt; &lt;span class="sx"&gt;~N[2022-08-22 12:47:17.123]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;price:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;quantity:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we can give this to &lt;code&gt;Explorer.DataFrame.new()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Explorer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DataFrame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#Explorer.DataFrame&amp;lt;&lt;/span&gt;
  &lt;span class="no"&gt;Polars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;executed_at&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;17.123000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;open_time&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;17.000000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Once we've got our data in a &lt;code&gt;DataFrame&lt;/code&gt;, it's trivial to do the actual candlestick summary. That's because Explorer data frames offer the &lt;a href="https://hexdocs.pm/explorer/Explorer.DataFrame.html#summarise/2"&gt;&lt;code&gt;Explorer.DataFrame.summarise/2&lt;/code&gt;&lt;/a&gt; function, which performs the exact operations we want. You'll just need to make sure the data is in the right order, and is grouped by the &lt;code&gt;open_time&lt;/code&gt; value we calculated earlier.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;iex&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Explorer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DataFrame&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;candles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;   &lt;span class="n"&gt;df&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;DataFrame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:executed_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;DataFrame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;group_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:open_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;...&amp;gt;&lt;/span&gt;   &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;DataFrame&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;summarise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;price:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:max&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:min&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="c1"&gt;#Explorer.DataFrame&amp;lt;&lt;/span&gt;
  &lt;span class="no"&gt;Polars&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;open_time&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2022&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;17.000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;price_first&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;price_last&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;price_max&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;price_min&lt;/span&gt; &lt;span class="n"&gt;integer&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;99&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now you have data for a candlestick chart! There is plenty of room for optimization here, like storing your trading timestamps as &lt;code&gt;NaiveDateTime&lt;/code&gt; structs so we don't need to do the conversion. You might be able to do this all within a database query, in fact, but I will argue that you can't beat the simplicity of &lt;code&gt;Explorer.DataFrame.summarise/2&lt;/code&gt;. Turning this data into an actual image is an exercise left for the reader.&lt;/p&gt;

&lt;p&gt;If you want to see an example of how an event-sourced cryptocurrency exchange might work, check out my &lt;code&gt;exchange&lt;/code&gt; project.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Cantido"&gt;
        Cantido
      &lt;/a&gt; / &lt;a href="https://github.com/Cantido/exchange"&gt;
        exchange
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A market application with a focus on cryptocurrencies
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Exchange&lt;/h1&gt;
&lt;p&gt;To start your Phoenix server:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install dependencies with &lt;code&gt;mix deps.get&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Create and migrate your database with &lt;code&gt;mix ecto.setup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Install Node.js dependencies with &lt;code&gt;npm install&lt;/code&gt; inside the &lt;code&gt;assets&lt;/code&gt; directory&lt;/li&gt;
&lt;li&gt;Start Phoenix endpoint with &lt;code&gt;mix phx.server&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now you can visit &lt;a href="http://localhost:4000" rel="nofollow"&gt;&lt;code&gt;localhost:4000&lt;/code&gt;&lt;/a&gt; from your browser.&lt;/p&gt;
&lt;p&gt;Ready to run in production? Please &lt;a href="https://hexdocs.pm/phoenix/deployment.html" rel="nofollow"&gt;check our deployment guides&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
Learn more&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Official website: &lt;a href="https://www.phoenixframework.org/" rel="nofollow"&gt;https://www.phoenixframework.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guides: &lt;a href="https://hexdocs.pm/phoenix/overview.html" rel="nofollow"&gt;https://hexdocs.pm/phoenix/overview.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://hexdocs.pm/phoenix" rel="nofollow"&gt;https://hexdocs.pm/phoenix&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Forum: &lt;a href="https://elixirforum.com/c/phoenix-forum" rel="nofollow"&gt;https://elixirforum.com/c/phoenix-forum&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/phoenixframework/phoenix"&gt;https://github.com/phoenixframework/phoenix&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/Cantido/exchange"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Phoenix LiveView, but event-sourced</title>
      <dc:creator>Rosa Richter</dc:creator>
      <pubDate>Sun, 16 Jan 2022 21:15:05 +0000</pubDate>
      <link>https://dev.to/cantido/phoenix-liveview-but-event-sourced-7pe</link>
      <guid>https://dev.to/cantido/phoenix-liveview-but-event-sourced-7pe</guid>
      <description>&lt;p&gt;Event sourcing is a powerful way to structure an application, and I've discovered that it pairs beautifully with Phoenix LiveView.&lt;/p&gt;

&lt;p&gt;The context: I'm building a cryptocurrency exchange application. I don't have the business chops to run an actual exchange, so this is just for fun. The application is built in &lt;a href="https://elixir-lang.org/"&gt;Elixir&lt;/a&gt;, using the &lt;a href="https://github.com/commanded/commanded"&gt;Commanded&lt;/a&gt; framework for CQRS/ES goodness, and &lt;a href="https://github.com/phoenixframework/phoenix_live_view"&gt;Phoenix LiveView&lt;/a&gt; because it's the hot new thing that I wanted to learn.&lt;/p&gt;

&lt;p&gt;My goal is to use LiveView to update a price chart as trades are executed by the system. A LiveView process is a lot like a GenServer, with a bespoke process for each client, executing &lt;code&gt;handle_*&lt;/code&gt; functions as the client does things. The first step to real-time chart updates is to trigger one of these handler functions in my LiveView controller when a trade is executed. I'm using Commanded's own &lt;a href="https://github.com/commanded/eventstore"&gt;EventStore&lt;/a&gt; library to dispatch and store my events, so their documentation is the place to start.&lt;/p&gt;

&lt;p&gt;In the EventStore documentation, I found that subscribing to the event stream is really simple: it's a single function call. Here's the example from &lt;a href="https://github.com/commanded/eventstore/blob/master/guides/Subscriptions.md#transient-subscriptions"&gt;EventStore's documentation on transient subscriptions&lt;/a&gt; that we care about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;EventStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RecordedEvent&lt;/span&gt;
&lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EventStore&lt;/span&gt;

&lt;span class="no"&gt;EventStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stream_uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;selector:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;
  &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;RecordedEvent&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;data:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# receive first batch of mapped event data&lt;/span&gt;
&lt;span class="k"&gt;receive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;RecordedEvent&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Received non nil event data: "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;All we need to do is call &lt;a href="https://hexdocs.pm/eventstore/EventStore.html#c:subscribe/2"&gt;&lt;code&gt;EventStore.subscribe/2&lt;/code&gt;&lt;/a&gt;, optionally with a selector function, and then the current process will start receiving events. We're going to call this in our LiveView's &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#c:mount/3"&gt;&lt;code&gt;mount/3&lt;/code&gt;&lt;/a&gt; callback. I'm also going to load the initial set of data here.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;ExchangeWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;DashboardLive&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;ExchangeWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:live_view&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;EventStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;RecordedEvent&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;Exchange&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;EventStore&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="ss"&gt;:ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EventStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"$all"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;selector:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;RecordedEvent&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;event_type:&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;data:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
          &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Elixir.Exchange.Orderbook.TradeExecuted"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt;
          &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"BTCUSDT"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;mapper:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;RecordedEvent&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;data:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Exchange&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Orderbooks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:ok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;trades:&lt;/span&gt; &lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;


  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;handle_info&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="ss"&gt;:events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;trades&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="no"&gt;Enum&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="no"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;&amp;amp;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:timestamp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:price&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:noreply&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;push_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"trades"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;%{&lt;/span&gt;&lt;span class="ss"&gt;trades:&lt;/span&gt; &lt;span class="n"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;})}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now, if you were using a server-side charting library like &lt;a href="https://contex-charts.org/"&gt;ContEx&lt;/a&gt;, then you would just append the new events to what you've already got assigned to the socket, and your normal rendering function would rebuild the chart. You're done! But I wanted to make it more complicated.&lt;/p&gt;

&lt;p&gt;I'm using &lt;a href="https://www.chartjs.org/"&gt;Chart.js&lt;/a&gt;, which is a popular graphing JavaScript library. It lives entirely on the client-side, which isn't very agreeable with Phoenix LiveView's server-side focus. Fortunately, LiveView allows you to set up JavaScript hooks, and then push events to them. We can make the client event-sourced, too! That's why I'm using &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#push_event/3"&gt;&lt;code&gt;push_event/3&lt;/code&gt;&lt;/a&gt; instead of &lt;a href="https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#assign/3"&gt;&lt;code&gt;assign/3&lt;/code&gt;&lt;/a&gt; in the example above. I'm using LiveView's JavaScript hooks to respond to events that I push from the LiveView process. Read more about &lt;a href="https://hexdocs.pm/phoenix_live_view/js-interop.html"&gt;LiveView JavaScript interoperability&lt;/a&gt;, it's really interesting.&lt;/p&gt;

&lt;p&gt;A LiveView client hook is an object containing a couple of functions.&lt;br&gt;
We're going to create a &lt;code&gt;mounted()&lt;/code&gt; function to initialize the chart with the data we already have. After that, we're going to set up an event handler. The &lt;code&gt;mounted()&lt;/code&gt; function receives a &lt;code&gt;this&lt;/code&gt; object with a few utilities, with the &lt;code&gt;handleEvent&lt;/code&gt; function being the most important to us. We call &lt;code&gt;this.handleEvent&lt;/code&gt; to set up the function that will handle the event that we pushed in our LiveView module's &lt;code&gt;handle_info&lt;/code&gt; callback.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Chart&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chart.js/auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;Hooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="nx"&gt;Hooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TradesChart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;mounted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;Configuration&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;your&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nothing&lt;/span&gt; &lt;span class="nx"&gt;interesting&lt;/span&gt; &lt;span class="nx"&gt;here&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;trades&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executed_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;executed_at&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                    &lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;liveSocket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;LiveSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/live&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Hooks&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will push new data into the chart without asking it to completely re-render.&lt;/p&gt;

&lt;p&gt;Lastly, we need to attach the hook to the element containing the chart. That's accomplished by adding a &lt;code&gt;phx-hook&lt;/code&gt; attribute to your markup element:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"trades-chart"&lt;/span&gt; &lt;span class="na"&gt;phx-hook=&lt;/span&gt;&lt;span class="s"&gt;"TradesChart"&lt;/span&gt; &lt;span class="na"&gt;width=&lt;/span&gt;&lt;span class="s"&gt;"400"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"200"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By adding that attribute, you've told Phoenix LiveView to call the &lt;code&gt;mounted()&lt;/code&gt; hook when the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element is mounted, which will then subscribe the update function to &lt;code&gt;"trade"&lt;/code&gt; events sent by the backend.&lt;/p&gt;

&lt;p&gt;All together, EventStore pushes events to our LiveView process, which pushes an event to our client hook, which puts the new data in the chart. Event sourcing is so cool!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m0UNjg0j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k21oiaoov6w7cjk0z8k3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m0UNjg0j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k21oiaoov6w7cjk0z8k3.gif" alt="Animation" width="880" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the whole project here:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/Cantido"&gt;
        Cantido
      &lt;/a&gt; / &lt;a href="https://github.com/Cantido/exchange"&gt;
        exchange
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A market application with a focus on cryptocurrencies
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>elixir</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
