Building a Bitcoin price ticker
Tags: bitcoin, zio, scala, functional
Published: 2021-01-09 20:23:20

---
title: Building a Bitcoin price ticker
published: true
description: In this issue you'll build a BTC ticker that runs on your screen. This way you will always know when to buy and when to sell.
tags: bitcoin, zio, scala, functional, fp
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/ncb3nrz9j6gl0qgw66wk.jpg
 
---

> Learning functional programming
> 
> Advanced track

In this series I want to explore fundamental concepts and tools of functional programming by building small applications.

# Streams

{% youtube sXYceYCLUZw %}

A stream is a conceptual device that abstracts the way in wich we access and process data. A stream at its core is just a sequence of data. However, differently from an array or a list, a stream doesn't necessarily have a beginning or an end.

In real life we have some example of streams. A ticker for the price of Bitcoin for example. It's a stream because it's clearly a timed sequence of data. It does have a beginning (when Bitcoin was created) but it never ends.

## Building a Bitcoin price ticker

In this issue you'll build a BTC ticker that runs on your screen. This way you will always know when to buy and when to sell.

### Design

<div style="margin: 20px"><img alt="Design draft for the BTC ticker" src="./img/design-draft-for-the-bitcoin-ticker.png" height="300"></div>

A simple design you can draft in two seconds is the one sketched above. We'll chose some API, parse the output, turn it into a number and finally print it on our screen (or physical ticker if you are retro).

![Gomez Addams inspecting a stock ticker](./img/gomez-examining-a-stock-ticker.png)

### Programming

A very first draft of the program might be like this:

```scala
ZStream
  .repeat("100.000") // generate some fake data
  .take(3) // let's not overload
  .map(identity) // placeholder for parsing
  .map(priceString => BigDecimal(priceString))
  .foreach(price =>
    putStrLn(
      s"BTC-EUR: ${price.setScale(2, BigDecimal.RoundingMode.UP)}"
    )
  )
```

You can go ahead and [launch this](https://scastie.scala-lang.org/hwQEk38eTOCfYYrEjhne2Q). In the first line we are getting a hold of the `ZStream` object, it contains the `repeat` method wich just infinitely produces whatever we pass: a string in this case. We limit it to 3 strings, otherwise it will just overload our machine and never stop. The parsing for now is impersonated by an identity function. We then leverage the `BigDecimal` parsing capabilities to have a number to work with. Lastly the `foreach` method is used, which is a terminal method. It will wrap up the stream by printing on the screen a well formatted price tick.

We could have even skipped the conversion to `BigDecimal` and back if we don't care about having control on rounding.

```scala
ZStream
  .repeat("100.000")
  .take(3)
  .map(identity)
  .foreach(price => putStrLn(s"BTC-EUR: $price"))
```
Now we go hunting for a good API. Like in the cooking TV show I’ve already done this, but you can look for yourself and I recommend [ProgrammableWeb](https://www.programmableweb.com/).

I’m going to use the [Kraken public API](https://api.kraken.com/0/public/Ticker?pair=BTCEUR) which is very basic. It returns a json of this form:

```json
{
    "error": [],
    "result": {
        "XXBTZEUR": {
            ... other stuff
            "c": "last trade price",
            ... other stuff
        }
    }
```
We are interested in the current price which is the last trade price. We could (and should) use a json parser to extract this. However that would open a can of worms, so I’m going to use a regular expression:
<!-- aggiungere nota sulla regex-->

```scala
def parsePrice(json: String): String = {
  val regex = raw"""(?s).*"c":\["([0-9.]+).*""".r
  json match {
    case regex(price) => price
  }
}
```
So we have our parsing step, we finally need to actually call the API. Again, there are super-cool http clients we could use; instead for simplicity's sake we'll use the scala built-in client.

```scala
val tickerURLCall: Task[String] = IO(
  io.Source
    .fromURL("https://api.kraken.com/0/public/Ticker?pair=BTCEUR")
    .mkString
)
```
We can now put everything together in the program. We will use `tickerURLCall` instead of the fake data and the `priceParce` instead of the identity.

```scala
  val program: ZIO[Console with Clock, Throwable, Unit] = ZStream
    .repeatEffect(tickerURLCall)
    .throttleShape(1, 5.seconds)(_ => 1) // let's slow down
    .map(parsePrice)
    .foreach(price =>
      putStrLn(
        s"BTC-EUR: $price"
      )
    )
```
Node the addition of the `throttleShape` function. This is because we don't want to call the API as fast as we can, but we want a constant slow rate. The code says 1 call every 5 seconds. The function we supply in the second pair of brackets is a function to compute the weight of the result (to shape the throttle accordingly) we consider all responses equal to 1.

This last feature is useful if you have pagination and you don't know how many items you have for each page. You could create a stream that outputs 10 items per minute and sometimes the calls to the source API return more (or less). Then the API would be called more slowly (or faster) to compensate.

Also we don't need to limit the number of ticks with `take(3)` now that we have throttling in place.

You can [test the bitcoin ticker on Scastie](https://scastie.scala-lang.org/8nRhWur0TEqgSbfayn3JOA) remember to edit it to display your favourite currency pair and your favourite rounding.