Skip to content

Commit

Permalink
v2.0.2
Browse files Browse the repository at this point in the history
**Bug Fixes**
- When deserializing a product, price is set to default if the price is missing.
- Small bug fix for TimeInForce enum.
- Fixed Level2 Channel for the WebSocket.
- MarginWindowMeasure is now correctly public.

**New Additions**
- Readers returned from the `websocket.connect` can be passed directly to listen.
- Serialization added to Message Events.
- Made `WebSocketClient::process_message()` publicly accessible.
- `WebSocketClient::fetch_async()` and `WebSocketClient::fetch_sync()` added so that the user can obtain and process messages with closures.
- Implemented From<Product> for ProductUpdate.
- Added Result<(), String> as a return to the fetch_(a)sync functions.

**Optimizations**
- Updated snippets to adhere to best practices (linting).
- Corrected the mutability for WebSocketClient connect() and `connect_endpoint()`.
- Simplify messages to use no custom serde function.
- Removed several unused crates.

**Documentation**
- README updates.
- Updated websocket example.
- Add example of pattern matching on events.

**Breaking Changes**
- Swapped names for `handle_reconnection` and `reconnect`, made `reconnect` public.
- Removed `enable_user` from WebSocketClientBuilder. Toggled by providing authentication details.
- Removed `MessageCallback` and `CandleCallback` traits. They were clunky in implmentation for the user.
- Removed `candle_watcher`, this will be revisited in the future with the new listener system.
  • Loading branch information
Ohkthx authored Jan 18, 2025
2 parents 8718cb3 + 300d8d1 commit 0189015
Show file tree
Hide file tree
Showing 30 changed files with 577 additions and 1,064 deletions.
217 changes: 51 additions & 166 deletions Cargo.lock

Large diffs are not rendered by default.

38 changes: 6 additions & 32 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cbadv"
version = "2.0.1"
version = "2.0.2"
edition = "2021"
description = "Asynchronous Coinbase Advanced REST and WebSocket API"
license = "MIT"
Expand All @@ -12,43 +12,22 @@ categories = ["api-bindings", "cryptography::cryptocurrencies"]
include = ["src/**", "Cargo.toml", "README.md", "LICENSE", "examples/**"]

[features]
default = ["config"]
default = []
full = ["config"]
config = ["dep:toml"]

[dependencies]
# Core dependencies
reqwest = { version = "0.12.9", features = ["json"] }
futures = "0.3.31"
tokio = { version = "1.41.1", features = ["full"] }

# Cryptography and signing
hmac = "0.12.1"
sha2 = "0.10.8"
hex = "0.4.3"

# Serialization and configuration
reqwest = { version = "0.12.9", features = ["json"] }
tokio-tungstenite = { version = "0.24.0", features = ["native-tls"] }
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread"], default-features = false }
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
serde_with = "3.11.0"
toml = { version = "0.8.19", optional = true }

# WebSocket support
tokio-tungstenite = { version = "0.24.0", features = ["native-tls"] }
futures-util = "0.3.31"
async-trait = "0.1.83"

# Utilities
uuid = { version = "1.11.0", features = [
"v4",
"fast-rng",
"macro-diagnostics",
] }
chrono = "0.4.38"
num-traits = "0.2.19"
uuid = { version = "1.11.0", features = ["v4", "fast-rng"] }
base64 = "0.22.1"
ring = "0.17.8"
rand = "0.8.5"
openssl = "0.10.68"

[[example]]
Expand Down Expand Up @@ -103,17 +82,12 @@ required-features = ["config"]
[[example]]
name = "websocket"
path = "examples/websocket.rs"
required-features = ["config"]

[[example]]
name = "websocket_user"
path = "examples/websocket_user.rs"
required-features = ["config"]

[[example]]
name = "watch_candles"
path = "examples/watch_candles.rs"

[[example]]
name = "custom_config"
path = "examples/custom_config.rs"
Expand Down
25 changes: 14 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- markdownlint-disable MD033 MD041 -->
<p align="center">
<a href="https://github.com/Ohkthx/cbadv-rs#tips-appreciated" title="Donate with Bitcoin!">
<img src="https://img.shields.io/badge/donate-black?style=for-the-badge&logo=bitcoin&logoColor=f38ba8&label=BITCOIN&labelColor=11111b&color=f38ba8"
Expand All @@ -19,6 +20,7 @@
<img src="https://img.shields.io/github/repo-size/Ohkthx/cbadv-rs?style=for-the-badge&logoColor=a6e3a1&labelColor=11111b&color=a6e3a1"
alt="GitHub repo size"></a>
</p>
<!-- markdownlint-enable MD033 MD041 -->

---

Expand All @@ -39,16 +41,18 @@ cbadv = { git = "https://github.com/ohkthx/cbadv-rs", branch = "main" }

## Table of Contents

