Skip to content
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

106 implement cancel modify #110

Merged
merged 13 commits into from
Nov 1, 2024
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
Loading