Crate akkorokamui[][src]

Expand description

Kraken HTTP client.

How to use akkorokamui

Add akkorokamui to the list of your dependencies:

akkorokamui = "0.5"

Features

By default akkorokamui will make use of the rustls transport layer security to connect to HTTPs destinations. If you wish to use the system-native TLS you can do so by specifying the native-tls optional feature:

akkorokamui = { version = "0.5", features = ["native-tls"], default-features = false }

Examples

Create a client without credentials (server time)

There are two possible ways to construct a client: with or without credentials, but be aware that without credentials you will only have access to the public APIs.

According to the Kraken APIs documentation, all the HTTP responses will contain two fields:

error = array of error messages
result = result of API call (may not be present if errors occur)

The ResponseValue type is the most general type of response that can be returned by the client and it mirrors the above description, where the result, if present, will be encoded in a serde_json::Value enum.

use akkorokamui::{api, blocking::Client, ResponseValue};
use anyhow::Result;

fn main() -> Result<()> {
   let user_agent = "<product>/<product-version>";
   let client = Client::new(user_agent)?;

   let api = api::public::time();
   let resp: ResponseValue = client.send(api)?;
   println!("{:?}", resp);

   Ok(())
}

Extract fields from a generic response (server time unixtime)

You can extract any field from the JSON response using the serde_json::Value available methods.

use akkorokamui::{api, blocking::Client, ResponseValue};
use anyhow::Result;

fn main() -> Result<()> {
   let user_agent = "<product>/<product-version>";
   let client = Client::new(user_agent)?;

   let api = api::public::time();
   let resp: ResponseValue = client.send(api)?;
   println!("{:?}", resp);

   if let Some(result) = resp.result {
       let time = result.get("unixtime").and_then(|t| t.as_u64());
       println!("Time: {:?}", time);
   }

   Ok(())
}

Deserialize a response into a user defined type (server time unixtime)

The ResponseValue allows not to worry too much on the JSON structure returned by the Kraken APIs, while not being tied too much on the types defined in this library. But if you want to exploit the type safety of your user defined types you can do so by defining your own Response<T>.

use akkorokamui::{api, blocking::Client, Response};
use anyhow::{bail, Result};
use serde::Deserialize;

fn main() -> Result<()> {
   let user_agent = "<product>/<product-version>";
   let client = Client::new(user_agent)?;

   #[derive(Debug, Deserialize)]
   struct Time {
       unixtime: u64,
   }

   let api = api::public::time();
   let resp: Response<Time> = client.send(api)?;
   println!("{:?}", resp);

   if let Some(result) = resp.result {
       println!("Time: {}", result.unixtime);
   } else {
      bail!("Cannot get server time: {:?}", resp.error);
   }

   Ok(())
}

Specify API parameters (recent trades)

The API builder allows to specify any key-value pair as new parameter via the method .with(key, value), which can be chained for as many parameters are needed:

use akkorokamui::{api, Asset, blocking::Client, Response};
use anyhow::{bail, Result};
use serde::Deserialize;
use std::{
    collections::HashMap,
    time::{Duration, SystemTime, UNIX_EPOCH},
};

fn main() -> Result<()> {
    let user_agent = "<product>/<product-version>";
    let client = Client::new(user_agent)?;

    #[derive(Debug, Deserialize)]
    struct Trade {
        price: String,
        volume: String,
        time: f32,
        buy_sell: String,
        market_limit: String,
        miscellaneous: String,
    }

    #[derive(Debug, Deserialize)]
    struct Trades {
        #[serde(flatten)]
        asset_pair_trades: HashMap<String, Vec<Trade>>,
        last: String,
    }

    let since = Duration::from_secs(30 * 60);
    let since = match SystemTime::now().checked_sub(since) {
        Some(since) => since.duration_since(UNIX_EPOCH)?.as_secs(),
        _ => bail!("invalid duration"),
    };

    let asset_pair = Asset::new("XXBT").pair("ZEUR");
    let api = api::public::trades()
        .with("pair", &asset_pair)
        .with("since", since);

    let resp: Response<Trades> = client.send(api)?;
    println!("{:?}", resp);

    if let Some(result) = resp.result {
        // note: check GET public/AssetPairs for the actual asset pair name
        let asset_pair_name = asset_pair.to_string();
        if let Some(trades) = result.asset_pair_trades.get(&asset_pair_name) {
            for trade in trades {
                println!("price at {}: {}", trade.time, trade.price);
            }
        }
    } else {
        bail!("Cannot get trades: {:?}", resp.error);
    }

    Ok(())
}

