Skip to content

Order API endpoints #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub fn generate_nonce() -> Result<String> {
let start = SystemTime::now();
let since_epoch = start.duration_since(UNIX_EPOCH)?;

let timestamp = since_epoch.as_secs() * 1000 + since_epoch.subsec_nanos() as u64 / 1_000_000;

Ok((timestamp + 1).to_string())
}
let timestamp = since_epoch.as_millis() * 1000 as u128;
Ok(timestamp.to_string())
}
29 changes: 22 additions & 7 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use serde::Serialize;

static API1_HOST : &'static str = "https://api.bitfinex.com/v2/";
static API_SIGNATURE_PATH : &'static str = "/api/v2/auth/r/";
static API_SIGNATURE_PATH_ORDER : &'static str = "/api/v2/auth/w/";
static NO_PARAMS: &'static [(); 0] = &[];

#[derive(Clone)]
Expand Down Expand Up @@ -36,30 +37,42 @@ impl Client {
}

pub fn post_signed(&self, request: String, payload: String) -> Result<String> {
self.post_signed_params(request, payload, NO_PARAMS)
self.post_signed_params(request, payload, NO_PARAMS, false)
}

pub fn post_signed_order(&self, request: String, payload: String) -> Result<String> {
self.post_signed_params(request, payload, NO_PARAMS, true)
}

pub fn post_signed_params<P: Serialize + ?Sized>(
&self,
request: String,
payload: String,
params: &P,
is_order_request: bool
) -> Result<String> {
let url: String = format!("{}auth/r/{}", API1_HOST, request);

let url = match is_order_request {
true => format!("{}auth/w/{}", API1_HOST, request),
_ => format!("{}auth/r/{}", API1_HOST, request)
};

let api_signature_path = if is_order_request { API_SIGNATURE_PATH_ORDER } else { API_SIGNATURE_PATH };

println!("{} {} {}", url, api_signature_path, payload);
let client = reqwest::Client::new();
let response = client.post(url.as_str())
.headers(self.build_headers(request, payload.clone())?)
.headers(self.build_headers(request, payload.clone(), api_signature_path.to_string())?)
.body(payload)
.query(params)
.send()?;

self.handler(response)
}

