Skip to content

Commit

Permalink
Merge pull request #110 from calumrussell/106-implement-cancel-modify
Browse files Browse the repository at this point in the history
106 implement cancel modify
  • Loading branch information
calumrussell authored Nov 1, 2024
2 parents 915c52c + 607f27a commit 1bb95be
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 14 deletions.
68 changes: 65 additions & 3 deletions rotala-client/src/client/uist_v2.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use anyhow::{Error, Result};
use reqwest;
use rotala::exchange::uist_v2::Order;
use rotala::exchange::uist_v2::{Order, OrderId};
use rotala::input::athena::Athena;
use rotala_http::http::uist_v2::{
AppState, BacktestId, Client, FetchDepthResponse, FetchQuotesResponse, InfoResponse,
InitResponse, InsertOrderRequest, NowResponse, TickResponse, UistV2Error,
AppState, BacktestId, CancelOrderRequest, Client, FetchDepthResponse, FetchQuotesResponse,
InfoResponse, InitResponse, InsertOrderRequest, ModifyOrderRequest, NowResponse, TickResponse,
UistV2Error,
};
use std::future::{self, Future};

Expand Down Expand Up @@ -37,6 +38,38 @@ impl Client for HttpClient {
.await?)
}

async fn modify_order(
&mut self,
order_id: OrderId,
quantity_change: f64,
backtest_id: BacktestId,
) -> Result<()> {
let req = ModifyOrderRequest {
order_id,
quantity_change,
};
Ok(self
.client
.post(self.path.clone() + format!("/backtest/{backtest_id}/modify_order").as_str())
.json(&req)
.send()
.await?
.json::<()>()
.await?)
}

async fn cancel_order(&mut self, order_id: OrderId, backtest_id: BacktestId) -> Result<()> {
let req = CancelOrderRequest { order_id };
Ok(self
.client
.post(self.path.clone() + format!("/backtest/{backtest_id}/cancel_order").as_str())
.json(&req)
.send()
.await?
.json::<()>()
.await?)
}