Client credentials and private APIs (account balance - async version)

In order to use private APIs you need to own a pair of keys: the public API key as well as your private key (refer to the Kraken support page to learn how to generate these keys).

The keys must be stored in a single file, where the first line contains the public API key and the second line contains the private key.

use akkorokamui::{api, Asset, Client, Credentials, Response};
use anyhow::{bail, Result};
use std::collections::HashMap;

type Amount = String;
type Balance<'a> = HashMap<Asset<'a>, Amount>;

#[tokio::main]
async fn main() -> Result<()> {
    let keys_path = "kraken.key";
    let credentials = Credentials::read(keys_path)?;

    let user_agent = "<product>/<product-version>";
    let client = Client::with_credentials(user_agent, credentials)?;

    let api = api::private::balance();
    let resp: Response<Balance> = client.send(api).await?;
    println!("{:?}", resp);

    if let Some(result) = resp.result {
        println!("GBP: {:?}", result.get(&Asset::new("ZGBP")));
    } else {
        bail!("Cannot get balance: {:?}", resp.error);
    }

    Ok(())
}

Add a new order

use akkorokamui::{
    api, Asset, AssetPair, blocking::Client, Credentials, Order, OrderType,
    Response, ResponseValue,
};
use anyhow::{bail, Result};
use serde::Deserialize;
use std::collections::HashMap;

fn main() -> Result<()> {
    let keys_path = "kraken.key";
    let credentials = Credentials::read(keys_path)?;

    let user_agent = "<product>/<product-version>";
    let client = Client::with_credentials(user_agent, credentials)?;

    let asset_pairs = get_asset_pairs(&client)?;
    let pair = Asset::new("XXRP").pair("ZGBP");
    let xrp_gbp = if let Some(name) = asset_pairs.get(&pair) {
        name
    } else {
        bail!("{} asset pair name not found", pair)
    };

    let api = api::private::add_order()
        // validate only, do not actually place any order
        .with("validate", true)
        .with("pair", xrp_gbp)
        .with("type", Order::Buy)
        .with("ordertype", OrderType::TakeProfitLimit)
        // take profit price trigger
        .with("price", 0.19)
        // limit price
        .with("price2", 0.191)
        .with("volume", 30)
        // prefer fee in quote currency
        .with("oflags", "fciq");

    let resp: ResponseValue = client.send(api)?;
    println!("{:?}", resp);

    Ok(())
}

fn get_asset_pairs<'a>(client: &Client) -> Result<HashMap<AssetPair<'a>, String>> {
    type AssetPairs<'a> = HashMap<String, AssetPair<'a>>;

    let api = api::public::asset_pairs();
    let resp: Response<AssetPairs> = client.send(api)?;

    if let Some(result) = resp.result {
        Ok(result
            .into_iter()
            .map(|(k, v)| (v.base.pair(v.quote), k))
            .collect())
    } else {
        bail!("Cannot get asset pairs: {:?}", resp.error);
    }
}

Re-exports

pub use api::Api;
pub use api::Response;
pub use api::ResponseValue;
pub use client::blocking;
pub use client::Client;

Modules

Structs

Kraken tradable asset name, such a cryptocurrencies or FIAT.

An asset pair with base and quote assets.

Public-Private key pair to be used by the API.

Enums

Crate error enumeration.

Order to buy or sell the asset.

Enumeration of order types.