Skip to content

Commit

Permalink
add broker
Browse files Browse the repository at this point in the history
  • Loading branch information
ryqdev committed May 28, 2024
1 parent c328bb0 commit 41742d6
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 105 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ All in one trading engine
- [ ] Multiple strategy
- [ ] Customed backtest time range
- [x] Backtest UI
- [ ] Indicators

### Paper trading & live trading
- [x] Single live broker: IBKR
Expand Down
14 changes: 3 additions & 11 deletions src/cmds/backtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,18 @@ impl Command for BackTestCommand {

async fn handler(m: &ArgMatches) -> Result<()> {
let symbol = m.get_one::<String>("symbol").unwrap();
log::info!("Handle backtest {symbol}");
log::info!("Backtest {symbol}");
backtest(symbol).await?;
Ok(())
}

}

async fn backtest(symbol: &str) -> Result<()> {
log::info!("Backtesting {symbol}...");
let cash = 10_000.0;
let mut green = Green::new()
.add_data_feed(symbol)
// .add_broker(100_000.0)
.add_strategy(SimpleStrategy {
name: "simple".to_string(),
cash: Vec::from([cash]),
position: Vec::from([0.0]),
net_assets: Vec::from([cash]),
order: vec![],
})
.add_broker(100_000.0)
.add_strategy(SimpleStrategy{})
.build();

green.run();
Expand Down
10 changes: 5 additions & 5 deletions src/cmds/paper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,10 @@ async fn paper_trading(){


let contract = Contract {
symbol: "USD".to_string(),
symbol: "USD".to_owned(),
security_type: SecurityType::ForexPair,
currency: "JPY".to_string(),
exchange: "IDEALPRO".to_string(),
currency: "JPY".to_owned(),
exchange: "IDEALPRO".to_owned(),
..Default::default()
};

Expand All @@ -99,9 +99,9 @@ async fn paper_trading(){
}

let action = if bar.close > channel.high() {
Action::Buy
} else if bar.close < channel.low() {
Action::Sell
} else if bar.close < channel.low() {
Action::Buy
} else {
continue;
};
Expand Down
2 changes: 2 additions & 0 deletions src/green/broker/backtest.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::green::strategy::hold::Order;
use super::Broker;

#[derive(Default, Clone, Debug)]
pub struct BackTestBroker{
pub(crate) cash: Vec<f64>,
pub position: Vec<f64>,
pub(crate) net_assets: Vec<f64>,
pub order: Vec<Order>
}

// impl Broker for BackTestBroker {
Expand Down
77 changes: 39 additions & 38 deletions src/green/green.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,23 +12,23 @@ use crate::green::{
broker::backtest::BackTestBroker,
analyzer::Analyzer
};
use crate::green::strategy::hold::SimpleStrategy;
use crate::green::strategy::hold::{Order, SimpleStrategy};
use crate::green::visualization;


#[derive(Default)]
pub struct Green {
data: Vec<Vec<f64>>,
strategy: SimpleStrategy,
// broker: BackTestBroker,
broker: BackTestBroker,
}


#[derive(Default)]
pub struct GreenBuilder {
data: Vec<Vec<f64>>,
strategy: SimpleStrategy,
// broker: BackTestBroker
broker: BackTestBroker
}

impl Green {
Expand All @@ -40,33 +40,36 @@ impl Green {
pub fn run(&mut self) {
log::info!("Running {:?}...", self.strategy);
for bar in self.data.iter() {
// log::info!("\x1b[93m bar:\x1b[0m {:?} ", bar);
self.strategy.next(bar);
// let action = Action::Buy;
//
// // TODO: build the client
// let order_id = client.next_order_id();
// let order = order_builder::market_order(action, 1000.0);
//
// let notices = client.place_order(order_id, &contract, &order).unwrap();
// for notice in notices {
// if let OrderNotification::ExecutionData(data) = notice {
// println!("{} {} shares of {}", data.execution.side, data.execution.shares, data.contract.symbol);
// } else {
// println!("{:?}", notice);
// }
// }
let order = self.strategy.next(bar);
let cash = self.broker.cash.last().unwrap();
let position = self.broker.position.last().unwrap();
let price = bar[3];
// TODO: Action is not the struct I defined
match order.action {
Action::Buy => {
self.broker.cash.push(cash - order.size * price);
self.broker.position.push(position + order.size);
self.broker.net_assets.push(self.broker.cash.last().unwrap() + self.broker.position.last().unwrap() * price);
self.broker.order.push(order)
}
Action::Sell => {
self.broker.cash.push(cash + order.size * price);
self.broker.position.push(position - order.size);
self.broker.net_assets.push(self.broker.cash.last().unwrap() + self.broker.position.last().unwrap() * price);
self.broker.order.push(order)
}
_ => {
todo!()
}
}
}
log::info!("{}", self.strategy.cash.last().unwrap());
log::info!("{}", self.strategy.position.last().unwrap());
log::info!("{}", self.strategy.net_assets.last().unwrap());
}
pub fn plot(&self) {
log::info!("Ploting {:?}...", self.strategy.name);
log::info!("Plotting {:?}...", self.strategy);
let candle_data = self.data.clone();
let cash_data = self.strategy.cash.clone();
let net_asset_data = self.strategy.net_assets.clone();
let order_data = self.strategy.order.clone();
let cash_data = self.broker.cash.clone();
let net_asset_data = self.broker.net_assets.clone();
let order_data = self.broker.order.clone();

let native_options = eframe::NativeOptions::default();
eframe::run_native(
Expand All @@ -80,9 +83,6 @@ impl Green {
})),
).expect("Plotting error");
}
// fn run_strategy(&self){
// self.broker.set_cash(XXX)
// }
}

// TODO: add more types in HistoricalData
Expand All @@ -108,14 +108,15 @@ impl GreenBuilder{
self.data = finance_data;
self
}
// pub fn add_broker(&mut self, cash: f64) -> &mut GreenBuilder {
// self.broker = BackTestBroker{
// cash: Vec::from([cash]),
// position: Vec::from([0.0]),
// net_assets: Vec::from([cash]),
// };
// self
// }
pub fn add_broker(&mut self, cash: f64) -> &mut GreenBuilder {
self.broker = BackTestBroker{
cash: Vec::from([cash]),
position: Vec::from([0.0]),
net_assets: Vec::from([cash]),
order: vec![],
};
self
}
pub fn add_strategy(&mut self, strategy: SimpleStrategy) -> &mut GreenBuilder{
self.strategy = strategy;
self
Expand All @@ -127,7 +128,7 @@ impl GreenBuilder{
Box::new(Green{
data: self.data.clone(),
strategy: self.strategy.clone(),
// broker: self.broker.clone()
broker: self.broker.clone()
})
}
}
61 changes: 22 additions & 39 deletions src/green/strategy/hold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,57 +2,40 @@ use crate::green::broker::backtest::BackTestBroker;
use crate::green::green::Green;
use crate::green::strategy::Strategy;

#[derive(Debug, Default, Copy, Clone)]
pub enum Action {
#[default]
None,
Buy,
Sell,
}


#[derive(Default, Clone, Debug)]
pub(crate) struct Order {
pub(crate) symbol: String,
pub action: Action,
pub(crate) size: f64,
}

#[derive(Default, Clone, Debug)]
pub struct SimpleStrategy {
// broker: BackTestBroker,
pub(crate) name: String,
pub(crate) cash: Vec<f64>,
pub position: Vec<f64>,
pub(crate) net_assets: Vec<f64>,
pub order: Vec<Order>
}
pub struct SimpleStrategy {}

impl Strategy for SimpleStrategy {
fn next(&mut self, data: &Vec<f64>) {
fn next(&mut self, data: &Vec<f64>) -> Order {
let open_price = data[0];
let close_price = data[3];
if close_price > open_price {
self.buy(1.0, close_price);
log::info!("buy");
Order{
action: Action::Buy,
size: 1.0
}
} else {
self.sell(1.0, close_price);
log::info!("sell");
Order{
action: Action::Sell,
size: 1.0
}
}
}

fn buy(&mut self, size: f64, price: f64) {
log::info!("buy");
let cash = self.cash.last().unwrap();
let position = self.position.last().unwrap();
self.cash.push(cash - size * price);
self.position.push(position + size);
self.net_assets.push(self.cash.last().unwrap() + self.position.last().unwrap() * price);
self.order.push(Order{
symbol: self.name.to_owned(),
size
});
}

fn sell(&mut self, size: f64, price: f64) {
log::info!("sell");
let cash = self.cash.last().unwrap();
let position = self.position.last().unwrap();
self.cash.push(cash + size * price);
self.position.push(position - size);
self.net_assets.push(self.cash.last().unwrap() + self.position.last().unwrap() * price);
self.order.push(Order{
symbol: self.name.to_owned(),
size
});
}

}
10 changes: 0 additions & 10 deletions src/green/strategy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
pub mod hold;

use crate::green::broker::backtest::BackTestBroker;
use crate::green::broker::Broker;

pub trait Strategy {
fn next(&mut self, _: &Vec<f64>);

fn buy(&mut self, size: f64, price: f64);

fn sell(&mut self, size: f64, price: f64);

// fn update_broker(&self, broker: &mut BackTestBroker){
// broker.set_cash(self.cash);
// }

}
2 changes: 1 addition & 1 deletion src/green/visualization/candle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ impl eframe::App for App {
ui.label("Orders:");
// for order in &self.order_data {
// ui.horizontal(|ui| {
// ui.label(order.size.to_string());
// ui.label(order.size.to_owned());
// });
// }
});
Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ fn init_log() {
"{}:{} {} [{}] - {}",
record.file().unwrap_or("unknown_file"),
record.line().unwrap_or(0),
chrono::Local::now().format("%Y-%m-%dT%H:%M:%S").to_string().blue(),
chrono::Local::now().format("%Y-%m-%dT%H:%M:%S").to_owned().blue(),
record.level(),
record.args()
)
Expand Down

0 comments on commit 41742d6

Please sign in to comment.