use indexmap::map::IndexMap;
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fmt::{Debug, Display};
use super::error::{KError, KrakenErrors};
pub mod asset;
pub mod private;
pub mod public;
pub type KrakenResult<T> = Result<T, KrakenErrors<KError>>;
#[derive(Deserialize, Serialize, Debug)]
pub(crate) struct KResult<T> {
pub result: Option<T>,
pub error: Vec<String>,
}
pub(crate) enum MethodType {
Private,
Public,
}
impl fmt::Display for MethodType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MethodType::Private => write!(f, "private"),
MethodType::Public => write!(f, "public"),
}
}
}
#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "lowercase")]
pub enum SystemStatus {
Online,
CancelOnly,
PostOnly,
LimitOnly,
Offline,
}
pub enum OHLCInterval {
One,
Five,
Fifteen,
Thirty,
Sixty,
TwoForty,
FourteenForty,
TenEighty,
TwentyoneSixty,
}
impl fmt::Display for OHLCInterval {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OHLCInterval::One => write!(f, "1"),
OHLCInterval::Five => write!(f, "5"),
OHLCInterval::Fifteen => write!(f, "15"),
OHLCInterval::Thirty => write!(f, "30"),
OHLCInterval::Sixty => write!(f, "60"),
OHLCInterval::TwoForty => write!(f, "240"),
OHLCInterval::FourteenForty => write!(f, "1440"),
OHLCInterval::TenEighty => write!(f, "10080"),
OHLCInterval::TwentyoneSixty => write!(f, "21600"),
}
}
}
pub enum OrderCloseTime {
Open,
Close,
Both,
}
impl fmt::Display for OrderCloseTime {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OrderCloseTime::Open => write!(f, "open"),
OrderCloseTime::Close => write!(f, "close"),
OrderCloseTime::Both => write!(f, "both"),
}
}
}
pub enum TradeHistoryType {
All,
PosAny,
PosClosed,
PosClosing,
PosNone,
}
impl fmt::Display for TradeHistoryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TradeHistoryType::All => write!(f, "all"),
TradeHistoryType::PosAny => write!(f, "any+position"),
TradeHistoryType::PosClosed => write!(f, "closed+position"),
TradeHistoryType::PosClosing => write!(f, "closing+position"),
TradeHistoryType::PosNone => write!(f, "no+position"),
}
}
}
pub enum LedgerType {
All,
Deposit,
Withdrawal,
Trade,
Margin,
}
impl fmt::Display for LedgerType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LedgerType::All => write!(f, "all"),
LedgerType::Deposit => write!(f, "deposit"),
LedgerType::Withdrawal => write!(f, "withdrawal"),
LedgerType::Trade => write!(f, "trade"),
LedgerType::Margin => write!(f, "margin"),
}
}
}
pub enum TradeType {
Buy,
Sell,
}
impl fmt::Display for TradeType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TradeType::Buy => write!(f, "buy"),
TradeType::Sell => write!(f, "sell"),
}
}
}
pub enum OrderType {
Market,
Limit(String),
StopLoss(String),
TakeProfit(String),
StopLossLimit(String, String),
TakeProfitLimit(String, String),
SettlePosition,
}
use OrderType::{
Limit, Market, SettlePosition, StopLoss, StopLossLimit, TakeProfit, TakeProfitLimit,
};
impl OrderType {
fn elide(&self) -> (Option<String>, Option<String>) {
match self {
Market => (None, None),
Limit(price1) => (Some(price1.to_string()), None),
StopLoss(price1) => (Some(price1.to_string()), None),
TakeProfit(price1) => (Some(price1.to_string()), None),
StopLossLimit(price1, price2) => (Some(price1.to_string()), Some(price2.to_string())),
TakeProfitLimit(price1, price2) => (Some(price1.to_string()), Some(price2.to_string())),
SettlePosition => (None, None),
}
}
pub(crate) fn percent_encode(&self) -> (Option<String>, Option<String>) {
match self.elide() {
(Some(price1), Some(price2)) => {
let encoded_price1 = price1
.replace("+", "%2B")
.replace("#", "%23")
.replace("%", "%25");
let encoded_price2 = price2
.replace("+", "%2B")
.replace("#", "%23")
.replace("%", "%25");
(Some(encoded_price1), Some(encoded_price2))
}
(Some(price1), None) => {
let encoded_price1 = price1
.replace("+", "%2B")
.replace("#", "%23")
.replace("%", "%25");
(Some(encoded_price1), None)
}
(None, Some(_)) => {
unreachable!()
}
(None, None) => (None, None),
}
}
pub(crate) fn price1(&self) -> Option<String> {
match self.percent_encode() {
(Some(price), _) => Some(price),
(None, _) => None,
}
}
pub(crate) fn price2(&self) -> Option<String> {
match self.percent_encode() {
(_, Some(price)) => Some(price),
(_, None) => None,
}
}
}
impl fmt::Display for OrderType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OrderType::Market => write!(f, "market"),
OrderType::Limit(_) => write!(f, "limit"),
OrderType::StopLoss(_) => write!(f, "stop-loss"),
OrderType::TakeProfit(_) => write!(f, "take-profit"),
OrderType::StopLossLimit(_, _) => write!(f, "stop-loss-limit"),
OrderType::TakeProfitLimit(_, _) => write!(f, "take-profit-limit"),
OrderType::SettlePosition => write!(f, "settle-position"),
}
}
}
pub enum OrderFlags {
BaseCurrency,
QuoteCurrency,
NoMarketPriceProtection,
PostOnly,
}
impl fmt::Display for OrderFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OrderFlags::BaseCurrency => write!(f, "fcib"),
OrderFlags::QuoteCurrency => write!(f, "fciq"),
OrderFlags::NoMarketPriceProtection => write!(f, "nompp"),
OrderFlags::PostOnly => write!(f, "post"),
}
}
}
pub(crate) struct EndpointInfo {
methodtype: MethodType,
endpoint: String,
}
impl EndpointInfo {
pub(crate) fn method(&self) -> &MethodType {
&self.methodtype
}
pub(crate) fn endpoint(&self) -> &String {
&self.endpoint
}
}
pub struct KrakenInput {
info: EndpointInfo,
params: Option<IndexMap<String, String>>,
}
impl KrakenInput {
pub(crate) fn info(&self) -> &EndpointInfo {
&self.info
}
pub(crate) fn params(&self) -> Option<&IndexMap<String, String>> {
match &self.params {
Some(params) => Some(¶ms),
None => None,
}
}
}
pub trait Input {
fn finish(self) -> KrakenInput;
fn finish_clone(self) -> (KrakenInput, Self);
}
pub trait Output {}
pub(crate) trait MutateInput {
fn list_mut(&mut self) -> &mut IndexMap<String, String>;
}
pub(crate) trait IntoInputList: MutateInput {
fn list_name(&self) -> String;
}
pub(crate) trait InputListItem: IntoInputList {
type ListItem;
fn with_item(mut self, item: Self::ListItem) -> Self
where
Self: Sized,
Self::ListItem: Display,
{
self.format_item(item);
self
}
fn format_item(&mut self, item: Self::ListItem)
where
Self::ListItem: Display,
{
let listname = self.list_name();
match self.list_mut().get_mut(&listname) {
Some(list) => {
if list.contains(&item.to_string()) {
return;
}
*list = format!("{},{}", list, item.to_string());
}
None => {
self.list_mut().insert(listname, item.to_string());
}
}
}
}
pub(crate) trait InputList: InputListItem {
fn with_item_list<U>(mut self, items: U) -> Self
where
U: IntoIterator<Item = Self::ListItem>,
Self: Sized,
Self::ListItem: Display,
{
let listname = self.list_name();
match self.list_mut().contains_key(&listname) {
true => {
items.into_iter().for_each(|item| self.format_item(item));
self
}
false => {
let mut iter = items.into_iter();
match iter.next() {
Some(val) => {
self.list_mut().insert(listname, val.to_string());
self.with_item_list(iter)
}
None => self,
}
}
}
}
}
pub(crate) trait UpdateInput: MutateInput {
fn update_input<T>(mut self, key: &str, value: T) -> Self
where
Self: Sized,
T: Display,
{
match self.list_mut().get_mut(key) {
Some(key) => {
*key = value.to_string();
self
}
None => {
self.list_mut().insert(String::from(key), value.to_string());
self
}
}
}
}
pub(crate) fn format_params<T, U>(params: &Option<&IndexMap<T, U>>) -> Option<String>
where
T: Display,
U: Display,
{
match params {
Some(params) => {
let mut res = String::new();
for index in 0..params.len() {
let pair = params.get_index(index).unwrap();
if index == 0 {
res = format!("{}{}={}", res, pair.0, pair.1);
} else {
res = format!("{}&{}={}", res, pair.0, pair.1);
}
}
Some(res)
}
None => None,
}
}