async fn fetch_quotes(&mut self, backtest_id: BacktestId) -> Result<FetchQuotesResponse> {
Ok(self
.client
Expand Down Expand Up @@ -113,6 +146,7 @@ impl Client for TestClient {
fn tick(&mut self, backtest_id: BacktestId) -> impl Future<Output = Result<TickResponse>> {
if let Some(resp) = self.state.tick(backtest_id) {
future::ready(Ok(TickResponse {
modified_orders: resp.3,
inserted_orders: resp.2,
executed_trades: resp.1,
has_next: resp.0,
Expand All @@ -134,6 +168,34 @@ impl Client for TestClient {
}
}

fn modify_order(
&mut self,
order_id: OrderId,
quantity_change: f64,
backtest_id: BacktestId,
) -> impl Future<Output = Result<()>> {
if let Some(()) = self
.state
.modify_order(order_id, quantity_change, backtest_id)
{
future::ready(Ok(()))
} else {
future::ready(Err(Error::new(UistV2Error::UnknownBacktest)))
}
}

fn cancel_order(
&mut self,
order_id: OrderId,
backtest_id: BacktestId,
) -> impl Future<Output = Result<()>> {
if let Some(()) = self.state.cancel_order(order_id, backtest_id) {
future::ready(Ok(()))
} else {
future::ready(Err(Error::new(UistV2Error::UnknownBacktest)))
}
}

fn fetch_quotes(
&mut self,
backtest_id: BacktestId,
Expand Down
2 changes: 2 additions & 0 deletions rotala-http/src/bin/uist_server_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ async fn main() -> std::io::Result<()> {
.service(fetch_depth)
.service(tick)
.service(insert_order)
.service(modify_order)
.service(cancel_order)
})
.bind((address, port))?
.run()
Expand Down
96 changes: 91 additions & 5 deletions rotala-http/src/http/uist_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ use std::sync::Mutex;
use anyhow::Result;
use serde::{Deserialize, Serialize};

use rotala::exchange::uist_v2::{InnerOrder, Order, Trade, UistV2};
use rotala::exchange::uist_v2::{InnerOrder, ModifyResult, Order, OrderId, Trade, UistV2};
use rotala::input::athena::{Athena, DateBBO, DateDepth};

pub type BacktestId = u64;
pub type TickResponseType = (bool, Vec<Trade>, Vec<InnerOrder>, Vec<ModifyResult>);

pub struct BacktestState {
pub id: BacktestId,
Expand Down Expand Up @@ -56,15 +57,18 @@ impl AppState {
}
}

pub fn tick(&mut self, backtest_id: BacktestId) -> Option<(bool, Vec<Trade>, Vec<InnerOrder>)> {
pub fn tick(&mut self, backtest_id: BacktestId) -> Option<TickResponseType> {
if let Some(backtest) = self.backtests.get_mut(&backtest_id) {
if let Some(dataset) = self.datasets.get(&backtest.dataset_name) {
let mut has_next = false;
//TODO: this has perf implications, not quite sure why memory is being created here
let mut executed_trades = Vec::new();
let mut inserted_orders = Vec::new();
let mut modified_orders = Vec::new();

if let Some(quotes) = dataset.get_quotes(&backtest.date) {
let mut res = backtest.exchange.tick(quotes, backtest.date);
modified_orders.append(&mut res.2);
executed_trades.append(&mut res.0);
inserted_orders.append(&mut res.1);
}
Expand All @@ -75,7 +79,7 @@ impl AppState {
backtest.date = *dataset.get_date(new_pos).unwrap();
}
backtest.pos = new_pos;
return Some((has_next, executed_trades, inserted_orders));
return Some((has_next, executed_trades, inserted_orders, modified_orders));
}
}
None
Expand Down Expand Up @@ -125,6 +129,27 @@ impl AppState {
None
}

pub fn modify_order(
&mut self,
order_id: OrderId,
quantity_change: f64,
backtest_id: BacktestId,
) -> Option<()> {
if let Some(backtest) = self.backtests.get_mut(&backtest_id) {
backtest.exchange.modify_order(order_id, quantity_change);
return Some(());
}
None
}

pub fn cancel_order(&mut self, order_id: OrderId, backtest_id: BacktestId) -> Option<()> {
if let Some(backtest) = self.backtests.get_mut(&backtest_id) {
backtest.exchange.cancel_order(order_id);
return Some(());
}
None
}

pub fn new_backtest(&mut self, dataset_name: &str) -> Option<BacktestId> {
let new_id = self.last + 1;

Expand Down Expand Up @@ -154,13 +179,25 @@ pub struct TickResponse {
pub has_next: bool,
pub executed_trades: Vec<Trade>,
pub inserted_orders: Vec<InnerOrder>,
pub modified_orders: Vec<ModifyResult>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct InsertOrderRequest {
pub order: Order,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct ModifyOrderRequest {
pub order_id: OrderId,
pub quantity_change: f64,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct CancelOrderRequest {
pub order_id: OrderId,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct FetchQuotesResponse {
pub quotes: DateBBO,
Expand Down Expand Up @@ -221,6 +258,17 @@ pub trait Client {
order: Order,
backtest_id: BacktestId,
) -> impl Future<Output = Result<()>>;
fn modify_order(
&mut self,
order_id: OrderId,
quantity_change: f64,
backtest_id: BacktestId,
) -> impl Future<Output = Result<()>>;
fn cancel_order(
&mut self,
order_id: OrderId,
backtest_id: BacktestId,
) -> impl Future<Output = Result<()>>;
fn fetch_quotes(
&mut self,
backtest_id: BacktestId,
Expand All @@ -240,8 +288,9 @@ pub mod server {
use actix_web::{get, post, web};

use super::{
BacktestId, FetchDepthResponse, FetchQuotesResponse, InfoResponse, InitResponse,
InsertOrderRequest, NowResponse, TickResponse, UistState, UistV2Error,
BacktestId, CancelOrderRequest, FetchDepthResponse, FetchQuotesResponse, InfoResponse,
InitResponse, InsertOrderRequest, ModifyOrderRequest, NowResponse, TickResponse, UistState,
UistV2Error,
};

#[get("/backtest/{backtest_id}/tick")]
Expand All @@ -254,6 +303,7 @@ pub mod server {

if let Some(result) = uist.tick(backtest_id) {
Ok(web::Json(TickResponse {
modified_orders: result.3,
inserted_orders: result.2,
executed_trades: result.1,
has_next: result.0,
Expand All @@ -278,6 +328,40 @@ pub mod server {
}
}

#[post("/backtest/{backtest_id}/modify_order")]
pub async fn modify_order(
app: web::Data<UistState>,
path: web::Path<(BacktestId,)>,
modify_order: web::Json<ModifyOrderRequest>,
) -> Result<web::Json<()>, UistV2Error> {
let mut uist = app.lock().unwrap();
let (backtest_id,) = path.into_inner();
if let Some(()) = uist.modify_order(
modify_order.order_id,
modify_order.quantity_change,
backtest_id,
) {
Ok(web::Json(()))
} else {
Err(UistV2Error::UnknownBacktest)
}
}

#[post("/backtest/{backtest_id}/cancel_order")]
pub async fn cancel_order(
app: web::Data<UistState>,
path: web::Path<(BacktestId,)>,
cancel_order: web::Json<CancelOrderRequest>,
) -> Result<web::Json<()>, UistV2Error> {
let mut uist = app.lock().unwrap();
let (backtest_id,) = path.into_inner();
if let Some(()) = uist.cancel_order(cancel_order.order_id, backtest_id) {
Ok(web::Json(()))
} else {
Err(UistV2Error::UnknownBacktest)
}
}

#[get("/backtest/{backtest_id}/fetch_quotes")]
pub async fn fetch_quotes(
app: web::Data<UistState>,
Expand Down Expand Up @@ -402,6 +486,8 @@ mod tests {
.service(fetch_depth)
.service(tick)
.service(insert_order)
.service(modify_order)
.service(cancel_order)
.service(now),
)
.await;
Expand Down
24 changes: 24 additions & 0 deletions rotala-python/src/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,30 @@ def insert_order(self, order):
)
return r.json()

def modify_order(self, order_id, quantity_change):
if self.backtest_id is None:
raise ValueError("Called before init")

val = f'{{"order_id": {order_id}, "quantity_change": {quantity_change}'
r = requests.post(
f"{self.base_url}/backtest/{self.backtest_id}/modify_order",
data=val,
headers={"Content-type": "application/json"},
)
return r.json()

def cancel_order(self, order_id):
if self.backtest_id is None:
raise ValueError("Called before init")

val = f'{{"order_id": {order_id}'
r = requests.post(
f"{self.base_url}/backtest/{self.backtest_id}/cancel_order",
data=val,
headers={"Content-type": "application/json"},
)
return r.json()

def fetch_quotes(self):
if self.backtest_id is None:
raise ValueError("Called before init")
Expand Down
1 change: 1 addition & 0 deletions rotala/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ csv = "1.1.6"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
tokio = { version = "1.35.1", features = ["full"] }
anyhow = "1.0.91"

[dev-dependencies]
criterion = { version = "0.5.1", features = ["async_tokio"] }
Expand Down
Loading

0 comments on commit 1bb95be

Please sign in to comment.