- [Features](#features)
- [Documentation](#documentation)
- [Configuration](#configuration)
- [Examples](#examples)
- [API Coverage](#api-coverage)
- [WebSocket API](#websocket-api)
- [REST API](#rest-api)
- [TODO](#todo)
- [Contributing](#contributing)
- [Tips Appreciated!](#tips-appreciated)
- [Asynchronous CoinBase Advanced API](#asynchronous-coinbase-advanced-api)
- [Table of Contents](#table-of-contents)
- [Features](#features)
- [Documentation](#documentation)
- [API Coverage](#api-coverage)
- [WebSocket API](#websocket-api)
- [REST API](#rest-api)
- [Configuration](#configuration)
- [Examples](#examples)
- [TODO](#todo)
- [Contributing](#contributing)
- [Tips Appreciated](#tips-appreciated)

---

Expand All @@ -58,7 +62,6 @@ cbadv = { git = "https://github.com/ohkthx/cbadv-rs", branch = "main" }
- Authenticated and Public REST Endpoints.
- Builders to create REST and WebSocket Clients.
- Convenient configuration file support for API keys (`features = ["config"]`).
- Comprehensive coverage of all accessible REST and WebSocket endpoints (as of **20231206**).
- Numerous examples for seamless integration and testing.

---
Expand Down
49 changes: 20 additions & 29 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- markdownlint-disable MD033 MD041 -->
<p align="center">
<a href="https://crates.io/crates/cbadv" title="View on crates.io">
<img src="https://img.shields.io/crates/v/cbadv?style=for-the-badge&logoColor=89b4fa&labelColor=11111b&color=89b4fa"
Expand All @@ -9,30 +10,32 @@
<img src="https://img.shields.io/github/repo-size/Ohkthx/cbadv-rs?style=for-the-badge&logoColor=a6e3a1&labelColor=11111b&color=a6e3a1"
alt="GitHub repo size"></a>
</p>
<!-- markdownlint-enable MD033 MD041 -->

# cbadv-rs: Coinbase Advanced Trading API Wrapper

Welcome to **cbadv-rs**, a Rust crate for interacting with the Coinbase Advanced Trading API. This library provides easy-to-use interfaces for various Coinbase APIs such as Account, Product, Fee, Order, Portfolio, Public, Sandbox, and WebSocket.

## Table of Contents

- [Examples](#examples)
- [Account API](#account-api)
- [Product API](#product-api)
- [Fee API](#fee-api)
- [Order API](#order-api)
- [Portfolio API](#portfolio-api)
- [Payment API](#payment-api)
- [Convert API](#convert-api)
- [Data API](#data-api)
- [Public API](#public-api)
- [Sandbox API](#sandbox-api)
- [WebSocket API](#websocket-api)
- [User Orders (WebSocket API)](#user-orders-websocket-api)
- [Watch Candles (WebSocket API)](#watch-candles-websocket-api)
- [Custom Configurations](#custom-configurations)
- [Contributing](#contributing)
- [License](#license)
- [cbadv-rs: Coinbase Advanced Trading API Wrapper](#cbadv-rs-coinbase-advanced-trading-api-wrapper)
- [Table of Contents](#table-of-contents)
- [Examples](#examples)
- [Account API](#account-api)
- [Product API](#product-api)
- [Fee API](#fee-api)
- [Order API](#order-api)
- [Portfolio API](#portfolio-api)
- [Convert API](#convert-api)
- [Payment API](#payment-api)
- [Data API](#data-api)
- [Public API](#public-api)
- [Sandbox API](#sandbox-api)
- [WebSocket API](#websocket-api)
- [User Orders (WebSocket API)](#user-orders-websocket-api)
- [Custom Configurations](#custom-configurations)
- [Contributing](#contributing)
- [License](#license)

---

Expand Down Expand Up @@ -182,18 +185,6 @@ cargo run --example websocket_user --features="config"

---

#### Watch Candles (WebSocket API)

Learn how to watch candlestick data via the WebSocket API. Currently, only 5-minute granularity is supported (as of 2023-10-19). Example source: [watch_candles.rs](https://github.com/Ohkthx/cbadv-rs/tree/main/examples/watch_candles.rs)

**Run the example**:

```bash
cargo run --example watch_candles --features="config"
```

---

### Custom Configurations

Learn how to create custom configuration files tailored to your integration needs. Example source: [custom_config.rs](https://github.com/Ohkthx/cbadv-rs/tree/main/examples/custom_config.rs)
Expand Down
2 changes: 1 addition & 1 deletion examples/account_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async fn main() {
match accounts.iter().position(|r| r.currency == product_name) {
Some(index) => {
let account = accounts.get(index).unwrap();
account_uuid = account.uuid.clone();
account_uuid.clone_from(&account.uuid);
}
None => println!("Out of bounds, could not find account."),
}
Expand Down
142 changes: 84 additions & 58 deletions examples/order_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,16 @@
use std::process::exit;
use std::thread;
use std::time::Duration;

use cbadv::config::{self, BaseConfig};
use cbadv::models::order::{
OrderCancelRequest, OrderCreateBuilder, OrderEditRequest, OrderListQuery, OrderSide,
OrderStatus, OrderType, TimeInForce,
OrderCancelRequest, OrderCreateBuilder, OrderCreateRequest, OrderEditRequest, OrderListQuery,
OrderSide, OrderStatus, OrderType, TimeInForce,
};
use cbadv::RestClientBuilder;
use chrono::Duration;

#[tokio::main]
async fn main() {
let create_new: bool = false;
let edit_created: bool = true;
let cancel_created: bool = true;
let cancel_all: bool = false;
let product_id: &str = "ETH-USDC";
let mut created_order_id: Option<String> = None;
let new_order = match OrderCreateBuilder::new(product_id, OrderSide::Buy)
.base_size(0.005)
.limit_price(100.0)
.post_only(true)
.order_type(OrderType::Limit)
.time_in_force(TimeInForce::GoodUntilCancelled)
.build()
{
Ok(order) => order,
Err(error) => {
println!("Unable to build order: {error}");
exit(1);
}
};
use cbadv::{RestClient, RestClientBuilder};

fn init_client() -> RestClient {
// Load the configuration file.
let config: BaseConfig = match config::load("config.toml") {
Ok(c) => c,
Expand All @@ -60,53 +38,101 @@ async fn main() {
};

// Create a client to interact with the API.
let mut client = match RestClientBuilder::new().with_config(&config).build() {
match RestClientBuilder::new().with_config(&config).build() {
Ok(c) => c,
Err(why) => {
eprintln!("!ERROR! {why}");
exit(1)
}
};
}
}

if create_new {
println!(
"Creating Order with Client ID: {}",
new_order.client_order_id
);
match client.order.create(&new_order).await {
Ok(summary) => {
if let Some(success) = &summary.success_response {
created_order_id = Some(success.order_id.clone());
}
println!("Order creation result: {summary:#?}");
async fn create_new_order(
client: &mut RestClient,
new_order: &OrderCreateRequest,
) -> Option<String> {
let mut created_order_id: Option<String> = None;
println!(
"Creating Order with Client ID: {}",
new_order.client_order_id
);

match client.order.create(new_order).await {
Ok(summary) => {
if let Some(success) = &summary.success_response {
created_order_id = Some(success.order_id.clone());
}
Err(error) => println!("Unable to create order: {error}"),
println!("Order creation result: {summary:#?}");
}
Err(error) => println!("Unable to create order: {error}"),
}

created_order_id
}

async fn edit_created_order(client: &mut RestClient, order_id: &str) {
let edit_order = OrderEditRequest::new(order_id, 50.0, 0.006);
println!("\n\nEditing order for {order_id}.");
match client.order.edit(&edit_order).await {
Ok(result) => println!("{result:#?}"),
Err(error) => println!("Unable to edit order: {error}"),
}
}

async fn cancel_created_order(client: &mut RestClient, order_id: &str) {
println!("\n\nCancelling Order with ID: {order_id}");
match client
.order
.cancel(&OrderCancelRequest::new(&[order_id.to_string()]))
.await
{
Ok(summary) => println!("Order cancel result: {summary:#?}"),
Err(error) => println!("Unable to cancel order: {error}"),
}
}

#[tokio::main]
async fn main() {
let create_new: bool = false;
let edit_created: bool = true;
let cancel_created: bool = true;
let cancel_all: bool = false;
let product_id: &str = "ETH-USDC";
let mut created_order_id: Option<String> = None;
let new_order = match OrderCreateBuilder::new(product_id, OrderSide::Buy)
.base_size(0.005)
.limit_price(100.0)
.post_only(true)
.order_type(OrderType::Limit)
.time_in_force(TimeInForce::GoodUntilCancelled)
.build()
{
Ok(order) => order,
Err(error) => {
println!("Unable to build order: {error}");
exit(1);
}
};

let mut client = init_client();

// Creates a new order from scratch, the resulting order id will be used for other operations.
if create_new {
created_order_id = create_new_order(&mut client, &new_order).await;
}

// Edits the created order.
if let Some(order_id) = &created_order_id {
if create_new && edit_created {
thread::sleep(Duration::seconds(1).to_std().unwrap());
let edit_order = OrderEditRequest::new(order_id, 50.0, 0.006);
println!("\n\nEditing order for {order_id}.");
match client.order.edit(&edit_order).await {
Ok(result) => println!("{result:#?}"),
Err(error) => println!("Unable to edit order: {error}"),
}
thread::sleep(Duration::from_secs(1));
edit_created_order(&mut client, order_id).await;
}
}

// Cancels the created order.
if let Some(order_id) = &created_order_id {
if create_new && cancel_created {
println!("\n\nCancelling Order with ID: {order_id}");
match client
.order
.cancel(&OrderCancelRequest::new(&[order_id.clone()]))
.await
{
Ok(summary) => println!("Order cancel result: {summary:#?}"),
Err(error) => println!("Unable to cancel order: {error}"),
}
cancel_created_order(&mut client, order_id).await;
}
}

Expand Down Expand Up @@ -143,7 +169,7 @@ async fn main() {
println!("Orders obtained: {:#?}", orders.orders.len());
match orders.orders.first() {
Some(order) => {
order_id = order.order_id.clone();
order_id.clone_from(&order.order_id);
println!("{order:#?}");
}
None => println!("Out of bounds, no orders exist."),
Expand Down
Loading

0 comments on commit 0189015

Please sign in to comment.