Requesting exchange rates for crypto pairs in code the easy way

in programming •  7 years ago  (edited)

Here is some simple code to get the current rates for crypto currency pairs using the CryptoCompare API.

It is written in C# but should be simple enough to translate to other languages.

Might be a fun challenge to translate to other languages. If you do this post in the comments or write a piece and link back to my post :)

Anyway on with the post. The CryptoCompare API is simple enough, here is the request.

https://min-api.cryptocompare.com/data/price?fsym=XXX&tsyms=YYY

So ETH to USD would be

https://min-api.cryptocompare.com/data/price?fsym=ETH&tsyms=USD

The response is a simple JSON stream. A valid response being:

{"USD":315.98}

And an error being

{
    "Response":"Error",
    "Message":"Additional supply depots required. (Invalid Market)",
    "Data":[],
    "Type":99
}

So to request the rate for a pair I have this simple class:

public static class PriceRequester
{
    private const string ErrorResponseKey = "Message";

    public static async Task<Either<decimal, string>> 
        GetExchangeRate(string from, string to)
    {
        try
        {
            using (var client = new HttpClient())
            {
                var response = await client
                    .GetStringAsync(
                        $"https://min-api.cryptocompare.com/data/price?fsym={from}&tsyms={to}")
                    .ConfigureAwait(false);

                return ResponseParser(from, to, response);
            }
        }
        catch (Exception ex)
        {
            return $"Failed to get rate for {from} in {to}: {ex.Message}".ToLeft<decimal, string>();
        }
    }

    public static Either<decimal, string> 
        ResponseParser(string from, string to, string walletResponse)
    {
        var parser = JsonObject.Parse(walletResponse);

        if (parser.Keys.Contains(ErrorResponseKey))
        {
            return parser[ErrorResponseKey].GetString().ToLeft<decimal, string>();
        }

        return ((decimal)parser[to].GetNumber()).ToRight<decimal, string>();
    }
}

So it performs an async request to the API, if it gets a response that is parsed and returned as a rate or an error using an Either Monad.

You call it as follows.

var ethValue = PriceRequester.GetExchangeRate("ETH", "USD").Result;

The Either Monad is stolen from Haskell, a bit out of scope for this article but I have included the code to be complete.

It is either a Right value, this is a success value, or a Left value which is what is left if not valid. That is Haskell people for you :)

public static class Either
{
    public static Either<TRight, TLeft> ToRight<TRight, TLeft>(this TRight right)
        => new Either<TRight, TLeft>.RightInstance(right);

    public static Either<TRight, TLeft> ToLeft<TRight, TLeft>(this TLeft left)
        => new Either<TRight, TLeft>.LeftInstance(left);
}

public abstract class Either<TRight, TLeft>
{
    public abstract TRight Right { get; }

    public abstract TLeft Left { get; }

    public abstract bool IsRight { get; }

    public abstract bool IsLeft { get; }

    public abstract Either<TRightResult, TLeft> 
        Select<TRightResult>(Func<TRight, TRightResult> map);

    public abstract Either<TRightResult, TLeft> 
        SelectMany<TRightResult>(Func<TRight, Either<TRightResult, TLeft>> map);

    internal class RightInstance : Either<TRight, TLeft>
    {
        public RightInstance(TRight right)
        {
            Right = right;
        }

        public override TRight Right { get; }

        public override TLeft Left
            => throw new InvalidOperationException("Either is not Right");

        public override bool IsRight => true;

        public override bool IsLeft => false;

        public override Either<TRightResult, TLeft> 
            Select<TRightResult>(Func<TRight, TRightResult> map)
            => map(Right).ToRight<TRightResult, TLeft>();

        public override Either<TRightResult, TLeft> 
            SelectMany<TRightResult>(Func<TRight, Either<TRightResult, TLeft>> map)
            => map(Right);
    }

    internal class LeftInstance : Either<TRight, TLeft>
    {
        public LeftInstance(TLeft left)
        {
            Left = left;
        }

        public override TRight Right
            => throw new InvalidOperationException("Either is not Right");

        public override TLeft Left { get; }

        public override bool IsRight => false;

        public override bool IsLeft => true;

        public override Either<TRightResult, TLeft>
            Select<TRightResult>(Func<TRight, TRightResult> map)
            => Left.ToLeft<TRightResult, TLeft>();

        public override Either<TRightResult, TLeft> 
            SelectMany<TRightResult>(Func<TRight, Either<TRightResult, TLeft>> map)
            => Left.ToLeft<TRightResult, TLeft>();
    }
}

Hope you found this helpful. Leave comments or questions below.

Happy coding

Woz

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!
Sort Order:  

When I played around with a bot, I used the tools for Python linked on the bittrex homepage. There's also a script on github to load the historic price charts.