fn build_headers(&self, request: String, payload: String) -> Result<HeaderMap> {
fn build_headers(&self, request: String, payload: String, api_signature_path: String) -> Result<HeaderMap> {
let nonce: String = auth::generate_nonce()?;
let signature_path: String = format!("{}{}{}{}", API_SIGNATURE_PATH, request, nonce, payload);
let signature_path: String = format!("{}{}{}{}", api_signature_path, request, nonce, payload);

let signature = auth::sign_payload(self.secret_key.as_bytes(), signature_path.as_bytes())?;

Expand All @@ -74,10 +87,12 @@ impl Client {
}

fn handler(&self, mut response: Response) -> Result<String> {
let mut body = String::new();
response.read_to_string(&mut body)?;
println!("{:?}", body);

match response.status() {
StatusCode::OK => {
let mut body = String::new();
response.read_to_string(&mut body)?;
return Ok(body);
},
StatusCode::INTERNAL_SERVER_ERROR => {
Expand Down
4 changes: 2 additions & 2 deletions src/ledger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,13 @@ impl Ledger {
{
let payload: String = format!("{}", "{}");
let request: String = format!("ledgers/{}/hist", symbol.into());
let params = HistoryParams{
let params = HistoryParams {
start: format!("{}", start),
end: format!("{}", end),
limit: limit,
};

let data = self.client.post_signed_params(request, payload, &params)?;
let data = self.client.post_signed_params(request, payload, &params, false)?;

let entry: Vec<Entry> = from_str(data.as_str())?;

Expand Down
3 changes: 2 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ extern crate url;
extern crate serde_derive;

mod book;
mod client;
mod ticker;
mod trades;
mod orders;
mod account;
mod ledger;
mod auth;
mod client;

pub mod model;
pub mod candles;
pub mod api;
pub mod pairs;
Expand Down
137 changes: 137 additions & 0 deletions src/model.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use serde::{Serialize, Deserialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct Order {
pub id: i64,
pub group_id: Option<i32>,
pub client_id: i64,
pub symbol: String,
pub creation_timestamp: i64,
pub update_timestamp: i64,
pub amount: f64,
pub amount_original: f64,
pub order_type: String,
pub previous_order_type: Option<String>,

#[serde(skip_serializing)]
_placeholder_1: Option<String>,
#[serde(skip_serializing)]
_placeholder_2: Option<String>,

pub flags: Option<i32>,
pub order_status: Option<String>,

#[serde(skip_serializing)]
_placeholder_3: Option<String>,
#[serde(skip_serializing)]
_placeholder_4: Option<String>,

pub price: f64,
pub price_avg: f64,
pub price_trailing: Option<f64>,
pub price_aux_limit: Option<f64>,

#[serde(skip_serializing)]
__placeholder_5: Option<String>,
#[serde(skip_serializing)]
_placeholder_6: Option<String>,
#[serde(skip_serializing)]
_placeholder_7: Option<String>,

pub notify: i32,
pub hidden: i32,
pub placed_id: Option<i32>
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Trade {
pub id: i64,
pub group_id: Option<i32>,
pub client_id: i64,
pub symbol: String,
pub creation_timestamp: i64,
pub update_timestamp: i64,
pub amount: f64,
pub amount_original: f64,
pub order_type: String,
pub previous_order_type: Option<String>,
pub time_in_forse: Option<i64>,

#[serde(skip_serializing)]
_placeholder_1: Option<String>,

pub flags: Option<i32>,
pub order_status: String,

#[serde(skip_serializing)]
_placeholder_2: Option<String>,
#[serde(skip_serializing)]
_placeholder_3: Option<String>,

pub price: f64,
pub price_avg: f64,
pub price_trailing: Option<f64>,
pub price_aux_limit: Option<f64>,

#[serde(skip_serializing)]
_placeholder_4: Option<String>,
#[serde(skip_serializing)]
_placeholder_5: Option<String>,
#[serde(skip_serializing)]
_placeholder_6: Option<String>,

pub hidden: i32,
pub placed_id: Option<i32>,

#[serde(skip_serializing)]
_placeholder_7: Option<String>,
#[serde(skip_serializing)]
_placeholder_8: Option<String>,
#[serde(skip_serializing)]
_placeholder_9: Option<String>,

routing: Option<String>,

#[serde(skip_serializing)]
_placeholder_10: Option<String>,
#[serde(skip_serializing)]
_placeholder_11: Option<String>,

meta: Option<String>,
}


#[derive(Debug, Deserialize, Serialize)]
pub struct TradeResponse {
pub millisecond_timestamp: i64,
pub req_type: String,

pub message_id: Option<i32>,

#[serde(skip_serializing)]
pub _null: Option<()>,

pub order: Vec<Trade>,

pub code: Option<i64>,
pub status: String,
pub text: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct TradeCancelResponse {
pub millisecond_timestamp: i64,
pub req_type: String,

pub message_id: Option<i32>,

#[serde(skip_serializing)]
pub _null: Option<()>,

pub order: Trade,

pub code: Option<i64>,
pub status: String,
pub text: String,
}

102 changes: 58 additions & 44 deletions src/orders.rs
Original file line number Diff line number Diff line change
@@ -1,55 +1,23 @@
use client::*;
use errors::*;
use serde_json::from_str;
use model::*;
use serde_json::{from_str, to_string};
use std::collections::BTreeMap;

#[derive(Serialize, Deserialize)]
pub struct Order {
pub id: i64,
pub group_id: Option<i32>,
pub client_id: i64,
pub symbol: String,
pub creation_timestamp: i64,
pub update_timestamp: i64,
pub amount: f64,
pub amount_original: f64,
pub order_type: String,
pub previous_order_type: Option<String>,

#[serde(skip_serializing)]
_placeholder_1: Option<String>,
#[serde(skip_serializing)]
_placeholder_2: Option<String>,

pub flags: Option<i32>,
pub order_status: Option<String>,

#[serde(skip_serializing)]
_placeholder_3: Option<String>,
#[serde(skip_serializing)]
_placeholder_4: Option<String>,

pub price: f64,
pub price_avg: f64,
pub price_trailing: Option<f64>,
pub price_aux_limit: Option<f64>,

#[serde(skip_serializing)]
__placeholder_5: Option<String>,
#[serde(skip_serializing)]
_placeholder_6: Option<String>,
#[serde(skip_serializing)]
_placeholder_7: Option<String>,

pub notify: i32,
pub hidden: i32,
pub placed_id: Option<i32>
}
static ORDER_TYPE_LIMIT: &str = "EXCHANGE LIMIT";

#[derive(Clone)]
pub struct Orders {
client: Client,
}

struct OrderRequest {
pub order_type: String,
pub symbol: String,
pub amount: f64,
pub price: f64
}

impl Orders {
pub fn new(api_key: Option<String>, secret_key: Option<String>) -> Self {
Orders {
Expand Down Expand Up @@ -81,9 +49,55 @@ impl Orders {
where S: Into<String>
{
let data = self.client.post_signed(request.into(), payload.into())?;

let orders: Vec<Order> = from_str(data.as_str())?;

Ok(orders)
}
pub fn submit_order<S, F>(&self, symbol: S, qty: F, price: f64) -> Result<TradeResponse>
where
S: Into<String>,
F: Into<f64>,
{
let buy: OrderRequest = OrderRequest {
order_type: ORDER_TYPE_LIMIT.to_string(),
symbol: symbol.into(),
amount: qty.into(),
price,
};

let order = self.build_order(buy);
let payload = to_string(&order)?;

let data = self.client.post_signed_order("order/submit".into(), payload)?;
println!("DATA: {:?}", data.as_str());
let transaction: TradeResponse = from_str(data.as_str())?;

println!("Trans: {:?}", transaction);
Ok(transaction)
}

pub fn cancel_order(&self, order_id: i64) -> Result<TradeCancelResponse>
{
let mut parameters: BTreeMap<String, i64> = BTreeMap::new();
parameters.insert("id".into(), order_id);
let payload = to_string(&parameters)?;
let data = self.client.post_signed_order("order/cancel".into(), payload)?;
let order_canceled: TradeCancelResponse = from_str(data.as_str())?;

Ok(order_canceled)
}

fn build_order(&self, order: OrderRequest) -> BTreeMap<String, String> {
let mut order_parameters: BTreeMap<String, String> = BTreeMap::new();

order_parameters.insert("symbol".into(), order.symbol);
order_parameters.insert("type".into(), order.order_type);
order_parameters.insert("amount".into(), order.amount.to_string());

if order.price != 0.0 {
order_parameters.insert("price".into(), order.price.to_string());
}

order_parameters
}
}
Loading