v_exchanges 0.17.3

Implementations of HTTP/HTTPS/WebSocket API methods for some crypto exchanges, using [crypto-botters](<https://github.com/negi-grass/crypto-botters>) framework
Documentation
mod account;
mod market;

pub use adapters::kucoin::KucoinOption;

crate::define_provider_timeframe!(KucoinTimeframe, ["1m", "3m", "5m", "15m", "30m", "1h", "2h", "4h", "6h", "8h", "12h", "1d", "1w"]);
use std::collections::BTreeMap;

use secrecy::SecretString;
use v_exchanges_adapters::Client;
use v_utils::trades::{Pair, Timeframe};

use crate::{
	ExchangeName, ExchangeResult, Instrument, RequestRange, Symbol,
	core::{ExchangeImpl, ExchangeInfo, Klines, PersonalInfo},
};

#[derive(Clone, Debug, Default, derive_more::Deref, derive_more::DerefMut)]
pub struct Kucoin(pub Client);

#[async_trait::async_trait]
impl ExchangeImpl for Kucoin {
	fn name(&self) -> ExchangeName {
		ExchangeName::Kucoin
	}

	fn auth(&mut self, pubkey: String, secret: SecretString) {
		self.update_default_option(KucoinOption::Pubkey(pubkey));
		self.update_default_option(KucoinOption::Secret(secret));
		// Note: Passphrase needs to be set separately via KucoinOption::Passphrase
	}

	fn set_recv_window(&mut self, _recv_window: std::time::Duration) {
		tracing::warn!("KuCoin does not support configurable recv_window - uses a fixed 5-second tolerance window for all authenticated requests");
	}

	fn default_recv_window(&self) -> Option<std::time::Duration> {
		None // KuCoin doesn't support configurable recv_window
	}

	async fn exchange_info(&self, instrument: Instrument) -> ExchangeResult<ExchangeInfo> {
		match instrument {
			Instrument::Spot => market::exchange_info(self, None).await,
			Instrument::Perp => market::futures::exchange_info(self, None).await,
			_ => unimplemented!(),
		}
	}

	async fn prices(&self, pairs: Option<Vec<Pair>>, instrument: Instrument) -> ExchangeResult<BTreeMap<Pair, f64>> {
		match instrument {
			Instrument::Spot => market::prices(self, pairs, None).await,
			Instrument::Perp => market::futures::prices(self, pairs, None).await,
			_ => unimplemented!(),
		}
	}

	async fn klines(&self, symbol: Symbol, tf: Timeframe, range: RequestRange) -> ExchangeResult<Klines> {
		match symbol.instrument {
			Instrument::Spot => market::klines(self, symbol, tf.try_into()?, range, None).await,
			Instrument::Perp => market::futures::klines(self, symbol, tf.try_into()?, range, None).await,
			_ => unimplemented!(),
		}
	}

	async fn personal_info(&self, _instrument: Instrument, recv_window: Option<std::time::Duration>) -> ExchangeResult<PersonalInfo> {
		account::personal_info(self, recv_window).await
	}
}