Skip to content

Commit

Permalink
feat(Open API): add order and recent trades. (#294)
Browse files Browse the repository at this point in the history
  • Loading branch information
silathdiir authored Aug 30, 2021
1 parent 44a902e commit 48e0149
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 6 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ log = "0.4.14"
nix = "0.20.0"
num_enum = "0.5.1"
orchestra = { git = "https://github.com/fluidex/orchestra.git", branch = "master", features = [ "exchange" ] }
paperclip = { git = "https://github.com/fluidex/paperclip.git", features = ["actix"] }
paperclip = { git = "https://github.com/fluidex/paperclip.git", features = [ "actix", "chrono", "rust_decimal" ] }
qstring = "0.7.2"
rand = "0.8.3"
serde = { version = "1.0.124", features = [ "derive" ] }
Expand Down
5 changes: 4 additions & 1 deletion src/bin/openapi.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use actix_web::{App, HttpServer};
use dingir_exchange::openapi::public_history::{order_trades, recent_trades};
use dingir_exchange::openapi::user::get_user;
use dingir_exchange::restapi::state::{AppCache, AppState};
use fluidex_common::non_blocking_tracing;
Expand Down Expand Up @@ -48,7 +49,9 @@ async fn main() -> std::io::Result<()> {
.service(
web::scope("/openapi")
.route("/ping", web::get().to(ping))
.route("/user/{l1addr_or_l2pubkey}", web::get().to(get_user)),
.route("/user/{l1addr_or_l2pubkey}", web::get().to(get_user))
.route("/recenttrades/{market}", web::get().to(recent_trades))
.route("/ordertrades/{market}/{order_id}", web::get().to(order_trades)),
)
.with_json_spec_at("/api/spec")
.build()
Expand Down
1 change: 1 addition & 0 deletions src/openapi/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod public_history;
pub mod user;
114 changes: 114 additions & 0 deletions src/openapi/public_history.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
use crate::models::tablenames::{MARKETTRADE, USERTRADE};
use crate::models::{self, DecimalDbType, TimestampDbType};
use crate::restapi::errors::RpcError;
use crate::restapi::state::AppState;
use crate::restapi::types;
use chrono::{DateTime, SecondsFormat, Utc};
use core::cmp::min;
use paperclip::actix::api_v2_operation;
use paperclip::actix::web::{self, HttpRequest, Json};

fn check_market_exists(_market: &str) -> bool {
// TODO
true
}

#[api_v2_operation]
pub async fn recent_trades(req: HttpRequest, data: web::Data<AppState>) -> Result<Json<Vec<models::MarketTrade>>, actix_web::Error> {
let market = req.match_info().get("market").unwrap();
let qstring = qstring::QString::from(req.query_string());
let limit = min(100, qstring.get("limit").unwrap_or_default().parse::<usize>().unwrap_or(20));
log::debug!("recent_trades market {} limit {}", market, limit);
if !check_market_exists(market) {
return Err(RpcError::bad_request("invalid market").into());
}

// TODO: this API result should be cached, either in-memory or using redis

// Here we use the kline trade table, which is more market-centric
// and more suitable for fetching latest trades on a market.
// models::UserTrade is designed for a user to fetch his trades.

let sql_query = format!("select * from {} where market = $1 order by time desc limit {}", MARKETTRADE, limit);

let trades: Vec<models::MarketTrade> = match sqlx::query_as(&sql_query).bind(market).fetch_all(&data.db).await {
Ok(trades) => trades,
Err(error) => {
let error: RpcError = error.into();
return Err(error.into());
}
};

log::debug!("query {} recent_trades records", trades.len());
Ok(Json(trades))
}

#[derive(sqlx::FromRow, Debug, Clone)]
struct QueriedUserTrade {
pub time: TimestampDbType,
pub user_id: i32,
pub trade_id: i64,
pub order_id: i64,
pub price: DecimalDbType,
pub amount: DecimalDbType,
pub quote_amount: DecimalDbType,
pub fee: DecimalDbType,
}

#[cfg(sqlxverf)]
fn sqlverf_ticker() -> impl std::any::Any {
sqlx::query_as!(
QueriedUserTrade,
"select time, user_id, trade_id, order_id,
price, amount, quote_amount, fee
from user_trade where market = $1 and order_id = $2
order by trade_id, time asc",
"USDT_ETH",
10000,
)
}

#[api_v2_operation]
pub async fn order_trades(
app_state: web::Data<AppState>,
path: web::Path<(String, i64)>,
) -> Result<Json<types::OrderTradeResult>, actix_web::Error> {
let (market_name, order_id): (String, i64) = path.into_inner();
log::debug!("order_trades market {} order_id {}", market_name, order_id);

let sql_query = format!(
"
select time, user_id, trade_id, order_id,
price, amount, quote_amount, fee
from {} where market = $1 and order_id = $2
order by trade_id, time asc",
USERTRADE
);

let trades: Vec<QueriedUserTrade> = match sqlx::query_as(&sql_query)
.bind(market_name)
.bind(order_id)
.fetch_all(&app_state.db)
.await
{
Ok(trades) => trades,
Err(error) => {
let error: RpcError = error.into();
return Err(error.into());
}
};

Ok(Json(types::OrderTradeResult {
trades: trades
.into_iter()
.map(|v| types::MarketTrade {
trade_id: v.trade_id,
time: DateTime::<Utc>::from_utc(v.time, Utc).to_rfc3339_opts(SecondsFormat::Secs, true),
amount: v.amount.to_string(),
quote_amount: v.quote_amount.to_string(),
price: v.price.to_string(),
fee: v.fee.to_string(),
})
.collect(),
}))
}
5 changes: 3 additions & 2 deletions src/restapi/types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::config::{Asset, Market};
use paperclip::actix::Apiv2Schema;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug)]
Expand Down Expand Up @@ -42,7 +43,7 @@ pub struct TickerResult {
pub to: u64,
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Apiv2Schema)]
pub struct MarketTrade {
pub time: String,
pub trade_id: i64,
Expand All @@ -52,7 +53,7 @@ pub struct MarketTrade {
pub fee: String,
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Apiv2Schema)]
pub struct OrderTradeResult {
pub trades: Vec<MarketTrade>,
}
Expand Down
2 changes: 1 addition & 1 deletion src/storage/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ pub struct SliceHistory {
pub end_trade_id: i64,
}

#[derive(sqlx::FromRow, Debug, Clone, Serialize)]
#[derive(sqlx::FromRow, Debug, Clone, Serialize, Deserialize, Apiv2Schema)]
pub struct MarketTrade {
pub time: TimestampDbType,
pub market: String,
Expand Down
3 changes: 2 additions & 1 deletion src/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use num_enum::TryFromPrimitive;
use paperclip::actix::Apiv2Schema;
use serde::{Deserialize, Serialize};

pub type SimpleResult = anyhow::Result<()>;
Expand All @@ -14,7 +15,7 @@ pub enum MarketRole {
// It seems we don't need varchar(n), text is enough?
// https://github.com/launchbadge/sqlx/issues/237#issuecomment-610696905 must use 'varchar'!!!
// text is more readable than #[repr(i16)] and TryFromPrimitive
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, sqlx::Type)]
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, sqlx::Type, Apiv2Schema)]
#[sqlx(type_name = "varchar")]
#[sqlx(rename_all = "lowercase")]
pub enum OrderSide {
Expand Down

0 comments on commit 48e0149

Please sign in to comment.