Skip to content

Commit

Permalink
Merge pull request #2295 from bonomat/fix/dev-maker
Browse files Browse the repository at this point in the history
dev/app: A few fixes and clean-ups
  • Loading branch information
holzeis authored Mar 22, 2024
2 parents 860f6a2 + 59ecbf8 commit 37c99c9
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 77 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ jobs:
run: RUST_BACKTRACE=1 ${{ matrix.tests }} --nocapture --ignored
- name: Print maker logs on e2e tests error
if: failure()
run: docker logs maker
run: cat data/maker/regtest.log
- name: Print coordinator logs on e2e tests error
if: failure()
run: cat data/coordinator/regtest.log
Expand Down
1 change: 0 additions & 1 deletion crates/commons/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use bitcoin::secp256k1::PublicKey;
use rust_decimal::prelude::ToPrimitive;
use serde::Deserialize;
use serde::Serialize;

Expand Down
77 changes: 46 additions & 31 deletions crates/commons/src/price.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::order::Order;
use crate::order::OrderState;
use crate::ToPrimitive;
use rust_decimal::Decimal;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use time::OffsetDateTime;
use trade::ContractSymbol;
use trade::Direction;

Expand Down Expand Up @@ -33,38 +33,52 @@ pub fn best_current_price(current_orders: &[Order]) -> Prices {
prices
}

/// Best price (highest) of all long (buy) orders in the orderbook
fn best_bid_price(current_orders: &[Order], symbol: ContractSymbol) -> Option<Decimal> {
best_price_for(current_orders, Direction::Long, symbol)
}

