Add rate limiter (50/s + 5/s historical), symbol mapper, order builders, account queries, market data subscriptions, and high-level CTraderClient that orchestrates the full connect/auth/symbol-load lifecycle. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
101 lines
2.6 KiB
Rust
101 lines
2.6 KiB
Rust
//! cTrader Open API error types.
|
|
|
|
use std::fmt;
|
|
|
|
/// Errors from the cTrader Open API client.
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum CTraderError {
|
|
/// TCP or TLS connection failed.
|
|
#[error("connection failed: {0}")]
|
|
ConnectionFailed(String),
|
|
|
|
/// TLS handshake error.
|
|
#[error("TLS error: {0}")]
|
|
TlsError(String),
|
|
|
|
/// OAuth2 token exchange failed.
|
|
#[error("OAuth error: {0}")]
|
|
OAuthError(String),
|
|
|
|
/// Application-level authentication rejected.
|
|
#[error("application auth failed: {0}")]
|
|
AppAuthFailed(String),
|
|
|
|
/// Account-level authentication rejected.
|
|
#[error("account auth failed: {0}")]
|
|
AccountAuthFailed(String),
|
|
|
|
/// Protobuf encode/decode error.
|
|
#[error("protocol error: {0}")]
|
|
ProtocolError(String),
|
|
|
|
/// Codec framing error.
|
|
#[error("codec error: {0}")]
|
|
CodecError(String),
|
|
|
|
/// Rate limit exceeded (50 req/s or 5 req/s for historical).
|
|
#[error("rate limit exceeded for {bucket}")]
|
|
RateLimitExceeded {
|
|
/// Which bucket was exhausted.
|
|
bucket: RateLimitBucket,
|
|
},
|
|
|
|
/// Request timed out waiting for a response.
|
|
#[error("request timed out after {0:?}")]
|
|
Timeout(std::time::Duration),
|
|
|
|
/// Server returned an error payload.
|
|
#[error("server error {code}: {description}")]
|
|
ServerError {
|
|
/// cTrader error code.
|
|
code: String,
|
|
/// Human-readable description.
|
|
description: String,
|
|
},
|
|
|
|
/// Client is not connected.
|
|
#[error("not connected")]
|
|
NotConnected,
|
|
|
|
/// Symbol not found in the cached symbol list.
|
|
#[error("unknown symbol: {0}")]
|
|
UnknownSymbol(String),
|
|
|
|
/// I/O error (required by `tokio_util::codec`).
|
|
#[error("io error: {0}")]
|
|
Io(String),
|
|
}
|
|
|
|
impl From<std::io::Error> for CTraderError {
|
|
fn from(e: std::io::Error) -> Self {
|
|
Self::Io(e.to_string())
|
|
}
|
|
}
|
|
|
|
impl From<prost::DecodeError> for CTraderError {
|
|
fn from(e: prost::DecodeError) -> Self {
|
|
Self::ProtocolError(format!("protobuf decode error: {e}"))
|
|
}
|
|
}
|
|
|
|
/// Rate-limit bucket categories.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum RateLimitBucket {
|
|
/// Standard messages: 50 per second.
|
|
NonHistorical,
|
|
/// Historical data requests: 5 per second.
|
|
Historical,
|
|
}
|
|
|
|
impl fmt::Display for RateLimitBucket {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::NonHistorical => f.write_str("non-historical (50/s)"),
|
|
Self::Historical => f.write_str("historical (5/s)"),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Convenience alias used throughout the crate.
|
|
pub type Result<T> = std::result::Result<T, CTraderError>;
|