Skip to content

Commit

Permalink
feat: Show loading screen when app starts with and expired position
Browse files Browse the repository at this point in the history
This commit introduces the order reason and the force match. Basically the idea is that we have certain orders that will have to get created by the orderbook (on behalf of the user like expiry, liquidation, take profit and stop loss). Those orders are matched automatically without the users intervention. Once matched we inform the user about the match for execution. If the user is online the trade will be automatically executed if not, the execution is delayed until the user comes online. Note, this match can still expire.
  • Loading branch information
holzeis committed Sep 9, 2023
1 parent 5229895 commit 837b9d6
Show file tree
Hide file tree
Showing 33 changed files with 611 additions and 115 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

- Find match on an expired position
- Show loading screen when app starts with an expired position

## [1.2.6] - 2023-09-06

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- This file should undo anything in `up.sql`
ALTER TABLE orders
DROP COLUMN "order_reason";

DROP TYPE "OrderReason_Type";
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Your SQL goes here
CREATE TYPE "OrderReason_Type" AS ENUM (
'Manual',
'Expired'
);

ALTER TABLE "orders"
ADD COLUMN "order_reason" "OrderReason_Type" NOT NULL DEFAULT 'Manual';
2 changes: 2 additions & 0 deletions coordinator/src/node/expired_positions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use orderbook_commons::Match;
use orderbook_commons::MatchState;
use orderbook_commons::NewOrder;
use orderbook_commons::Order;
use orderbook_commons::OrderReason;
use orderbook_commons::OrderState;
use orderbook_commons::OrderType;
use rust_decimal::prelude::ToPrimitive;
Expand Down Expand Up @@ -95,6 +96,7 @@ pub async fn close(node: Node, trading_sender: mpsc::Sender<TradingMessage>) ->
let (sender, mut receiver) = mpsc::channel::<Result<Order>>(1);
let message = TradingMessage::NewOrder(NewOrderMessage {
new_order: new_order.clone(),
order_reason: OrderReason::Expired,
sender,
});

Expand Down
39 changes: 39 additions & 0 deletions coordinator/src/orderbook/db/custom_types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::schema::sql_types::DirectionType;
use crate::schema::sql_types::MatchStateType;
use crate::schema::sql_types::OrderReasonType;
use crate::schema::sql_types::OrderStateType;
use crate::schema::sql_types::OrderTypeType;
use diesel::deserialize;
Expand Down Expand Up @@ -134,6 +135,44 @@ impl FromSql<OrderStateType, Pg> for OrderState {
}
}

#[derive(Debug, Clone, Copy, PartialEq, FromSqlRow, AsExpression)]
#[diesel(sql_type = OrderReasonType)]
pub(crate) enum OrderReason {
/// The order has been created manually by the user.
Manual,
/// The order has been create automatically as the position expired.
Expired,
}

impl QueryId for OrderReasonType {
type QueryId = OrderReasonType;
const HAS_STATIC_QUERY_ID: bool = false;

fn query_id() -> Option<TypeId> {
None
}
}

impl ToSql<OrderReasonType, Pg> for OrderReason {
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
match *self {
OrderReason::Manual => out.write_all(b"Manual")?,
OrderReason::Expired => out.write_all(b"Expired")?,
}
Ok(IsNull::No)
}
}