/// Best price (lowest) of all short (sell) orders in the orderbook
fn best_ask_price(current_orders: &[Order], symbol: ContractSymbol) -> Option<Decimal> {
best_price_for(current_orders, Direction::Short, symbol)
/// If you place a market order to go short/sell, the best/highest `Bid` price
///
/// Differently said, remember `buy high`, `sell low`!
/// Ask = high
/// Bid = low
///
/// The best `Ask` is the lowest of all `Asks`
/// The best `Bid` is the highest of all `Bids`
///
/// If you SELL, you ask and you get the best price someone is willing to buy at i.e. the highest
/// bid price.
fn best_bid_price(orders: &[Order], symbol: ContractSymbol) -> Option<Decimal> {
orders
.iter()
.filter(|o| {
o.order_state == OrderState::Open
&& o.direction == Direction::Long
&& o.contract_symbol == symbol
&& o.expiry > OffsetDateTime::now_utc()
})
.map(|o| o.price)
.max()
}

fn best_price_for(
current_orders: &[Order],
direction: Direction,
symbol: ContractSymbol,
) -> Option<Decimal> {
assert_eq!(
symbol,
ContractSymbol::BtcUsd,
"only btcusd supported for now"
);
let use_max = direction == Direction::Long;
current_orders
/// If you place a market order to go long/buy, you get the best/lowest `Ask` price
///
/// Differently said, remember `buy high`, `sell low`!
/// Ask = high
/// Bid = low
///
/// The best `Ask` is the lowest of all `Asks`
/// The best `Bid` is the highest of all `Bids`
///
/// If you BUY, you bid and you get the best price someone is willing to sell at i.e. the lowest ask
/// price.
fn best_ask_price(orders: &[Order], symbol: ContractSymbol) -> Option<Decimal> {
orders
.iter()
.filter(|order| order.order_state == OrderState::Open && order.direction == direction)
.map(|order| order.price.to_f64().expect("to represent decimal as f64"))
// get the best price
.fold(None, |acc, x| match acc {
Some(y) => Some(if use_max { x.max(y) } else { x.min(y) }),
None => Some(x),
})?
.try_into()
.ok()
.filter(|o| {
o.order_state == OrderState::Open
&& o.direction == Direction::Short
&& o.contract_symbol == symbol
&& o.expiry > OffsetDateTime::now_utc()
})
.map(|o| o.price)
.min()
}

#[cfg(test)]
Expand All @@ -79,6 +93,7 @@ mod test {
use rust_decimal::Decimal;
use rust_decimal_macros::dec;
use std::str::FromStr;
use time::Duration;
use time::OffsetDateTime;
use trade::ContractSymbol;
use trade::Direction;
Expand All @@ -101,7 +116,7 @@ mod test {
quantity: 100.into(),
order_type: OrderType::Market,
timestamp: OffsetDateTime::now_utc(),
expiry: OffsetDateTime::now_utc(),
expiry: OffsetDateTime::now_utc() + Duration::minutes(1),
order_state,
order_reason: OrderReason::Manual,
stable: false,
Expand Down
71 changes: 50 additions & 21 deletions crates/dev-maker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ mod historic_rates;
mod logger;
mod orderbook_client;

const ORDER_EXPIRY: u64 = 30;

#[tokio::main]
async fn main() -> Result<()> {
init_tracing(LevelFilter::DEBUG)?;

let client = OrderbookClient::new(Url::from_str("http://localhost:8000/api/orderbook/orders")?);
let client = OrderbookClient::new(Url::from_str("http://localhost:8000")?);
let secret_key = SecretKey::new(&mut rand::thread_rng());
let public_key = secret_key.public_key(SECP256K1);

Expand All @@ -35,26 +37,49 @@ async fn main() -> Result<()> {
let mut historic_rates = historic_rates::read();
historic_rates.sort_by(|a, b| a.timestamp.cmp(&b.timestamp));

let mut past_ids = vec![];
loop {
for historic_rate in &historic_rates {
post_order(
client.clone(),
secret_key,
public_key,
Direction::Short,
historic_rate.open + Decimal::from(1),
)
.await;
post_order(
client.clone(),
secret_key,
public_key,
Direction::Long,
historic_rate.open + Decimal::from(1),
)
.await;
let mut tmp_ids = vec![];
for _ in 0..5 {
tmp_ids.push(
post_order(
client.clone(),
secret_key,
public_key,
Direction::Short,
historic_rate.open + Decimal::from(1),
ORDER_EXPIRY,
)
.await,
);
tmp_ids.push(
post_order(
client.clone(),
secret_key,
public_key,
Direction::Long,
historic_rate.open - Decimal::from(1),
ORDER_EXPIRY,
)
.await,
);
}

for old_id in &past_ids {
if let Err(err) = client.delete_order(old_id).await {
tracing::error!(
"Could not delete old order with id {old_id} because of {err:?}"
);
}
}

past_ids.clear();

past_ids.extend(tmp_ids);

sleep(Duration::from_secs(60)).await;
// we sleep a bit shorter than the last order expires to ensure always having an order
sleep(Duration::from_secs(ORDER_EXPIRY - 1)).await;
}
}
}
Expand All @@ -69,19 +94,22 @@ async fn post_order(
public_key: PublicKey,
direction: Direction,
price: Decimal,
) {
order_expiry_seconds: u64,
) -> Uuid {
let uuid = Uuid::new_v4();
if let Err(err) = client
.post_new_order(
NewOrder {
id: Uuid::new_v4(),
id: uuid,
contract_symbol: ContractSymbol::BtcUsd,
price,
quantity: Decimal::from(5000),
trader_id: public_key,
direction,
leverage: Decimal::from(2),
order_type: OrderType::Limit,
expiry: OffsetDateTime::now_utc() + time::Duration::minutes(1),
expiry: OffsetDateTime::now_utc()
+ time::Duration::seconds(order_expiry_seconds as i64),
stable: false,
},
None,
Expand All @@ -91,4 +119,5 @@ async fn post_order(
{
tracing::error!("Failed posting new order {err:?}");
}
uuid
}
24 changes: 23 additions & 1 deletion crates/dev-maker/src/orderbook_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use commons::NewOrderRequest;
use reqwest::Client;
use reqwest::Url;
use secp256k1::SecretKey;
use uuid::Uuid;

#[derive(Clone)]
pub struct OrderbookClient {
Expand All @@ -27,6 +28,8 @@ impl OrderbookClient {
channel_opening_params: Option<ChannelOpeningParams>,
secret_key: SecretKey,
) -> Result<()> {
let url = self.url.join("/api/orderbook/orders")?;

tracing::info!(
id = order.id.to_string(),
direction = order.direction.to_string(),
Expand All @@ -43,7 +46,7 @@ impl OrderbookClient {

let response = self
.client
.post(self.url.clone())
.post(url)
.json(&new_order_request)
.send()
.await?;
Expand All @@ -52,4 +55,23 @@ impl OrderbookClient {

Ok(())
}

pub async fn delete_order(&self, order_id: &Uuid) -> Result<()> {
tracing::debug!(
order_id = order_id.to_string(),
"Deleting order from orderbook"
);

let url = self.url.join(
format!("/api/orderbook/orders/{}", order_id)
.to_string()
.as_str(),
)?;

let response = self.client.delete(url).send().await?;

response.error_for_status()?;

Ok(())
}
}
60 changes: 38 additions & 22 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -292,17 +292,11 @@ coordinator args="":

cargo run --bin coordinator -- {{args}}

run_maker_args := if os() == "linux" {
"--network=\"host\" --pull always --name maker ghcr.io/get10101/aristides/aristides:main regtest --orderbook http://localhost:8000"
} else if os() == "macos" {
"--pull always --name maker ghcr.io/get10101/aristides/aristides:main regtest --orderbook http://host.docker.internal:8000"
} else {
"echo 'Only linux and macos are supported';
exit"
}

maker args="":
cargo run --bin dev-maker
#!/usr/bin/env bash
set -euxo pipefail
cargo run --bin dev-maker -- {{args}}

flutter-test:
cd mobile && fvm flutter pub run build_runner build --delete-conflicting-outputs && fvm flutter test
Expand Down Expand Up @@ -350,20 +344,18 @@ run-coordinator-detached:
just wait-for-coordinator-to-be-ready
echo "Coordinator successfully started. You can inspect the logs at {{coordinator_log_file}}"

run_maker_detached_args := if os() == "linux" {
"--network=\"host\" --pull always -d --name maker ghcr.io/get10101/aristides/aristides:main regtest --orderbook http://localhost:8000"
} else if os() == "macos" {
"--pull always -d --name maker ghcr.io/get10101/aristides/aristides:main regtest --orderbook http://host.docker.internal:8000"
} else {
"echo 'Only linux and macos are supported';
exit"
}

# Starts maker process in the background, piping logs to a file (used in other recipes)
run-maker-detached:
# we always delete the old container first as otherwise we might get an error if the container still exists
docker rm -f maker || true
docker run {{run_maker_detached_args}}
#!/usr/bin/env bash
set -euxo pipefail
echo "Building maker first"
cargo build --bin dev-maker
echo "Starting (and building) maker"

just maker &> {{maker_log_file}} &
just wait-for-maker-to-be-ready
echo "Maker successfully started. You can inspect the logs at {{maker_log_file}}"

# Attach to the current coordinator logs
coordinator-logs:
Expand Down Expand Up @@ -449,6 +441,30 @@ wait-for-coordinator-to-be-ready:
echo "Max attempts reached. Coordinator is still not ready."
exit 1

[private]
wait-for-maker-to-be-ready:
#!/usr/bin/env bash
set +e
MAX_RETRIES=30
RETRY_DELAY=2

for ((i=1; i<=$MAX_RETRIES; i++)); do
response=$(curl -s http://localhost:8000/api/orderbook/orders)
item_count=$(echo "$response" | jq '. | length')

if [[ $item_count -ge 2 ]]; then
echo "Request successful, found $item_count items."
exit 0
else
echo "Retry $i: Found $item_count items. Retrying in $RETRY_DELAY seconds..."
sleep $RETRY_DELAY
fi
done

echo "Maximum retries exceeded. Starting maker failed."
exit 1

build-ipa args="":
#!/usr/bin/env bash
BUILD_NUMBER=$(git rev-list HEAD --count)
Expand Down

0 comments on commit 37c99c9

Please sign in to comment.