impl FromSql<OrderReasonType, Pg> for OrderReason {
fn from_sql(bytes: PgValue<'_>) -> deserialize::Result<Self> {
match bytes.as_bytes() {
b"Manual" => Ok(OrderReason::Manual),
b"Expired" => Ok(OrderReason::Expired),
_ => Err("Unrecognized enum variant".into()),
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, FromSqlRow, AsExpression)]
#[diesel(sql_type = MatchStateType)]
pub(crate) enum MatchState {
Expand Down
36 changes: 34 additions & 2 deletions coordinator/src/orderbook/db/orders.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::db::positions::ContractSymbol;
use crate::orderbook::db::custom_types::Direction;
use crate::orderbook::db::custom_types::OrderReason;
use crate::orderbook::db::custom_types::OrderState;
use crate::orderbook::db::custom_types::OrderType;
use crate::schema::orders;
Expand All @@ -9,6 +10,7 @@ use diesel::result::QueryResult;
use diesel::PgConnection;
use orderbook_commons::NewOrder as OrderbookNewOrder;
use orderbook_commons::Order as OrderbookOrder;
use orderbook_commons::OrderReason as OrderBookOrderReason;
use orderbook_commons::OrderState as OrderBookOrderState;
use orderbook_commons::OrderType as OrderBookOrderType;
use rust_decimal::prelude::FromPrimitive;
Expand Down Expand Up @@ -93,6 +95,7 @@ struct Order {
pub order_state: OrderState,
pub contract_symbol: ContractSymbol,
pub leverage: f32,
pub order_reason: OrderReason,
}

impl From<Order> for OrderbookOrder {
Expand All @@ -111,6 +114,25 @@ impl From<Order> for OrderbookOrder {
timestamp: value.timestamp,
expiry: value.expiry,
order_state: value.order_state.into(),
order_reason: value.order_reason.into(),
}
}
}

impl From<OrderReason> for OrderBookOrderReason {
fn from(value: OrderReason) -> Self {
match value {
OrderReason::Manual => OrderBookOrderReason::Manual,
OrderReason::Expired => OrderBookOrderReason::Expired,
}
}
}

impl From<OrderBookOrderReason> for OrderReason {
fn from(value: OrderBookOrderReason) -> Self {
match value {
OrderBookOrderReason::Manual => OrderReason::Manual,
OrderBookOrderReason::Expired => OrderReason::Expired,
}
}
}
Expand All @@ -126,6 +148,7 @@ struct NewOrder {
pub quantity: f32,
pub order_type: OrderType,
pub expiry: OffsetDateTime,
pub order_reason: OrderReason,
pub contract_symbol: ContractSymbol,
pub leverage: f32,
}
Expand All @@ -149,6 +172,7 @@ impl From<OrderbookNewOrder> for NewOrder {
.expect("To be able to convert decimal to f32"),
order_type: value.order_type.into(),
expiry: value.expiry,
order_reason: OrderReason::Manual,
contract_symbol: value.contract_symbol.into(),
leverage: value.leverage,
}
Expand Down Expand Up @@ -211,9 +235,17 @@ pub fn get_all_orders(
}

/// Returns the number of affected rows: 1.
pub fn insert(conn: &mut PgConnection, order: OrderbookNewOrder) -> QueryResult<OrderbookOrder> {
pub fn insert(
conn: &mut PgConnection,
order: OrderbookNewOrder,
order_reason: OrderBookOrderReason,
) -> QueryResult<OrderbookOrder> {
let new_order = NewOrder {
order_reason: OrderReason::from(order_reason),
..NewOrder::from(order)
};
let order: Order = diesel::insert_into(orders::table)
.values(NewOrder::from(order))
.values(new_order)
.get_result(conn)?;

Ok(OrderbookOrder::from(order))
Expand Down
7 changes: 6 additions & 1 deletion coordinator/src/orderbook/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use diesel::r2d2::PooledConnection;
use diesel::PgConnection;
use orderbook_commons::NewOrder;
use orderbook_commons::Order;
use orderbook_commons::OrderReason;
use orderbook_commons::OrderbookMsg;
use serde::de;
use serde::Deserialize;
Expand Down Expand Up @@ -93,7 +94,11 @@ pub async fn post_order(
) -> Result<Json<Order>, AppError> {
let (sender, mut receiver) = mpsc::channel::<Result<Order>>(1);

let message = TradingMessage::NewOrder(NewOrderMessage { new_order, sender });
let message = TradingMessage::NewOrder(NewOrderMessage {
new_order,
order_reason: OrderReason::Manual,
sender,
});
state.trading_sender.send(message).await.map_err(|e| {
AppError::InternalServerError(format!("Failed to send new order message: {e:#}"))
})?;
Expand Down
4 changes: 4 additions & 0 deletions coordinator/src/orderbook/tests/sample_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::orderbook::tests::setup_db;
use crate::orderbook::tests::start_postgres;
use bitcoin::secp256k1::PublicKey;
use orderbook_commons::NewOrder;
use orderbook_commons::OrderReason;
use orderbook_commons::OrderType;
use rust_decimal_macros::dec;
use std::str::FromStr;
Expand All @@ -28,6 +29,7 @@ async fn crud_test() {
let order = orders::insert(
&mut conn,
dummy_order(OffsetDateTime::now_utc() + Duration::minutes(1)),
OrderReason::Manual,
)
.unwrap();

Expand Down Expand Up @@ -56,11 +58,13 @@ async fn test_filter_expired_orders() {
let order = orders::insert(
&mut conn,
dummy_order(OffsetDateTime::now_utc() + Duration::minutes(1)),
OrderReason::Manual,
)
.unwrap();
let _ = orders::insert(
&mut conn,
dummy_order(OffsetDateTime::now_utc() - Duration::minutes(1)),
OrderReason::Manual,
)
.unwrap();

Expand Down
Loading

0 comments on commit 837b9d6

Please sign in to comment.