Repository: yasstake/rbot (language: python)

Issue:
約定ログの実装
セッションオブジェクトから約定時にログファイルを生成する。
　・ファイル名：　Agent名ータイムスタンプ.txt
　・内容：　約定したOrderオブジェクトをJSON化したもの（CANCELオーダーは別途検討）
　・その他：タイムスタンプは、μs以外にISO形式も入れておく。

　{"event":" Execute", "timeus": 1600015, "time": "2023-09-10T11:00Z" , order: {xxxxxxxx}}

 こんなイメージ
Binance用DryRunner開発
まずはBinance用に一筆とおず

その後取引所を展開する。
Binance用DryRunner開発
まずはBinance用に一筆とおず

その後取引所を展開する。


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index cee26156..f3183e77 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -14,8 +14,6 @@ jobs:
     - uses: actions/checkout@v3
     - name: apt-get-update
       run: sudo apt-get update
-    - name: get-open-ssl
-      run: sudo apt-get install libssl-dev 
     - name: get-python
       run: sudo add-apt-repository ppa:deadsnakes/ppa && sudo apt-get install python3.7 python3.8 python3.9 python3.10 python3.11      
     - uses: messense/maturin-action@v1
diff --git a/src/common/ch.rs b/src/common/ch.rs
index 27507b57..545f85b5 100644
--- a/src/common/ch.rs
+++ b/src/common/ch.rs
@@ -2,6 +2,7 @@ use super::order::Order;
 use super::order::Trade;
 use super::AccountStatus;
 use anyhow::Result;
+use crossbeam_channel::bounded;
 // use crossbeam_channel::bounded;
 use crossbeam_channel::unbounded;
 use crossbeam_channel::Receiver;
@@ -64,6 +65,15 @@ impl MultiChannel {
         }
     }
 
+    pub fn close(&mut self) {
+        loop {
+            match self.channels.pop() {
+                Some(channel) => drop(channel),
+                None => break,
+            }
+        }
+    }
+
     pub fn add_channel(&mut self, channel: Sender<MarketMessage>) {
         self.channels.push(Channel {
             sender: channel,
@@ -71,8 +81,14 @@ impl MultiChannel {
         });
     }
 
-    pub fn open_channel(&mut self) -> MarketStream {
-        let (sender, receiver) = unbounded();
+    pub fn open_channel(&mut self, buffer_size: usize) -> MarketStream {
+        let (sender, receiver) = 
+            if buffer_size == 0 {
+                unbounded()
+            }
+            else {
+                bounded(buffer_size)
+            };
         self.add_channel(sender);
 
         MarketStream { reciver: receiver }
@@ -82,30 +98,16 @@ impl MultiChannel {
         let mut has_error: bool = false;
 
         for channel in self.channels.iter_mut() {
-            if channel.sender.is_full() {
-                log::warn!("channel is full");
-                continue;
-            }
-
-            let result = channel.sender.try_send(message.clone());
+            let result = channel.sender.send(message.clone());
 
             if result.is_err() {
-                match result.unwrap_err() {
-                    TrySendError::Full(_) => {
-                        log::warn!("channel is full");
-                    }
-                    TrySendError::Disconnected(_) => {
-                        log::error!("channel is disconnected");
-                        channel.valid = false;
-                        has_error = true;
-                    }
-                }
+                has_error = true;
             }
         }
 
         // remove invalid channels
         if has_error {
-            log::warn!("removing invalid channels");
+            log::warn!("Send ERROR: removing invalid channels");
             self.channels.retain(|x| x.valid);
         }
 
@@ -122,7 +124,7 @@ mod channel_test {
     #[test]
     fn test_channel() {
         let mut channel = MultiChannel::new();
-        let receiver = channel.open_channel();
+        let receiver = channel.open_channel(0);
 
         let message = MarketMessage {
             trade: None,
@@ -140,7 +142,7 @@ mod channel_test {
         init_log();
         init_debug_log();
         let mut channel = MultiChannel::new();
-        let _receiver = channel.open_channel();
+        let _receiver = channel.open_channel(0);
 
         for _ in 0..1024 {
             let message = MarketMessage {
@@ -164,7 +166,7 @@ mod channel_test {
     fn test_channel_disconnect() {
         init_log();
         let mut channel = MultiChannel::new();
-        let receiver = channel.open_channel();
+        let receiver = channel.open_channel(0);
 
         let message = MarketMessage {
             trade: None,
diff --git a/src/common/config.rs b/src/common/config.rs
index 26b1f764..ea79cc46 100644
--- a/src/common/config.rs
+++ b/src/common/config.rs
@@ -36,6 +36,8 @@ pub struct MarketConfig {
 
     pub home_currency: String,
     pub foreign_currency: String,
+
+    pub market_order_price_slip: Decimal,
 }
 
 
@@ -57,6 +59,7 @@ impl MarketConfig {
             fee_type: FeeType::Home,
             home_currency: home_currency.to_string(),
             foreign_currency: foreign_currency.to_string(),
+            market_order_price_slip: dec![0.0],
         }
     }
 }
diff --git a/src/common/order.rs b/src/common/order.rs
index 7463a899..7cc8c4e8 100644
--- a/src/common/order.rs
+++ b/src/common/order.rs
@@ -5,6 +5,10 @@ use crate::common::time::time_string;
 use super::time::MicroSec;
 use super::MarketConfig;
 use super::MarketMessage;
+use polars_core::prelude::DataFrame;
+use polars_core::prelude::NamedFrom;
+use polars_core::prelude::TimeUnit;
+use polars_core::series::Series;
 use pyo3::pyclass;
 use pyo3::pymethods;
 use rust_decimal::prelude::ToPrimitive;
@@ -156,9 +160,9 @@ impl OrderSide {
 #[derive(Debug, Clone, Copy, PartialEq, Display, Serialize, Deserialize)]
 /// enum order type
 pub enum OrderType {
-    #[strum(ascii_case_insensitive, serialize = "LIMIT")]
+    #[strum(ascii_case_insensitive, serialize = "Limit")]
     Limit,
-    #[strum(ascii_case_insensitive, serialize = "MARKET")]
+    #[strum(ascii_case_insensitive, serialize = "Market")]
     Market,
 }
 #[pymethods]
@@ -268,7 +272,7 @@ impl Trade {
     }
 }
 
-impl Into<MarketMessage> for Trade {
+impl Into<MarketMessage> for &Trade {
     fn into(self) -> MarketMessage {
         MarketMessage {
             trade: Some(self.clone()),
@@ -412,7 +416,7 @@ pub struct Order {
     pub symbol: String,
     #[pyo3(get)]
     pub create_time: MicroSec, // in us
-    #[pyo3(get)]    
+    #[pyo3(get)]
     pub status: OrderStatus,
     #[pyo3(get)]
     pub order_id: String, // YYYY-MM-DD-SEQ
@@ -428,7 +432,7 @@ pub struct Order {
     pub order_size: Decimal, // in foreign
 
     // 以後オーダーの状況に応じてUpdateされる。
-    #[pyo3(get)]    
+    #[pyo3(get)]
     pub remain_size: Decimal, // 残数
     #[pyo3(get)]
     pub transaction_id: String,
@@ -473,7 +477,7 @@ impl Order {
         return Order {
             symbol,
             create_time,
-            status: order_status,            
+            status: order_status,
             order_id,
             client_order_id,
             order_side,
@@ -578,7 +582,7 @@ impl Order {
     }
 
     #[getter]
-    pub fn get_commission_foreign(&self)->f64 {
+    pub fn get_commission_foreign(&self) -> f64 {
         return self.commission_foreign.to_f64().unwrap();
     }
     #[getter]
@@ -607,11 +611,9 @@ impl Order {
     pub fn get_lock_foreign_change(&self) -> f64 {
         return self.lock_foreign_change.to_f64().unwrap();
     }
-
-
 }
 
-impl Into<MarketMessage> for Order {
+impl Into<MarketMessage> for &Order {
     fn into(self) -> MarketMessage {
         MarketMessage {
             trade: None,
@@ -621,6 +623,137 @@ impl Into<MarketMessage> for Order {
     }
 }
 
+pub fn ordervec_to_dataframe(orders: Vec<Order>) -> DataFrame {
+    let mut symbol = Vec::<String>::new();
+    let mut create_time = Vec::<MicroSec>::new();
+    let mut status = Vec::<String>::new();
+    let mut order_id = Vec::<String>::new();
+    let mut client_order_id = Vec::<String>::new();
+    let mut order_side = Vec::<String>::new();
+    let mut order_type = Vec::<String>::new();
+    let mut order_price = Vec::<f64>::new();
+    let mut order_size = Vec::<f64>::new();
+    let mut remain_size = Vec::<f64>::new();
+    let mut transaction_id = Vec::<String>::new();
+    let mut update_time = Vec::<MicroSec>::new();
+    let mut execute_price = Vec::<f64>::new();
+    let mut execute_size = Vec::<f64>::new();
+    let mut quote_vol = Vec::<f64>::new();
+    let mut commission = Vec::<f64>::new();
+    let mut commission_asset = Vec::<String>::new();
+    let mut is_maker = Vec::<bool>::new();
+    let mut message = Vec::<String>::new();
+    let mut commission_home = Vec::<f64>::new();
+    let mut commission_foreign = Vec::<f64>::new();
+    let mut home_change = Vec::<f64>::new();
+    let mut foreign_change = Vec::<f64>::new();
+    let mut free_home_change = Vec::<f64>::new();
+    let mut free_foreign_change = Vec::<f64>::new();
+    let mut lock_home_change = Vec::<f64>::new();
+    let mut lock_foreign_change = Vec::<f64>::new();
+
+    for order in orders {
+        symbol.push(order.symbol.clone());
+        create_time.push(order.create_time);
+        status.push(order.status.to_string());
+        order_id.push(order.order_id.clone());
+        client_order_id.push(order.client_order_id.clone());
+        order_side.push(order.order_side.to_string());
+        order_type.push(order.order_type.to_string());
+        order_price.push(order.order_price.to_f64().unwrap());
+        order_size.push(order.order_size.to_f64().unwrap());
+        remain_size.push(order.remain_size.to_f64().unwrap());
+        transaction_id.push(order.transaction_id.clone());
+        update_time.push(order.update_time);
+        execute_price.push(order.execute_price.to_f64().unwrap());
+        execute_size.push(order.execute_size.to_f64().unwrap());
+        quote_vol.push(order.quote_vol.to_f64().unwrap());
+        commission.push(order.commission.to_f64().unwrap());
+        commission_asset.push(order.commission_asset.clone());
+        is_maker.push(order.is_maker);
+        message.push(order.message.clone());
+
+        commission_home.push(order.commission_home.to_f64().unwrap());
+        commission_foreign.push(order.commission_foreign.to_f64().unwrap());
+        home_change.push(order.home_change.to_f64().unwrap());
+        foreign_change.push(order.foreign_change.to_f64().unwrap());
+        free_home_change.push(order.free_home_change.to_f64().unwrap());
+        free_foreign_change.push(order.free_foreign_change.to_f64().unwrap());
+        lock_home_change.push(order.lock_home_change.to_f64().unwrap());
+        lock_foreign_change.push(order.lock_foreign_change.to_f64().unwrap());
+    }
+
+    let symbol = Series::new("symbol", symbol);
+    let create_time = Series::new("create_time", create_time);
+    let status = Series::new("status", status);
+    let order_id = Series::new("order_id", order_id);
+    let client_order_id = Series::new("client_order_id", client_order_id);
+    let order_side = Series::new("order_side", order_side);
+    let order_type = Series::new("order_type", order_type);
+    let order_price = Series::new("order_price", order_price);
+    let order_size = Series::new("order_size", order_size);
+    let remain_size = Series::new("remain_size", remain_size);
+    let transaction_id = Series::new("transaction_id", transaction_id);
+    let update_time = Series::new("update_time", update_time);
+    let execute_price = Series::new("execute_price", execute_price);
+    let execute_size = Series::new("execute_size", execute_size);
+    let quote_vol = Series::new("quote_vol", quote_vol);
+    let commission = Series::new("commission", commission);
+    let commission_asset = Series::new("commission_asset", commission_asset);
+    let is_maker = Series::new("is_maker", is_maker);
+    let message = Series::new("message", message);
+
+    let commission_home = Series::new("commission_home", commission_home);
+    let commission_foreign = Series::new("commission_foreign", commission_foreign);
+    let home_change = Series::new("home_change", home_change);
+    let foreign_change = Series::new("foreign_change", foreign_change);
+    let free_home_change = Series::new("free_home_change", free_home_change);
+    let free_foreign_change = Series::new("free_foreign_change", free_foreign_change);
+    let lock_home_change = Series::new("lock_home_change", lock_home_change);
+    let lock_foreign_change = Series::new("lock_foreign_change", lock_foreign_change);
+
+    let mut df = DataFrame::new(vec![
+        symbol,
+        create_time,
+        status,
+        order_id,
+        client_order_id,
+        order_side,
+        order_type,
+        order_price,
+        order_size,
+        remain_size,
+        transaction_id,
+        update_time,
+        execute_price,
+        execute_size,
+        quote_vol,
+        commission,
+        commission_asset,
+        is_maker,
+        message,
+        commission_home,
+        commission_foreign,
+        home_change,
+        foreign_change,
+        free_home_change,
+        free_foreign_change,
+        lock_home_change,
+        lock_foreign_change,
+    ])
+    .unwrap();
+
+    let time = df.column("create_time").unwrap().i64().unwrap().clone();
+    let date_time = time.into_datetime(TimeUnit::Microseconds, None);
+    let df = df.with_column(date_time).unwrap();
+
+    let time = df.column("update_time").unwrap().i64().unwrap().clone();
+    let date_time = time.into_datetime(TimeUnit::Microseconds, None);
+    let df = df.with_column(date_time).unwrap();
+
+    return df.clone();
+}
+
 impl Order {
     pub fn update_balance(&mut self, config: &MarketConfig) {
         match self.status {
@@ -638,7 +771,11 @@ impl Order {
 
     /// in order book, accout locked the size of order
     fn update_balance_new(&mut self, _config: &MarketConfig) {
-        let order_size= self.order_size;
+        if !self.is_maker {
+            return;
+        }
+
+        let order_size = self.order_size;
         let order_quote_vol = self.order_size * self.order_price;
 
         if self.order_side == OrderSide::Buy {
@@ -647,17 +784,18 @@ impl Order {
             self.commission_foreign = dec![0.0];
             self.home_change = dec![0.0];
             self.foreign_change = dec![0.0];
-            self.free_home_change = - order_quote_vol;
+            self.free_home_change = -order_quote_vol;
             self.free_foreign_change = dec![0.0];
             self.lock_home_change = order_quote_vol;
             self.lock_foreign_change = dec![0.0];
-        } else { // Sell
+        } else {
+            // Sell
             self.commission_home = dec![0.0];
             self.commission_foreign = dec![0.0];
             self.home_change = dec![0.0];
             self.foreign_change = dec![0.0];
             self.free_home_change = dec![0.0];
-            self.free_foreign_change = - order_size;
+            self.free_foreign_change = -order_size;
             self.lock_home_change = dec![0.0];
             self.lock_foreign_change = order_size;
         }
@@ -680,25 +818,32 @@ impl Order {
 
         if self.order_side == OrderSide::Buy {
             // move home to foregin
-            self.home_change = - filled_quote_vol;
+            self.home_change = -filled_quote_vol;
             self.foreign_change = filled_size;
-            self.free_home_change = - filled_quote_vol;
+
+            self.free_home_change = -filled_quote_vol;
             self.free_foreign_change = filled_size;
-            self.lock_home_change = - filled_quote_vol;
-            self.lock_foreign_change = dec![0.0];
-        } else { // Sell
+            if self.is_maker {
+                self.lock_home_change = -filled_quote_vol;
+                self.lock_foreign_change = dec![0.0];
+            }
+        } else {
+            // Sell
             self.home_change = filled_quote_vol;
-            self.foreign_change = - filled_size;
+            self.foreign_change = -filled_size;
+
             self.free_home_change = filled_quote_vol;
-            self.free_foreign_change = - filled_size;
-            self.lock_home_change = dec![0.0];
-            self.lock_foreign_change = - filled_size;
+            self.free_foreign_change = -filled_size;
+            if self.is_maker {
+                self.lock_home_change = dec![0.0];
+                self.lock_foreign_change = -filled_size;
+            }
         }
     }
 
     //
     fn update_balance_canceled(&mut self, _config: &MarketConfig) {
-        let order_size= self.order_size;
+        let order_size = self.order_size;
         let order_quote_vol = self.order_size * self.order_price;
 
         if self.order_side == OrderSide::Buy {
@@ -709,9 +854,10 @@ impl Order {
             self.foreign_change = dec![0.0];
             self.free_home_change = order_quote_vol;
             self.free_foreign_change = dec![0.0];
-            self.lock_home_change = - order_quote_vol;
+            self.lock_home_change = -order_quote_vol;
             self.lock_foreign_change = dec![0.0];
-        } else { // Sell
+        } else {
+            // Sell
             self.commission_home = dec![0.0];
             self.commission_foreign = dec![0.0];
             self.home_change = dec![0.0];
@@ -719,7 +865,7 @@ impl Order {
             self.free_home_change = dec![0.0];
             self.free_foreign_change = order_size;
             self.lock_home_change = dec![0.0];
-            self.lock_foreign_change = - order_size;
+            self.lock_foreign_change = -order_size;
         }
     }
 }
diff --git a/src/db/sqlite.rs b/src/db/sqlite.rs
index 4de5aab5..6297403c 100644
--- a/src/db/sqlite.rs
+++ b/src/db/sqlite.rs
@@ -57,10 +57,22 @@ pub trait TradeTableQuery {
 
 #[derive(Debug)]
 pub struct TradeTableDb {
+    pub file_name: String,
     connection: Connection,
 }
 
 impl TradeTableDb {
+    pub fn clone_connection(&self) -> TradeTableDb {
+
+        let conn = Connection::open(&self.file_name).unwrap();
+        let db = TradeTableDb {
+            file_name: self.file_name.clone(),
+            connection: conn,
+        };
+
+        return db;
+    }
+
     // insert records with param transaction and trades
     pub fn insert_transaction(tx: &Transaction, trades: &Vec<Trade>) -> Result<i64, Error> {
         let mut insert_len = 0;
@@ -180,7 +192,8 @@ impl TradeTableQuery for TradeTableDb {
         match result {
             Ok(conn) => {
                 let db = TradeTableDb {
-                connection: conn,
+                    file_name: name.to_string(),
+                    connection: conn,
                 };
 
                 Ok(db)
@@ -332,7 +345,7 @@ impl TradeTableQuery for TradeTableDb {
 #[derive(Debug)]
 pub struct TradeTable {
     file_name: String,
-    connection: TradeTableDb,
+    pub connection: TradeTableDb,
     cache_df: DataFrame,
     cache_ohlcvv: DataFrame,
     cache_duration: MicroSec,
@@ -1099,6 +1112,31 @@ impl TradeTableQuery for TradeTable {
     fn recreate_table(&self) {
         self.connection.recreate_table();
     }
+    
+    /// Selects trades from the database within the specified time range and applies the given closure to each trade.
+    ///
+    /// # Arguments
+    ///
+    /// * `from_time` - The start time of the time range to select trades from.
+    /// * `to_time` - The end time of the time range to select trades from.
+    /// * `f` - A closure that will be applied to each selected trade.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use rbot::db::sqlite::SqliteDb;
+    /// use rbot::models::Trade;
+    ///
+    /// let mut db = SqliteDb::new(":memory:").unwrap();
+    /// let from_time = 0;
+    /// let to_time = 100;
+    /// db.insert_trade(&Trade::new(from_time, 1.0, 1.0)).unwrap();
+    /// db.insert_trade(&Trade::new(to_time + 1, 2.0, 2.0)).unwrap();
+    /// let mut trades = Vec::new();
+    /// db.select(from_time, to_time, |trade| trades.push(trade.clone()));
+    /// assert_eq!(trades.len(), 1);
+    /// assert_eq!(trades[0].timestamp, from_time);
+    /// ```
     fn select<F>(&mut self, from_time: MicroSec, to_time: MicroSec, f: F)
     where
         F: FnMut(&Trade),
diff --git a/src/exchange/binance/config.rs b/src/exchange/binance/config.rs
index 7c29abf0..c1cab24b 100644
--- a/src/exchange/binance/config.rs
+++ b/src/exchange/binance/config.rs
@@ -1,4 +1,5 @@
 use pyo3::{pyclass, pymethods};
+use rust_decimal_macros::dec;
 use serde_derive::{Serialize, Deserialize};
 use serde_json::json;
 
@@ -64,6 +65,7 @@ impl BinanceConfig {
         config.public_ws_endpoint = "wss://testnet.binance.vision/ws".to_string();
         config.private_ws_endpoint = "wss://testnet.binance.vision/ws".to_string();
         config.testnet = true;
+        config.market_config.market_order_price_slip = dec![0.5];
 
         return config;
     }
@@ -98,6 +100,14 @@ impl BinanceConfig {
             "".to_string()
         };
 
+        let mut market_config = MarketConfig::new(
+            home_symbol,
+            foreign_symbol,
+            2,
+            4,
+        );
+
+
         return BinanceConfig {
             exchange_name: "BN".to_string(),
             trade_category: "SPOT".to_string(),
@@ -130,12 +140,7 @@ impl BinanceConfig {
             testnet: false,
             api_key,
             api_secret,
-            market_config: MarketConfig::new(
-                home_symbol,
-                foreign_symbol,
-                2,
-                4,
-            ),
+            market_config,
         };
     }
 
diff --git a/src/exchange/binance/market.rs b/src/exchange/binance/market.rs
index 207ffab5..1e5565a4 100644
--- a/src/exchange/binance/market.rs
+++ b/src/exchange/binance/market.rs
@@ -10,10 +10,10 @@ use rust_decimal::Decimal;
 use serde_json::Value;
 use std::borrow::BorrowMut;
 use std::sync::{Arc, Mutex};
-use std::thread::{sleep, JoinHandle};
+use std::thread::{sleep, JoinHandle, Thread, self};
 use std::time::Duration;
 
-use crate::common::convert_pyresult_vec;
+use crate::common::{convert_pyresult_vec, MarketMessage};
 use crate::common::DAYS;
 use crate::common::{convert_pyresult, MarketStream};
 use crate::common::{to_naive_datetime, MicroSec};
@@ -454,7 +454,29 @@ impl BinanceMarket {
 
     #[getter]
     pub fn get_channel(&mut self) -> MarketStream {
-        self.channel.lock().unwrap().open_channel()
+        self.channel.lock().unwrap().open_channel(0)
+    }
+
+    pub fn open_backtest_channel(&mut self, time_from: MicroSec, time_to: MicroSec) -> MarketStream {
+        let channel = self.channel.lock().unwrap().open_channel(1_000);
+        let sender = self.channel.clone();
+
+        let mut table_db = self.db.connection.clone_connection();
+
+        thread::spawn(move || {
+            let mut channel = sender.lock().unwrap();            
+            table_db.select(time_from, time_to, |trade| {
+                let message: MarketMessage = trade.into();
+                let r = channel.send(message);
+        
+                if r.is_err() {
+                    log::error!("Error in channel.send: {:?}", r);
+                }
+            });
+            channel.close();
+        });
+
+        return channel;
     }
 
     #[pyo3(signature = (side, price, size, client_order_id=None))]
@@ -722,6 +744,9 @@ impl BinanceMarket {
             }
         }
     }
+
+    fn write_message_to_channel(&mut self, message: MarketMessage) {
+    }
 }
 
 #[cfg(test)]
diff --git a/src/session/runner.rs b/src/session/runner.rs
index b1f8c620..f019df83 100644
--- a/src/session/runner.rs
+++ b/src/session/runner.rs
@@ -8,9 +8,13 @@ use pyo3::{pyclass, pymethods, Py, PyAny, PyErr, PyObject, Python};
 use rusqlite::ffi::SQLITE_FCNTL_CKSM_FILE;
 use rust_decimal::prelude::ToPrimitive;
 
-use crate::common::{MarketConfig, MarketMessage, MarketStream, MicroSec, Order, Trade, FLOOR_SEC, SEC};
+use crate::common::{
+    MarketConfig, MarketMessage, MarketStream, MicroSec, Order, Trade, FLOOR_SEC, SEC,
+};
 use crate::exchange::binance::config;
 
+use crossbeam_channel::Receiver;
+
 use super::{has_method, Session};
 
 #[pyclass]
@@ -30,35 +34,76 @@ impl Runner {
         }
     }
 
-    pub fn back_test(&self) {
-        // TODO: implement
-        // repave
-        // start db loop
+    pub fn back_test(
+        &mut self,
+        market: PyObject,
+        agent: &PyAny,
+        interval_sec: i64,
+        time_from: MicroSec,
+        time_to: MicroSec,
+    ) -> Result<Py<Session>, PyErr> {
+        let stream = Self::open_backtest_stream(&market, time_from, time_to);
+        let reciever = stream.reciver;
+        self.run(market, &reciever, agent, true, interval_sec, true)
     }
 
-    pub fn dry_run(&mut self, market: PyObject, agent: &PyAny, interval_sec: i64) -> Result<(), PyErr> {
-        // repave
-        // start market stream
-        self.run(market, agent, true, interval_sec)
-    }
+    pub fn dry_run(
+        &mut self,
+        market: PyObject,
+        agent: &PyAny,
+        interval_sec: i64,
+    ) -> Result<Py<Session>, PyErr> {
+        Python::with_gil(|py| {
+            market.call_method0(py, "start_market_stream").unwrap();
+        });
 
-    pub fn real_run(&mut self, market: PyObject, agent: &PyAny, interval_sec: i64) -> Result<(), PyErr> {
-        // repave
-        // start market stream
-        // start user stream
-        self.run(market, agent, false, interval_sec)
+        let stream = Self::get_market_stream(&market);
+        let reciever = stream.reciver;
+        self.run(market, &reciever, agent, true, interval_sec, false)
     }
 
-    pub fn run(&mut self, market: PyObject, agent: &PyAny, dummy: bool, interval_sec: i64) -> Result<(), PyErr> {
+    pub fn real_run(
+        &mut self,
+        market: PyObject,
+        agent: &PyAny,
+        interval_sec: i64,
+        log_memory: bool,
+    ) -> Result<Py<Session>, PyErr> {
         let stream = Self::get_market_stream(&market);
-        let stream = stream.reciver;
+        let reciever = stream.reciver;
+
+        Python::with_gil(|py| {
+            market.call_method0(py, "start_market_stream").unwrap();
+            market.call_method0(py, "start_user_stream").unwrap();
+        });
 
+        self.run(market, &reciever, agent, false, interval_sec, false)
+    }
+}
+
+const WARMUP_STEPS: i64 = 100;
+
+impl Runner {
+    pub fn run(
+        &mut self,
+        market: PyObject,
+        receiver: &Receiver<MarketMessage>,
+        agent: &PyAny,
+        dummy: bool,
+        interval_sec: i64,
+        log_memory: bool,
+    ) -> Result<Py<Session>, PyErr> {
         let result = Python::with_gil(|py| {
             let config = market.getattr(py, "market_config").unwrap();
             let config = config.extract::<MarketConfig>(py).unwrap();
-            // TODO: implement reflet session name based on agent name
-            let mut session = Session::new(market, dummy, &config, None);
-            session.open_log(&session.session_name.clone());
+
+            let mut session = Session::new(market, dummy, &config, None, log_memory);
+
+            if !log_memory {
+                // TODO: ERROR handling
+                session.open_log(&session.session_name.clone()).unwrap();
+            }
+
             let py_session = Py::new(py, session).unwrap();
 
             // TODO: implment on_clock;
@@ -66,61 +111,92 @@ impl Runner {
             let has_on_tick = has_method(agent, "on_tick");
             let has_on_update = has_method(agent, "on_update");
 
+            let mut warm_up_loop: i64 = WARMUP_STEPS;
+
             loop {
-                let message = stream.recv();
+                let message = receiver.recv();
 
                 if message.is_err() {
-                    log::error!("Error in stream.recv: {:?}", message);
+                    log::info!("Data stream is closed {:?}", message);
                     break;
                 }
 
                 let message = message.unwrap();
 
                 if dummy {
-                    let r = self.dummy_on_message(
-                        &py,
-                        agent,
-                        &py_session,
-                        &message,
-                        has_on_clock,
-                        has_on_tick,
-                        has_on_update,
-                        interval_sec
-                    );
+                    let r = if 0 < warm_up_loop {
+                        self.dummy_on_message(
+                            &py,
+                            agent,
+                            &py_session,
+                            &message,
+                            false,
+                            false,
+                            false,
+                            interval_sec,
+                        )
+                    } else {
+                        self.dummy_on_message(
+                            &py,
+                            agent,
+                            &py_session,
+                            &message,
+                            has_on_clock,
+                            has_on_tick,
+                            has_on_update,
+                            interval_sec,
+                        )
+                    };
 
                     if r.is_err() {
                         return Err(r.unwrap_err());
                     }
                 } else {
-                    let r = self.on_message(
-                        &py,
-                        agent,
-                        &py_session,
-                        &message,
-                        has_on_clock,
-                        has_on_tick,
-                        has_on_update,
-                        interval_sec                        
-                    );
+                    let r = if 0 < warm_up_loop {
+                        self.on_message(
+                            &py,
+                            agent,
+                            &py_session,
+                            &message,
+                            false,
+                            false,
+                            false,
+                            interval_sec,
+                        )
+                    } else {
+                        self.on_message(
+                            &py,
+                            agent,
+                            &py_session,
+                            &message,
+                            has_on_clock,
+                            has_on_tick,
+                            has_on_update,
+                            interval_sec,
+                        )
+                    };
 
                     if r.is_err() {
                         return Err(r.unwrap_err());
                     }
                 }
+
+                if message.trade.is_some() {
+                    warm_up_loop = warm_up_loop - 1;                    
+                }
             }
-            Ok(())
+
+            Ok(py_session)
         });
 
         if result.is_err() {
             return Err(result.unwrap_err());
         } else {
             println!("Done running agent");
-            Ok(())
+            Ok(result.unwrap())
         }
     }
-}
 
-impl Runner {
     pub fn on_message(
         self: &mut Self,
         py: &Python,
@@ -130,9 +206,8 @@ impl Runner {
         has_on_clock: bool,
         has_on_tick: bool,
         has_on_update: bool,
-        interval_sec: i64
+        interval_sec: i64,
     ) -> Result<(), PyErr> {
-
         if has_on_clock && message.trade.is_some() {
             self.on_clock(py, agent, py_session, message, interval_sec)?;
         }
@@ -141,10 +216,6 @@ impl Runner {
         session.on_message(&message);
         drop(session);
 
-        if has_on_clock {
-            // TODO: do something for clock intervall
-        }
-
         log::debug!("on_message: {:?}", message);
 
         if message.account.is_some() {
@@ -183,27 +254,24 @@ impl Runner {
             }
         }
 
-
         Ok(())
     }
 
-
     fn on_clock(
         self: &mut Self,
         py: &Python,
-        agent: &PyAny,        
+        agent: &PyAny,
         py_session: &Py<Session>,
         message: &MarketMessage,
-        interval_sec: i64
-    ) -> Result<(), PyErr>
-    {
+        interval_sec: i64,
+    ) -> Result<(), PyErr> {
         let trade = message.trade.as_ref().unwrap();
 
         let new_clock = FLOOR_SEC(trade.time, interval_sec);
 
         if self.current_clock < new_clock {
             self.current_clock = new_clock;
-            let session = py_session.borrow_mut(*py);                            
+            let session = py_session.borrow_mut(*py);
             agent.call_method1("on_clock", (session, self.current_clock))?;
         }
 
@@ -219,9 +287,8 @@ impl Runner {
         has_on_clock: bool,
         has_on_tick: bool,
         has_on_update: bool,
-        window_sec: i64
+        window_sec: i64,
     ) -> Result<(), PyErr> {
-
         if has_on_clock && message.trade.is_some() {
             self.on_clock(py, agent, py_session, message, window_sec)?;
         }
@@ -264,14 +331,12 @@ impl Runner {
             }
         }
 
-
         Ok(())
     }
 
-
-
     /// get market stream from Market object
     /// Every market object shold implement `channel` attribute
+    /// TODO: retrun Error
     pub fn get_market_stream(market: &PyObject) -> MarketStream {
         Python::with_gil(|py| {
             let stream = market.getattr(py, "channel").unwrap();
@@ -280,4 +345,25 @@ impl Runner {
             stream
         })
     }
+
+    /// TODO: return error
+    pub fn open_backtest_stream(
+        market: &PyObject,
+        time_from: MicroSec,
+        time_to: MicroSec,
+    ) -> MarketStream {
+        Python::with_gil(|py| {
+            let stream = market.call_method1(py, "open_backtest_channel", (time_from, time_to));
+
+            if stream.is_ok() {
+                let stream = stream.unwrap();
+                let stream = stream.extract::<MarketStream>(py).unwrap();
+                stream
+            } else {
+                let err = stream.unwrap_err();
+                log::error!("Error in open_backtest_channel: {:?}", err);
+                panic!("Error in open_backtest_channel: {:?}", err);
+            }
+        })
+    }
 }
diff --git a/src/session/session.rs b/src/session/session.rs
index 2ea4ca6b..e007be3b 100644
--- a/src/session/session.rs
+++ b/src/session/session.rs
@@ -3,11 +3,11 @@ use std::io::Write;
 use std::sync::Mutex;
 use std::{fs::OpenOptions, path::Path};
 
-use polars_core::utils::arrow::array::ord;
-use pyo3::{pyclass, pymethods, types::PyTuple, PyAny, PyObject, Python};
+use pyo3::{pyclass, pymethods, PyAny, PyObject, Python};
 use pyo3_polars::PyDataFrame;
 use rust_decimal::{prelude::ToPrimitive, Decimal};
 use rust_decimal_macros::dec;
+use serde_derive::Serialize;
 
 use crate::exchange::BoardItem;
 use crate::{
@@ -21,10 +21,57 @@ use crate::{
 use super::{has_method, OrderList};
 use pyo3::prelude::*;
 
-use crate::common::Trade;
+use crate::common::{Trade, ordervec_to_dataframe};
 use crate::common::{MarketMessage, SEC};
 use crate::common::{Order, OrderType};
 
+#[pyclass]
+#[derive(Debug)]
+pub struct ExecuteLog {
+    on_memory: bool,
+    memory: Vec<Order>,
+    log_file: Option<std::fs::File>,    
+}
+
+impl ExecuteLog {
+    pub fn new(on_memory: bool, log_file: Option<std::fs::File>) -> Self {
+        Self {
+            on_memory,
+            memory: vec![],
+            log_file,
+        }
+    }
+
+    pub fn open_log(&mut self, path: &str) -> Result<(), std::io::Error> {
+        let log_file = Path::new(path).with_extension("log");
+
+        self.log_file = Some(
+            OpenOptions::new()
+                .append(true)
+                .create(true)
+                .open(log_file)?,
+        );
+        Ok(())
+    }
+
+    pub fn log(&mut self, order: &Order) -> Result<(), std::io::Error>{
+        if self.on_memory {
+            self.memory.push(order.clone());
+        } else {
+            if let Some(file) = &mut self.log_file {
+                let s = serde_json::to_string(order)?;
+                file.write_all(s.as_bytes())?;
+                file.write_all(b"\n")?;
+            }
+        }
+        Ok(())
+    }
+
+    pub fn get(&self) -> Vec<Order> {
+        self.memory.clone()
+    }
+}
+
 #[pyclass(name = "Session")]
 #[derive(Debug)]
 pub struct Session {
@@ -38,20 +85,23 @@ pub struct Session {
     order_number: i64,
     transaction_number: i64,
 
-    commission_home: Decimal,
-    commission_foreign: Decimal,
-    home_change: Decimal,
-    foreign_change: Decimal,
-    free_home_change: Decimal,
-    free_foreign_change: Decimal,
-    lock_home_change: Decimal,
-    lock_foreign_change: Decimal,
+    commission_home_sum: Decimal,
+    commission_foreign_sum: Decimal,
+    home_sum: Decimal,
+    foreign_sum: Decimal,
+    free_home_sum: Decimal,
+    free_foreign_sum: Decimal,
+    lock_home_sum: Decimal,
+    lock_foreign_sum: Decimal,
+
+    sell_edge: Decimal,
+    buy_edge: Decimal,
 
     market_config: MarketConfig,
 
     dummy_q: Mutex<VecDeque<Vec<Order>>>,
 
-    log_file: Option<std::fs::File>,
+    log: ExecuteLog,
 }
 
 #[pymethods]
@@ -62,6 +112,7 @@ impl Session {
         dummy: bool,
         market_config: &MarketConfig,
         session_name: Option<&str>,
+        log_memory: bool,
     ) -> Self {
         let session_name = match session_name {
             Some(name) => name.to_string(),
@@ -88,20 +139,23 @@ impl Session {
             order_number: 0,
             transaction_number: 0,
 
-            commission_home: dec![0.0],
-            commission_foreign: dec![0.0],
-            home_change: dec![0.0],
-            foreign_change: dec![0.0],
-            free_home_change: dec![0.0],
-            free_foreign_change: dec![0.0],
-            lock_home_change: dec![0.0],
-            lock_foreign_change: dec![0.0],
+            commission_home_sum: dec![0.0],
+            commission_foreign_sum: dec![0.0],
+            home_sum: dec![0.0],
+            foreign_sum: dec![0.0],
+            free_home_sum: dec![0.0],
+            free_foreign_sum: dec![0.0],
+            lock_home_sum: dec![0.0],
+            lock_foreign_sum: dec![0.0],
+
+            sell_edge: dec![0.0],
+            buy_edge: dec![0.0],
 
             market_config: market_config.clone(),
 
             dummy_q: Mutex::new(VecDeque::new()),
 
-            log_file: None,
+            log: ExecuteLog::new(log_memory, None),
         };
 
         session.load_order_list().unwrap();
@@ -203,41 +257,63 @@ impl Session {
 
     #[getter]
     pub fn get_commission_home(&self) -> f64 {
-        self.commission_home.to_f64().unwrap()
+        self.commission_home_sum.to_f64().unwrap()
     }
 
     #[getter]
     pub fn commission_foreign(&self) -> f64 {
-        self.commission_foreign.to_f64().unwrap()
+        self.commission_foreign_sum.to_f64().unwrap()
     }
     #[getter]
     pub fn home_change(&self) -> f64 {
-        self.home_change.to_f64().unwrap()
+        self.home_sum.to_f64().unwrap()
     }
 
     #[getter]
     pub fn foreign_change(&self) -> f64 {
-        self.foreign_change.to_f64().unwrap()
+        self.foreign_sum.to_f64().unwrap()
     }
 
     #[getter]
     pub fn free_home_change(&self) -> f64 {
-        self.free_home_change.to_f64().unwrap()
+        self.free_home_sum.to_f64().unwrap()
     }
 
     #[getter]
     pub fn free_foreign_change(&self) -> f64 {
-        self.free_foreign_change.to_f64().unwrap()
+        self.free_foreign_sum.to_f64().unwrap()
     }
 
     #[getter]
     pub fn lock_home_change(&self) -> f64 {
-        self.lock_home_change.to_f64().unwrap()
+        self.lock_home_sum.to_f64().unwrap()
     }
 
     #[getter]
     pub fn lock_foreign_change(&self) -> f64 {
-        self.lock_foreign_change.to_f64().unwrap()
+        self.lock_foreign_sum.to_f64().unwrap()
+    }
+
+    #[getter]
+    pub fn get_buy_edge(&self) -> f64 {
+        self.buy_edge.to_f64().unwrap()        
+    }
+
+    #[getter]
+    pub fn get_sell_edge(&self) -> f64 {
+        self.sell_edge.to_f64().unwrap()
+    }
+
+    #[getter]
+    pub fn get_log(&self) -> Vec<Order> {
+        self.log.get()
+    }
+
+    #[getter]
+    pub fn get_log_df(&self) -> PyResult<PyDataFrame> {
+        let df = ordervec_to_dataframe(self.get_log());
+
+        Ok(PyDataFrame(df))
     }
 
     pub fn cancel_order(&mut self, order_id: &str) -> PyResult<Py<PyAny>> {
@@ -306,7 +382,7 @@ impl Session {
         })
     }
 
-    /// TODO: implement
+
     pub fn dummy_market_order(
         &mut self,
         side: OrderSide,
@@ -317,79 +393,40 @@ impl Session {
 
         let local_id = self.new_order_id(&side);
 
-        let board = if side == OrderSide::Buy {
-            self.get_asks_vec()
+        let execute_price = if side == OrderSide::Buy {
+            self.sell_edge + self.market_config.market_order_price_slip
         } else {
-            self.get_bids_vec()
+            self.buy_edge - self.market_config.market_order_price_slip            
         };
 
-        if board.is_err() {
-            // TODO: バックテストのときのアルゴリズムを考える。
-            log::error!("dummy_market_order: cannot get board.");
-            return Err(board.unwrap_err());
-        }
-
-        let board = board.unwrap();
-
-        if board.len() == 0 {
-            // TODO: バックテストのときのアルゴリズムを考える。
-            log::error!("dummy_market_order: board is empty");
-        }
-
-
-        let mut filled_orders = vec![];
-        let mut remain_size = size;
-
-        log::debug!("board: {:?}", board);
+        let mut order = Order::new(
+            self.market_config.symbol(),
+            self.current_time,
+            local_id.clone(),
+            local_id.clone(),
+            side,
+            OrderType::Market,
+            OrderStatus::Filled,
+            dec![0.0],
+            size,
+        );
 
-        for item in board {
-            if remain_size <= dec![0.0] {
-                break;
-            }
+        order.transaction_id = self.dummy_transaction_id();
+        order.update_time = self.current_time;
+        order.is_maker = false;
 
-            let mut order = Order::new(
-                self.market_config.symbol(),
-                self.current_time,
-                local_id.clone(),
-                local_id.clone(),
-                side,
-                OrderType::Market,
-                OrderStatus::Filled,
-                item.price,
-                size,
-            );
-
-            order.transaction_id = self.dummy_transaction_id();
-            order.update_time = self.current_time;
+        order.execute_size = size;
+        order.remain_size = dec![0.0];
+        order.execute_price = execute_price;
+        order.quote_vol = order.execute_price * order.execute_size;
 
-            if remain_size < item.size {
-                order.status = OrderStatus::Filled;
-                order.execute_size = remain_size;
-                order.remain_size = dec![0.0];
-                order.execute_price = item.price;
-                order.quote_vol = order.execute_price * order.execute_size;
-            }
-            else {
-                order.status = OrderStatus::PartiallyFilled;
-                order.execute_size = item.size;
-                order.remain_size = remain_size - item.size;
-                order.execute_price = item.price;
-                order.quote_vol = order.execute_price * order.execute_size;
-            }
+        order.update_balance(&self.market_config);
 
-            order.update_balance(&self.market_config);
-            filled_orders.push(order.clone());
-            remain_size -= item.size;
-        }
+        let orders = vec![order];
 
         Python::with_gil(|py| {
-            if filled_orders.len() == 0 {
-                let none = Python::None(py);
-                return Ok(none);
-            } else {
-                self.push_dummy_q(&filled_orders);
-                return Ok(filled_orders.into_py(py));
-            }
+            self.push_dummy_q(&orders);
+            return Ok(orders.into_py(py));
         })
     }
 
@@ -500,6 +537,7 @@ impl Session {
             sizedp,
         );
 
+        order.is_maker = true;
         order.update_balance(&self.market_config);
 
         if side == OrderSide::Buy {
@@ -546,6 +584,19 @@ impl Session {
     pub fn on_tick(&mut self, tick: &Trade) -> Vec<Order> {
         self.current_time = tick.time;
 
+        if tick.order_side == OrderSide::Buy {
+            self.sell_edge = tick.price;
+            if self.sell_edge <= self.buy_edge{
+                self.buy_edge = self.sell_edge - self.market_config.price_unit;
+            }
+        } else if tick.order_side == OrderSide::Sell {
+            self.buy_edge = tick.price;
+            if self.sell_edge <= self.buy_edge{
+                self.sell_edge = self.buy_edge + self.market_config.price_unit;
+            }
+        }
+
+
         if self.dummy == false {
             return vec![];
         }
@@ -576,7 +627,7 @@ impl Session {
 
         self.update_balance(order);
 
-        self.log(&order.__str__());
+        self.log(&order);
     }
 
     fn new_order_id(&mut self, side: &OrderSide) -> String {
@@ -629,37 +680,27 @@ impl Session {
 
     // TODO: check if this is correct
     pub fn update_balance(&mut self, order: &Order) {
-        self.commission_foreign += order.commission_foreign;
-        self.commission_home += order.commission_home;
+        self.commission_foreign_sum += order.commission_foreign;
+        self.commission_home_sum += order.commission_home;
 
-        self.home_change += order.home_change;
-        self.free_home_change += order.free_home_change;
-        self.lock_home_change += order.lock_home_change;
+        self.home_sum += order.home_change;
+        self.free_home_sum += order.free_home_change;
+        self.lock_home_sum += order.lock_home_change;
 
-        self.foreign_change += order.foreign_change;
-        self.free_foreign_change += order.free_foreign_change;
-        self.lock_foreign_change += order.lock_foreign_change;
+        self.foreign_sum += order.foreign_change;
+        self.free_foreign_sum += order.free_foreign_change;
+        self.lock_foreign_sum += order.lock_foreign_change;
     }
+}
 
-    pub fn open_log(&mut self, path: &str) -> Result<(), std::io::Error> {
-        let log_file = Path::new(path).with_extension("log");
 
-        self.log_file = Some(
-            OpenOptions::new()
-                .append(true)
-                .create(true)
-                .open(log_file)?,
-        );
-        Ok(())
+impl Session {    
+    pub fn log(&mut self, order: &Order) -> Result<(), std::io::Error> {
+        self.log.log(order)
     }
 
-    pub fn log(&mut self, message: &str) -> Result<(), std::io::Error> {
-        if let Some(file) = &mut self.log_file {
-            writeln!(file, "{}", message)?;
-            //file.write_all(format!("{}", message)?.as_bytes())?;
-        }
-
-        Ok(())
+    pub fn open_log(&mut self, path: &str) -> Result<(), std::io::Error> {
+        self.log.open_log(path)
     }
 }
 


Test patch:
diff --git a/src/test/binance_test/backtest.ipynb b/src/test/binance_test/backtest.ipynb
new file mode 100644
index 00000000..b0a1966b
--- /dev/null
+++ b/src/test/binance_test/backtest.ipynb
@@ -0,0 +1,758 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "\n",
+    "from rbot import Session\n",
+    "from rbot import Runner\n",
+    "from rbot import BinanceConfig\n",
+    "from rbot import BinanceMarket\n",
+    "from rbot import init_debug_log\n",
+    "from rbot import init_log\n",
+    "from rbot import OrderSide\n",
+    "from rbot import time_string\n",
+    "from rbot import OrderStatus\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "class MyAgent:\n",
+    "    def on_clock(self, session, clock):\n",
+    "        print(\"on_clock\", time_string(clock))\n",
+    "        session.market_order(OrderSide.Sell, 0.01)\n",
+    "        session.market_order(OrderSide.Buy, 0.01)\n",
+    "\n",
+    "\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "on_clock 2023-08-30T00:00:00.000000\n",
+      "on_clock 2023-08-30T01:00:00.000000\n",
+      "on_clock 2023-08-30T02:00:00.000000\n",
+      "on_clock 2023-08-30T03:00:00.000000\n",
+      "on_clock 2023-08-30T04:00:00.000000\n",
+      "on_clock 2023-08-30T05:00:00.000000\n",
+      "on_clock 2023-08-30T06:00:00.000000\n",
+      "on_clock 2023-08-30T07:00:00.000000\n",
+      "on_clock 2023-08-30T08:00:00.000000\n",
+      "on_clock 2023-08-30T09:00:00.000000\n",
+      "on_clock 2023-08-30T10:00:00.000000\n",
+      "on_clock 2023-08-30T11:00:00.000000\n",
+      "on_clock 2023-08-30T12:00:00.000000\n",
+      "on_clock 2023-08-30T13:00:00.000000\n",
+      "on_clock 2023-08-30T14:00:00.000000\n",
+      "on_clock 2023-08-30T15:00:00.000000\n",
+      "on_clock 2023-08-30T16:00:00.000000\n",
+      "on_clock 2023-08-30T17:00:00.000000\n",
+      "on_clock 2023-08-30T18:00:00.000000\n",
+      "on_clock 2023-08-30T19:00:00.000000\n",
+      "on_clock 2023-08-30T20:00:00.000000\n",
+      "on_clock 2023-08-30T21:00:00.000000\n",
+      "on_clock 2023-08-30T22:00:00.000000\n",
+      "on_clock 2023-08-30T23:00:00.000000\n",
+      "on_clock 2023-08-31T00:00:00.000000\n",
+      "on_clock 2023-08-31T01:00:00.000000\n",
+      "on_clock 2023-08-31T02:00:00.000000\n",
+      "on_clock 2023-08-31T03:00:00.000000\n",
+      "on_clock 2023-08-31T04:00:00.000000\n",
+      "on_clock 2023-08-31T05:00:00.000000\n",
+      "on_clock 2023-08-31T06:00:00.000000\n",
+      "on_clock 2023-08-31T07:00:00.000000\n",
+      "on_clock 2023-08-31T08:00:00.000000\n",
+      "on_clock 2023-08-31T09:00:00.000000\n",
+      "on_clock 2023-08-31T10:00:00.000000\n",
+      "on_clock 2023-08-31T11:00:00.000000\n",
+      "on_clock 2023-08-31T12:00:00.000000\n",
+      "on_clock 2023-08-31T13:00:00.000000\n",
+      "on_clock 2023-08-31T14:00:00.000000\n",
+      "on_clock 2023-08-31T15:00:00.000000\n",
+      "on_clock 2023-08-31T16:00:00.000000\n",
+      "on_clock 2023-08-31T17:00:00.000000\n",
+      "on_clock 2023-08-31T18:00:00.000000\n",
+      "on_clock 2023-08-31T19:00:00.000000\n",
+      "on_clock 2023-08-31T20:00:00.000000\n",
+      "on_clock 2023-08-31T21:00:00.000000\n",
+      "on_clock 2023-08-31T22:00:00.000000\n",
+      "on_clock 2023-08-31T23:00:00.000000\n",
+      "on_clock 2023-09-01T00:00:00.000000\n",
+      "on_clock 2023-09-01T01:00:00.000000\n",
+      "on_clock 2023-09-01T02:00:00.000000\n",
+      "on_clock 2023-09-01T03:00:00.000000\n",
+      "on_clock 2023-09-01T04:00:00.000000\n",
+      "on_clock 2023-09-01T05:00:00.000000\n",
+      "on_clock 2023-09-01T06:00:00.000000\n",
+      "on_clock 2023-09-01T07:00:00.000000\n",
+      "on_clock 2023-09-01T08:00:00.000000\n",
+      "on_clock 2023-09-01T09:00:00.000000\n",
+      "on_clock 2023-09-01T10:00:00.000000\n",
+      "on_clock 2023-09-01T11:00:00.000000\n",
+      "on_clock 2023-09-01T12:00:00.000000\n",
+      "on_clock 2023-09-01T13:00:00.000000\n",
+      "on_clock 2023-09-01T14:00:00.000000\n",
+      "on_clock 2023-09-01T15:00:00.000000\n",
+      "on_clock 2023-09-01T16:00:00.000000\n",
+      "on_clock 2023-09-01T17:00:00.000000\n",
+      "on_clock 2023-09-01T18:00:00.000000\n",
+      "on_clock 2023-09-01T19:00:00.000000\n",
+      "on_clock 2023-09-01T20:00:00.000000\n",
+      "on_clock 2023-09-01T21:00:00.000000\n",
+      "on_clock 2023-09-01T22:00:00.000000\n",
+      "on_clock 2023-09-01T23:00:00.000000\n",
+      "on_clock 2023-09-02T00:00:00.000000\n",
+      "on_clock 2023-09-02T01:00:00.000000\n",
+      "on_clock 2023-09-02T02:00:00.000000\n",
+      "on_clock 2023-09-02T03:00:00.000000\n",
+      "on_clock 2023-09-02T04:00:00.000000\n",
+      "on_clock 2023-09-02T05:00:00.000000\n",
+      "on_clock 2023-09-02T06:00:00.000000\n",
+      "on_clock 2023-09-02T07:00:00.000000\n",
+      "on_clock 2023-09-02T08:00:00.000000\n",
+      "on_clock 2023-09-02T09:00:00.000000\n",
+      "on_clock 2023-09-02T10:00:00.000000\n",
+      "on_clock 2023-09-02T11:00:00.000000\n",
+      "on_clock 2023-09-02T12:00:00.000000\n",
+      "on_clock 2023-09-02T13:00:00.000000\n",
+      "on_clock 2023-09-02T14:00:00.000000\n",
+      "on_clock 2023-09-02T15:00:00.000000\n",
+      "on_clock 2023-09-02T16:00:00.000000\n",
+      "on_clock 2023-09-02T17:00:00.000000\n",
+      "on_clock 2023-09-02T18:00:00.000000\n",
+      "on_clock 2023-09-02T19:00:00.000000\n",
+      "on_clock 2023-09-02T20:00:00.000000\n",
+      "on_clock 2023-09-02T21:00:00.000000\n",
+      "on_clock 2023-09-02T22:00:00.000000\n",
+      "on_clock 2023-09-02T23:00:00.000000\n",
+      "on_clock 2023-09-03T00:00:00.000000\n",
+      "on_clock 2023-09-03T01:00:00.000000\n",
+      "on_clock 2023-09-03T02:00:00.000000\n",
+      "on_clock 2023-09-03T03:00:00.000000\n",
+      "on_clock 2023-09-03T04:00:00.000000\n",
+      "on_clock 2023-09-03T05:00:00.000000\n",
+      "on_clock 2023-09-03T06:00:00.000000\n",
+      "on_clock 2023-09-03T07:00:00.000000\n",
+      "on_clock 2023-09-03T08:00:00.000000\n",
+      "on_clock 2023-09-03T09:00:00.000000\n",
+      "on_clock 2023-09-03T10:00:00.000000\n",
+      "on_clock 2023-09-03T11:00:00.000000\n",
+      "on_clock 2023-09-03T12:00:00.000000\n",
+      "on_clock 2023-09-03T13:00:00.000000\n",
+      "on_clock 2023-09-03T14:00:00.000000\n",
+      "on_clock 2023-09-03T15:00:00.000000\n",
+      "on_clock 2023-09-03T16:00:00.000000\n",
+      "on_clock 2023-09-03T17:00:00.000000\n",
+      "on_clock 2023-09-03T18:00:00.000000\n",
+      "on_clock 2023-09-03T19:00:00.000000\n",
+      "on_clock 2023-09-03T20:00:00.000000\n",
+      "on_clock 2023-09-03T21:00:00.000000\n",
+      "on_clock 2023-09-03T22:00:00.000000\n",
+      "on_clock 2023-09-03T23:00:00.000000\n",
+      "on_clock 2023-09-04T00:00:00.000000\n",
+      "on_clock 2023-09-04T01:00:00.000000\n",
+      "on_clock 2023-09-04T02:00:00.000000\n",
+      "on_clock 2023-09-04T03:00:00.000000\n",
+      "on_clock 2023-09-04T04:00:00.000000\n",
+      "on_clock 2023-09-04T05:00:00.000000\n",
+      "on_clock 2023-09-04T06:00:00.000000\n",
+      "on_clock 2023-09-04T07:00:00.000000\n",
+      "on_clock 2023-09-04T08:00:00.000000\n",
+      "on_clock 2023-09-04T09:00:00.000000\n",
+      "on_clock 2023-09-04T10:00:00.000000\n",
+      "on_clock 2023-09-04T11:00:00.000000\n",
+      "on_clock 2023-09-04T12:00:00.000000\n",
+      "on_clock 2023-09-04T13:00:00.000000\n",
+      "on_clock 2023-09-04T14:00:00.000000\n",
+      "on_clock 2023-09-04T15:00:00.000000\n",
+      "on_clock 2023-09-04T16:00:00.000000\n",
+      "on_clock 2023-09-04T17:00:00.000000\n",
+      "on_clock 2023-09-04T18:00:00.000000\n",
+      "on_clock 2023-09-04T19:00:00.000000\n",
+      "on_clock 2023-09-04T20:00:00.000000\n",
+      "on_clock 2023-09-04T21:00:00.000000\n",
+      "on_clock 2023-09-04T22:00:00.000000\n",
+      "on_clock 2023-09-04T23:00:00.000000\n",
+      "on_clock 2023-09-05T00:00:00.000000\n",
+      "on_clock 2023-09-05T01:00:00.000000\n",
+      "on_clock 2023-09-05T02:00:00.000000\n",
+      "on_clock 2023-09-05T03:00:00.000000\n",
+      "on_clock 2023-09-05T04:00:00.000000\n",
+      "on_clock 2023-09-05T05:00:00.000000\n",
+      "on_clock 2023-09-05T06:00:00.000000\n",
+      "on_clock 2023-09-05T07:00:00.000000\n",
+      "on_clock 2023-09-05T08:00:00.000000\n",
+      "on_clock 2023-09-05T09:00:00.000000\n",
+      "on_clock 2023-09-05T10:00:00.000000\n",
+      "on_clock 2023-09-05T11:00:00.000000\n",
+      "on_clock 2023-09-05T12:00:00.000000\n",
+      "on_clock 2023-09-05T13:00:00.000000\n",
+      "on_clock 2023-09-05T14:00:00.000000\n",
+      "on_clock 2023-09-05T15:00:00.000000\n",
+      "on_clock 2023-09-05T16:00:00.000000\n",
+      "on_clock 2023-09-05T17:00:00.000000\n",
+      "on_clock 2023-09-05T18:00:00.000000\n",
+      "on_clock 2023-09-05T19:00:00.000000\n",
+      "on_clock 2023-09-05T20:00:00.000000\n",
+      "on_clock 2023-09-05T21:00:00.000000\n",
+      "on_clock 2023-09-05T22:00:00.000000\n",
+      "on_clock 2023-09-05T23:00:00.000000\n",
+      "on_clock 2023-09-06T00:00:00.000000\n",
+      "on_clock 2023-09-06T01:00:00.000000\n",
+      "on_clock 2023-09-06T02:00:00.000000\n",
+      "on_clock 2023-09-06T03:00:00.000000\n",
+      "on_clock 2023-09-06T04:00:00.000000\n",
+      "on_clock 2023-09-06T05:00:00.000000\n",
+      "on_clock 2023-09-06T06:00:00.000000\n",
+      "on_clock 2023-09-06T07:00:00.000000\n",
+      "on_clock 2023-09-06T08:00:00.000000\n",
+      "on_clock 2023-09-06T09:00:00.000000\n",
+      "on_clock 2023-09-06T10:00:00.000000\n",
+      "on_clock 2023-09-06T11:00:00.000000\n",
+      "on_clock 2023-09-06T12:00:00.000000\n",
+      "on_clock 2023-09-06T13:00:00.000000\n",
+      "on_clock 2023-09-06T14:00:00.000000\n",
+      "on_clock 2023-09-06T15:00:00.000000\n",
+      "on_clock 2023-09-06T16:00:00.000000\n",
+      "on_clock 2023-09-06T17:00:00.000000\n",
+      "on_clock 2023-09-06T18:00:00.000000\n",
+      "on_clock 2023-09-06T19:00:00.000000\n",
+      "on_clock 2023-09-06T20:00:00.000000\n",
+      "on_clock 2023-09-06T21:00:00.000000\n",
+      "on_clock 2023-09-06T22:00:00.000000\n",
+      "on_clock 2023-09-06T23:00:00.000000\n",
+      "on_clock 2023-09-07T00:00:00.000000\n",
+      "on_clock 2023-09-07T01:00:00.000000\n",
+      "on_clock 2023-09-07T02:00:00.000000\n",
+      "on_clock 2023-09-07T03:00:00.000000\n",
+      "on_clock 2023-09-07T04:00:00.000000\n",
+      "on_clock 2023-09-07T05:00:00.000000\n",
+      "on_clock 2023-09-07T06:00:00.000000\n",
+      "on_clock 2023-09-07T07:00:00.000000\n",
+      "on_clock 2023-09-07T08:00:00.000000\n",
+      "on_clock 2023-09-07T09:00:00.000000\n",
+      "on_clock 2023-09-07T10:00:00.000000\n",
+      "on_clock 2023-09-07T11:00:00.000000\n",
+      "on_clock 2023-09-07T12:00:00.000000\n",
+      "on_clock 2023-09-07T13:00:00.000000\n",
+      "on_clock 2023-09-07T14:00:00.000000\n",
+      "on_clock 2023-09-07T15:00:00.000000\n",
+      "on_clock 2023-09-07T16:00:00.000000\n",
+      "on_clock 2023-09-07T17:00:00.000000\n",
+      "on_clock 2023-09-07T18:00:00.000000\n",
+      "on_clock 2023-09-07T19:00:00.000000\n",
+      "on_clock 2023-09-07T20:00:00.000000\n",
+      "on_clock 2023-09-07T21:00:00.000000\n",
+      "on_clock 2023-09-07T22:00:00.000000\n",
+      "on_clock 2023-09-07T23:00:00.000000\n",
+      "on_clock 2023-09-08T00:00:00.000000\n",
+      "on_clock 2023-09-08T01:00:00.000000\n",
+      "on_clock 2023-09-08T02:00:00.000000\n",
+      "on_clock 2023-09-08T03:00:00.000000\n",
+      "on_clock 2023-09-08T04:00:00.000000\n",
+      "on_clock 2023-09-08T05:00:00.000000\n",
+      "on_clock 2023-09-08T06:00:00.000000\n",
+      "on_clock 2023-09-08T07:00:00.000000\n",
+      "on_clock 2023-09-08T08:00:00.000000\n",
+      "on_clock 2023-09-08T09:00:00.000000\n",
+      "on_clock 2023-09-08T10:00:00.000000\n",
+      "on_clock 2023-09-08T11:00:00.000000\n",
+      "on_clock 2023-09-08T12:00:00.000000\n",
+      "on_clock 2023-09-08T13:00:00.000000\n",
+      "on_clock 2023-09-08T14:00:00.000000\n",
+      "on_clock 2023-09-08T15:00:00.000000\n",
+      "on_clock 2023-09-08T16:00:00.000000\n",
+      "on_clock 2023-09-08T17:00:00.000000\n",
+      "on_clock 2023-09-08T18:00:00.000000\n",
+      "on_clock 2023-09-08T19:00:00.000000\n",
+      "on_clock 2023-09-08T20:00:00.000000\n",
+      "on_clock 2023-09-08T21:00:00.000000\n",
+      "on_clock 2023-09-08T22:00:00.000000\n",
+      "on_clock 2023-09-08T23:00:00.000000\n",
+      "on_clock 2023-09-09T00:00:00.000000\n",
+      "on_clock 2023-09-09T01:00:00.000000\n",
+      "on_clock 2023-09-09T02:00:00.000000\n",
+      "on_clock 2023-09-09T03:00:00.000000\n",
+      "on_clock 2023-09-09T04:00:00.000000\n",
+      "on_clock 2023-09-09T05:00:00.000000\n",
+      "on_clock 2023-09-09T06:00:00.000000\n",
+      "on_clock 2023-09-09T07:00:00.000000\n",
+      "on_clock 2023-09-09T08:00:00.000000\n",
+      "on_clock 2023-09-09T09:00:00.000000\n",
+      "on_clock 2023-09-09T10:00:00.000000\n",
+      "on_clock 2023-09-09T11:00:00.000000\n",
+      "on_clock 2023-09-09T12:00:00.000000\n",
+      "on_clock 2023-09-09T13:00:00.000000\n",
+      "on_clock 2023-09-09T14:00:00.000000\n",
+      "on_clock 2023-09-09T15:00:00.000000\n",
+      "on_clock 2023-09-09T16:00:00.000000\n",
+      "on_clock 2023-09-09T17:00:00.000000\n",
+      "on_clock 2023-09-09T18:00:00.000000\n",
+      "on_clock 2023-09-09T19:00:00.000000\n",
+      "on_clock 2023-09-09T20:00:00.000000\n",
+      "on_clock 2023-09-09T21:00:00.000000\n",
+      "on_clock 2023-09-09T22:00:00.000000\n",
+      "on_clock 2023-09-09T23:00:00.000000\n",
+      "on_clock 2023-09-10T00:00:00.000000\n",
+      "on_clock 2023-09-10T01:00:00.000000\n",
+      "on_clock 2023-09-10T02:00:00.000000\n",
+      "on_clock 2023-09-10T03:00:00.000000\n",
+      "on_clock 2023-09-10T04:00:00.000000\n",
+      "on_clock 2023-09-10T05:00:00.000000\n",
+      "on_clock 2023-09-10T06:00:00.000000\n",
+      "on_clock 2023-09-10T07:00:00.000000\n",
+      "on_clock 2023-09-10T08:00:00.000000\n",
+      "on_clock 2023-09-10T09:00:00.000000\n",
+      "on_clock 2023-09-10T10:00:00.000000\n",
+      "on_clock 2023-09-10T11:00:00.000000\n",
+      "on_clock 2023-09-10T12:00:00.000000\n",
+      "on_clock 2023-09-10T13:00:00.000000\n",
+      "on_clock 2023-09-10T14:00:00.000000\n",
+      "on_clock 2023-09-10T15:00:00.000000\n",
+      "on_clock 2023-09-10T16:00:00.000000\n",
+      "on_clock 2023-09-10T17:00:00.000000\n",
+      "on_clock 2023-09-10T18:00:00.000000\n",
+      "on_clock 2023-09-10T19:00:00.000000\n",
+      "on_clock 2023-09-10T20:00:00.000000\n",
+      "on_clock 2023-09-10T21:00:00.000000\n",
+      "on_clock 2023-09-10T22:00:00.000000\n",
+      "on_clock 2023-09-10T23:00:00.000000\n",
+      "on_clock 2023-09-11T00:00:00.000000\n",
+      "on_clock 2023-09-11T01:00:00.000000\n",
+      "on_clock 2023-09-11T02:00:00.000000\n",
+      "on_clock 2023-09-11T03:00:00.000000\n",
+      "on_clock 2023-09-11T04:00:00.000000\n",
+      "on_clock 2023-09-11T05:00:00.000000\n",
+      "on_clock 2023-09-11T06:00:00.000000\n",
+      "on_clock 2023-09-11T07:00:00.000000\n",
+      "on_clock 2023-09-11T08:00:00.000000\n",
+      "on_clock 2023-09-11T09:00:00.000000\n",
+      "on_clock 2023-09-11T10:00:00.000000\n",
+      "on_clock 2023-09-11T11:00:00.000000\n",
+      "on_clock 2023-09-11T12:00:00.000000\n",
+      "on_clock 2023-09-11T13:00:00.000000\n",
+      "on_clock 2023-09-11T14:00:00.000000\n",
+      "on_clock 2023-09-11T15:00:00.000000\n",
+      "on_clock 2023-09-11T16:00:00.000000\n",
+      "on_clock 2023-09-11T17:00:00.000000\n",
+      "on_clock 2023-09-11T18:00:00.000000\n",
+      "on_clock 2023-09-11T19:00:00.000000\n",
+      "on_clock 2023-09-11T20:00:00.000000\n",
+      "on_clock 2023-09-11T21:00:00.000000\n",
+      "on_clock 2023-09-11T22:00:00.000000\n",
+      "on_clock 2023-09-11T23:00:00.000000\n",
+      "on_clock 2023-09-12T00:00:00.000000\n",
+      "on_clock 2023-09-18T23:00:00.000000\n",
+      "on_clock 2023-09-19T00:00:00.000000\n",
+      "on_clock 2023-09-19T01:00:00.000000\n",
+      "on_clock 2023-09-19T02:00:00.000000\n",
+      "on_clock 2023-09-19T03:00:00.000000\n",
+      "on_clock 2023-09-19T04:00:00.000000\n",
+      "on_clock 2023-09-19T05:00:00.000000\n",
+      "on_clock 2023-09-19T06:00:00.000000\n",
+      "on_clock 2023-09-19T07:00:00.000000\n",
+      "on_clock 2023-09-19T08:00:00.000000\n",
+      "on_clock 2023-09-19T09:00:00.000000\n",
+      "on_clock 2023-09-19T10:00:00.000000\n",
+      "on_clock 2023-09-19T11:00:00.000000\n",
+      "on_clock 2023-09-19T12:00:00.000000\n",
+      "on_clock 2023-09-19T13:00:00.000000\n",
+      "on_clock 2023-09-19T14:00:00.000000\n",
+      "on_clock 2023-09-19T15:00:00.000000\n",
+      "on_clock 2023-09-21T08:00:00.000000\n",
+      "on_clock 2023-09-21T09:00:00.000000\n",
+      "on_clock 2023-09-21T10:00:00.000000\n",
+      "on_clock 2023-09-21T11:00:00.000000\n",
+      "on_clock 2023-09-21T12:00:00.000000\n",
+      "on_clock 2023-09-21T14:00:00.000000\n",
+      "on_clock 2023-09-21T15:00:00.000000\n",
+      "on_clock 2023-09-21T16:00:00.000000\n",
+      "on_clock 2023-09-21T17:00:00.000000\n",
+      "on_clock 2023-09-21T18:00:00.000000\n",
+      "on_clock 2023-09-21T22:00:00.000000\n",
+      "on_clock 2023-09-22T03:00:00.000000\n",
+      "on_clock 2023-09-22T05:00:00.000000\n",
+      "on_clock 2023-09-22T07:00:00.000000\n",
+      "on_clock 2023-09-22T11:00:00.000000\n",
+      "on_clock 2023-09-22T12:00:00.000000\n",
+      "on_clock 2023-09-22T13:00:00.000000\n",
+      "on_clock 2023-09-22T14:00:00.000000\n",
+      "on_clock 2023-09-25T01:00:00.000000\n",
+      "on_clock 2023-09-25T02:00:00.000000\n",
+      "on_clock 2023-09-25T03:00:00.000000\n",
+      "on_clock 2023-09-25T04:00:00.000000\n",
+      "on_clock 2023-09-25T05:00:00.000000\n",
+      "on_clock 2023-09-25T06:00:00.000000\n",
+      "on_clock 2023-09-25T07:00:00.000000\n",
+      "on_clock 2023-09-25T08:00:00.000000\n",
+      "on_clock 2023-09-25T09:00:00.000000\n",
+      "on_clock 2023-09-25T15:00:00.000000\n",
+      "on_clock 2023-09-25T16:00:00.000000\n",
+      "on_clock 2023-09-26T00:00:00.000000\n",
+      "on_clock 2023-09-26T01:00:00.000000\n",
+      "on_clock 2023-09-26T02:00:00.000000\n",
+      "on_clock 2023-09-26T06:00:00.000000\n",
+      "on_clock 2023-09-26T07:00:00.000000\n",
+      "on_clock 2023-09-26T08:00:00.000000\n",
+      "on_clock 2023-09-26T09:00:00.000000\n",
+      "on_clock 2023-09-26T14:00:00.000000\n",
+      "on_clock 2023-09-26T15:00:00.000000\n",
+      "on_clock 2023-09-26T16:00:00.000000\n",
+      "on_clock 2023-09-26T17:00:00.000000\n",
+      "on_clock 2023-09-26T18:00:00.000000\n",
+      "on_clock 2023-09-26T19:00:00.000000\n",
+      "on_clock 2023-09-26T20:00:00.000000\n",
+      "on_clock 2023-09-26T21:00:00.000000\n",
+      "on_clock 2023-09-26T22:00:00.000000\n",
+      "on_clock 2023-09-26T23:00:00.000000\n",
+      "on_clock 2023-09-27T00:00:00.000000\n",
+      "on_clock 2023-09-27T01:00:00.000000\n",
+      "on_clock 2023-09-27T02:00:00.000000\n",
+      "on_clock 2023-09-27T03:00:00.000000\n",
+      "on_clock 2023-09-27T12:00:00.000000\n",
+      "on_clock 2023-09-27T13:00:00.000000\n",
+      "on_clock 2023-09-27T14:00:00.000000\n",
+      "on_clock 2023-09-27T15:00:00.000000\n",
+      "on_clock 2023-09-27T16:00:00.000000\n",
+      "on_clock 2023-09-27T22:00:00.000000\n",
+      "on_clock 2023-09-27T23:00:00.000000\n",
+      "on_clock 2023-09-28T00:00:00.000000\n",
+      "on_clock 2023-09-28T01:00:00.000000\n",
+      "on_clock 2023-09-28T02:00:00.000000\n",
+      "on_clock 2023-09-28T03:00:00.000000\n",
+      "on_clock 2023-09-28T04:00:00.000000\n",
+      "on_clock 2023-09-28T05:00:00.000000\n",
+      "on_clock 2023-09-28T06:00:00.000000\n",
+      "on_clock 2023-09-28T07:00:00.000000\n",
+      "on_clock 2023-09-28T08:00:00.000000\n",
+      "on_clock 2023-09-28T09:00:00.000000\n",
+      "on_clock 2023-09-28T10:00:00.000000\n",
+      "on_clock 2023-09-28T11:00:00.000000\n",
+      "on_clock 2023-09-28T12:00:00.000000\n",
+      "on_clock 2023-09-28T13:00:00.000000\n",
+      "on_clock 2023-09-28T14:00:00.000000\n",
+      "on_clock 2023-10-02T00:00:00.000000\n",
+      "on_clock 2023-10-02T01:00:00.000000\n",
+      "on_clock 2023-10-02T02:00:00.000000\n",
+      "on_clock 2023-10-02T03:00:00.000000\n",
+      "on_clock 2023-10-02T04:00:00.000000\n",
+      "on_clock 2023-10-02T05:00:00.000000\n",
+      "on_clock 2023-10-02T06:00:00.000000\n",
+      "on_clock 2023-10-02T07:00:00.000000\n",
+      "on_clock 2023-10-02T08:00:00.000000\n",
+      "on_clock 2023-10-02T09:00:00.000000\n",
+      "on_clock 2023-10-02T10:00:00.000000\n",
+      "on_clock 2023-10-02T11:00:00.000000\n",
+      "on_clock 2023-10-02T12:00:00.000000\n",
+      "on_clock 2023-10-02T13:00:00.000000\n",
+      "on_clock 2023-10-02T14:00:00.000000\n",
+      "on_clock 2023-10-02T15:00:00.000000\n",
+      "on_clock 2023-10-02T16:00:00.000000\n",
+      "on_clock 2023-10-02T17:00:00.000000\n",
+      "on_clock 2023-10-02T18:00:00.000000\n",
+      "on_clock 2023-10-02T19:00:00.000000\n",
+      "on_clock 2023-10-02T20:00:00.000000\n",
+      "on_clock 2023-10-02T21:00:00.000000\n",
+      "on_clock 2023-10-02T22:00:00.000000\n",
+      "on_clock 2023-10-02T23:00:00.000000\n",
+      "on_clock 2023-10-03T00:00:00.000000\n",
+      "on_clock 2023-10-03T01:00:00.000000\n",
+      "on_clock 2023-10-03T02:00:00.000000\n",
+      "on_clock 2023-10-03T03:00:00.000000\n",
+      "on_clock 2023-10-03T04:00:00.000000\n",
+      "on_clock 2023-10-03T05:00:00.000000\n",
+      "on_clock 2023-10-03T06:00:00.000000\n",
+      "on_clock 2023-10-03T07:00:00.000000\n",
+      "on_clock 2023-10-03T08:00:00.000000\n",
+      "on_clock 2023-10-03T09:00:00.000000\n",
+      "on_clock 2023-10-03T10:00:00.000000\n",
+      "on_clock 2023-10-03T11:00:00.000000\n",
+      "on_clock 2023-10-03T12:00:00.000000\n",
+      "on_clock 2023-10-03T13:00:00.000000\n",
+      "on_clock 2023-10-03T14:00:00.000000\n",
+      "on_clock 2023-10-03T15:00:00.000000\n",
+      "on_clock 2023-10-03T16:00:00.000000\n",
+      "on_clock 2023-10-04T02:00:00.000000\n",
+      "on_clock 2023-10-04T03:00:00.000000\n",
+      "on_clock 2023-10-04T04:00:00.000000\n",
+      "on_clock 2023-10-04T16:00:00.000000\n",
+      "on_clock 2023-10-04T17:00:00.000000\n",
+      "on_clock 2023-10-04T18:00:00.000000\n",
+      "on_clock 2023-10-04T19:00:00.000000\n",
+      "on_clock 2023-10-04T20:00:00.000000\n",
+      "on_clock 2023-10-04T21:00:00.000000\n",
+      "on_clock 2023-10-04T22:00:00.000000\n",
+      "on_clock 2023-10-04T23:00:00.000000\n",
+      "on_clock 2023-10-05T00:00:00.000000\n",
+      "on_clock 2023-10-05T01:00:00.000000\n",
+      "on_clock 2023-10-05T02:00:00.000000\n",
+      "on_clock 2023-10-05T03:00:00.000000\n",
+      "on_clock 2023-10-05T04:00:00.000000\n",
+      "on_clock 2023-10-05T05:00:00.000000\n",
+      "on_clock 2023-10-05T06:00:00.000000\n",
+      "on_clock 2023-10-05T07:00:00.000000\n",
+      "on_clock 2023-10-05T08:00:00.000000\n",
+      "on_clock 2023-10-05T09:00:00.000000\n",
+      "on_clock 2023-10-05T10:00:00.000000\n",
+      "on_clock 2023-10-05T11:00:00.000000\n",
+      "on_clock 2023-10-05T12:00:00.000000\n",
+      "on_clock 2023-10-05T13:00:00.000000\n",
+      "on_clock 2023-10-05T14:00:00.000000\n",
+      "on_clock 2023-10-05T15:00:00.000000\n",
+      "on_clock 2023-10-05T16:00:00.000000\n",
+      "on_clock 2023-10-05T17:00:00.000000\n",
+      "on_clock 2023-10-05T18:00:00.000000\n",
+      "on_clock 2023-10-05T19:00:00.000000\n",
+      "on_clock 2023-10-05T20:00:00.000000\n",
+      "on_clock 2023-10-05T21:00:00.000000\n",
+      "on_clock 2023-10-05T22:00:00.000000\n",
+      "on_clock 2023-10-05T23:00:00.000000\n",
+      "on_clock 2023-10-06T00:00:00.000000\n",
+      "on_clock 2023-10-06T01:00:00.000000\n",
+      "on_clock 2023-10-06T02:00:00.000000\n",
+      "on_clock 2023-10-06T03:00:00.000000\n",
+      "on_clock 2023-10-06T04:00:00.000000\n",
+      "on_clock 2023-10-06T05:00:00.000000\n",
+      "on_clock 2023-10-06T06:00:00.000000\n",
+      "on_clock 2023-10-10T01:00:00.000000\n",
+      "on_clock 2023-10-10T02:00:00.000000\n",
+      "on_clock 2023-10-10T03:00:00.000000\n",
+      "on_clock 2023-10-10T04:00:00.000000\n",
+      "on_clock 2023-10-10T05:00:00.000000\n",
+      "on_clock 2023-10-10T06:00:00.000000\n",
+      "on_clock 2023-10-10T14:00:00.000000\n",
+      "on_clock 2023-10-10T15:00:00.000000\n",
+      "on_clock 2023-10-10T16:00:00.000000\n",
+      "on_clock 2023-10-10T17:00:00.000000\n",
+      "on_clock 2023-10-11T01:00:00.000000\n",
+      "on_clock 2023-10-11T02:00:00.000000\n",
+      "on_clock 2023-10-11T13:00:00.000000\n",
+      "on_clock 2023-10-11T15:00:00.000000\n",
+      "on_clock 2023-10-11T16:00:00.000000\n",
+      "on_clock 2023-10-11T17:00:00.000000\n",
+      "on_clock 2023-10-11T22:00:00.000000\n",
+      "on_clock 2023-10-11T23:00:00.000000\n",
+      "on_clock 2023-10-12T00:00:00.000000\n",
+      "on_clock 2023-10-12T01:00:00.000000\n",
+      "on_clock 2023-10-12T02:00:00.000000\n",
+      "on_clock 2023-10-12T03:00:00.000000\n",
+      "on_clock 2023-10-12T04:00:00.000000\n",
+      "on_clock 2023-10-12T08:00:00.000000\n",
+      "on_clock 2023-10-12T09:00:00.000000\n",
+      "on_clock 2023-10-12T10:00:00.000000\n",
+      "on_clock 2023-10-12T11:00:00.000000\n",
+      "on_clock 2023-10-12T12:00:00.000000\n",
+      "on_clock 2023-10-12T13:00:00.000000\n",
+      "on_clock 2023-10-12T14:00:00.000000\n",
+      "on_clock 2023-10-15T09:00:00.000000\n",
+      "on_clock 2023-10-15T10:00:00.000000\n",
+      "on_clock 2023-10-15T11:00:00.000000\n",
+      "on_clock 2023-10-15T12:00:00.000000\n",
+      "on_clock 2023-10-15T13:00:00.000000\n",
+      "on_clock 2023-10-15T14:00:00.000000\n",
+      "on_clock 2023-10-15T15:00:00.000000\n",
+      "on_clock 2023-10-15T16:00:00.000000\n",
+      "on_clock 2023-10-15T17:00:00.000000\n",
+      "on_clock 2023-10-15T18:00:00.000000\n",
+      "on_clock 2023-10-15T19:00:00.000000\n",
+      "on_clock 2023-10-15T20:00:00.000000\n",
+      "on_clock 2023-10-15T21:00:00.000000\n",
+      "on_clock 2023-10-15T22:00:00.000000\n",
+      "on_clock 2023-10-15T23:00:00.000000\n",
+      "on_clock 2023-10-16T00:00:00.000000\n",
+      "on_clock 2023-10-16T01:00:00.000000\n",
+      "on_clock 2023-10-16T02:00:00.000000\n",
+      "on_clock 2023-10-16T03:00:00.000000\n",
+      "on_clock 2023-10-16T04:00:00.000000\n",
+      "on_clock 2023-10-16T05:00:00.000000\n",
+      "on_clock 2023-10-16T06:00:00.000000\n",
+      "on_clock 2023-10-16T07:00:00.000000\n",
+      "on_clock 2023-10-16T08:00:00.000000\n",
+      "on_clock 2023-10-16T09:00:00.000000\n",
+      "on_clock 2023-10-16T10:00:00.000000\n",
+      "on_clock 2023-10-16T11:00:00.000000\n",
+      "on_clock 2023-10-16T12:00:00.000000\n",
+      "on_clock 2023-10-16T13:00:00.000000\n",
+      "on_clock 2023-10-16T14:00:00.000000\n",
+      "on_clock 2023-10-16T15:00:00.000000\n",
+      "on_clock 2023-10-16T16:00:00.000000\n",
+      "on_clock 2023-10-16T17:00:00.000000\n",
+      "on_clock 2023-10-16T18:00:00.000000\n",
+      "on_clock 2023-10-16T19:00:00.000000\n",
+      "on_clock 2023-10-16T20:00:00.000000\n",
+      "on_clock 2023-10-16T21:00:00.000000\n",
+      "on_clock 2023-10-16T22:00:00.000000\n",
+      "on_clock 2023-10-16T23:00:00.000000\n",
+      "on_clock 2023-10-17T00:00:00.000000\n",
+      "on_clock 2023-10-17T01:00:00.000000\n",
+      "on_clock 2023-10-17T02:00:00.000000\n",
+      "on_clock 2023-10-17T03:00:00.000000\n",
+      "on_clock 2023-10-17T04:00:00.000000\n",
+      "on_clock 2023-10-17T05:00:00.000000\n",
+      "on_clock 2023-10-17T06:00:00.000000\n",
+      "on_clock 2023-10-17T07:00:00.000000\n",
+      "on_clock 2023-10-17T08:00:00.000000\n",
+      "on_clock 2023-10-17T09:00:00.000000\n",
+      "on_clock 2023-10-17T10:00:00.000000\n",
+      "on_clock 2023-10-17T11:00:00.000000\n",
+      "on_clock 2023-10-17T12:00:00.000000\n",
+      "on_clock 2023-10-17T13:00:00.000000\n",
+      "on_clock 2023-10-17T14:00:00.000000\n",
+      "on_clock 2023-10-17T15:00:00.000000\n",
+      "on_clock 2023-10-17T16:00:00.000000\n",
+      "on_clock 2023-10-17T17:00:00.000000\n",
+      "on_clock 2023-10-17T18:00:00.000000\n",
+      "on_clock 2023-10-17T19:00:00.000000\n",
+      "on_clock 2023-10-17T20:00:00.000000\n",
+      "on_clock 2023-10-17T21:00:00.000000\n",
+      "on_clock 2023-10-17T22:00:00.000000\n",
+      "on_clock 2023-10-17T23:00:00.000000\n",
+      "on_clock 2023-10-18T00:00:00.000000\n",
+      "on_clock 2023-10-18T01:00:00.000000\n",
+      "on_clock 2023-10-18T02:00:00.000000\n",
+      "on_clock 2023-10-18T03:00:00.000000\n",
+      "on_clock 2023-10-18T04:00:00.000000\n",
+      "on_clock 2023-10-18T05:00:00.000000\n",
+      "on_clock 2023-10-18T06:00:00.000000\n",
+      "on_clock 2023-10-18T07:00:00.000000\n",
+      "on_clock 2023-10-18T08:00:00.000000\n",
+      "on_clock 2023-10-18T09:00:00.000000\n",
+      "on_clock 2023-10-18T10:00:00.000000\n",
+      "on_clock 2023-10-18T11:00:00.000000\n",
+      "on_clock 2023-10-18T12:00:00.000000\n",
+      "on_clock 2023-10-18T13:00:00.000000\n",
+      "on_clock 2023-10-18T14:00:00.000000\n",
+      "on_clock 2023-10-18T15:00:00.000000\n",
+      "Done running agent\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<Session at 0x104ed4b80>"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "market = BinanceMarket(BinanceConfig.TEST_BTCUSDT)\n",
+    "\n",
+    "agent = MyAgent()\n",
+    "runner = Runner()\n",
+    "\n",
+    "\n",
+    "\n",
+    "session = runner.back_test(market, agent, 3600, 0, 0)\n",
+    "\n",
+    "session"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<div><style>\n",
+       ".dataframe > thead > tr > th,\n",
+       ".dataframe > tbody > tr > td {\n",
+       "  text-align: right;\n",
+       "  white-space: pre-wrap;\n",
+       "}\n",
+       "</style>\n",
+       "<small>shape: (1_188, 27)</small><table border=\"1\" class=\"dataframe\"><thead><tr><th>symbol</th><th>create_time</th><th>status</th><th>order_id</th><th>client_order_id</th><th>order_side</th><th>order_type</th><th>order_price</th><th>order_size</th><th>remain_size</th><th>transaction_id</th><th>update_time</th><th>execute_price</th><th>execute_size</th><th>quote_vol</th><th>commission</th><th>commission_asset</th><th>is_maker</th><th>message</th><th>commission_home</th><th>commission_foreign</th><th>home_change</th><th>foreign_change</th><th>free_home_change</th><th>free_foreign_change</th><th>lock_home_change</th><th>lock_foreign_change</th></tr><tr><td>str</td><td>datetime[μs]</td><td>str</td><td>str</td><td>str</td><td>str</td><td>str</td><td>f64</td><td>f64</td><td>f64</td><td>str</td><td>datetime[μs]</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td><td>str</td><td>bool</td><td>str</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td><td>f64</td></tr></thead><tbody><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 00:00:06.226</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 00:00:06.226</td><td>27719.08</td><td>0.01</td><td>277.1908</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>277.1908</td><td>-0.01</td><td>277.1908</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 00:00:06.226</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 00:00:06.226</td><td>27720.09</td><td>0.01</td><td>277.2009</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-277.2009</td><td>0.01</td><td>-277.2009</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 00:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 00:59:59.999</td><td>27621.73</td><td>0.01</td><td>276.2173</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>276.2173</td><td>-0.01</td><td>276.2173</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 00:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 00:59:59.999</td><td>27622.74</td><td>0.01</td><td>276.2274</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-276.2274</td><td>0.01</td><td>-276.2274</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 01:59:59.998</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 01:59:59.998</td><td>27552.11</td><td>0.01</td><td>275.5211</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>275.5211</td><td>-0.01</td><td>275.5211</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 01:59:59.998</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 01:59:59.998</td><td>27553.12</td><td>0.01</td><td>275.5312</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-275.5312</td><td>0.01</td><td>-275.5312</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 02:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 02:59:59.999</td><td>27400.06</td><td>0.01</td><td>274.0006</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>274.0006</td><td>-0.01</td><td>274.0006</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 02:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 02:59:59.999</td><td>27401.07</td><td>0.01</td><td>274.0107</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-274.0107</td><td>0.01</td><td>-274.0107</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 03:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 03:59:59.999</td><td>27390.62</td><td>0.01</td><td>273.9062</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>273.9062</td><td>-0.01</td><td>273.9062</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 03:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 03:59:59.999</td><td>27391.63</td><td>0.01</td><td>273.9163</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-273.9163</td><td>0.01</td><td>-273.9163</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 04:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 04:59:59.999</td><td>27442.49</td><td>0.01</td><td>274.4249</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>274.4249</td><td>-0.01</td><td>274.4249</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-08-30 04:59:59.999</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-08-30 04:59:59.999</td><td>27443.5</td><td>0.01</td><td>274.435</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-274.435</td><td>0.01</td><td>-274.435</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td><td>&hellip;</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 09:59:59.805</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 09:59:59.805</td><td>28331.39</td><td>0.01</td><td>283.3139</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>283.3139</td><td>-0.01</td><td>283.3139</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 09:59:59.805</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 09:59:59.805</td><td>28332.4</td><td>0.01</td><td>283.324</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-283.324</td><td>0.01</td><td>-283.324</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 10:59:59.118</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 10:59:59.118</td><td>28372.81</td><td>0.01</td><td>283.7281</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>283.7281</td><td>-0.01</td><td>283.7281</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 10:59:59.118</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 10:59:59.118</td><td>28373.82</td><td>0.01</td><td>283.7382</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-283.7382</td><td>0.01</td><td>-283.7382</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 11:59:59.431</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 11:59:59.431</td><td>28340.7</td><td>0.01</td><td>283.407</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>283.407</td><td>-0.01</td><td>283.407</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 11:59:59.431</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 11:59:59.431</td><td>28341.71</td><td>0.01</td><td>283.4171</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-283.4171</td><td>0.01</td><td>-283.4171</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 12:59:59.633</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 12:59:59.633</td><td>28329.49</td><td>0.01</td><td>283.2949</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>283.2949</td><td>-0.01</td><td>283.2949</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 12:59:59.633</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 12:59:59.633</td><td>28330.5</td><td>0.01</td><td>283.305</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-283.305</td><td>0.01</td><td>-283.305</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 13:59:59.819</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 13:59:59.819</td><td>28339.57</td><td>0.01</td><td>283.3957</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>283.3957</td><td>-0.01</td><td>283.3957</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 13:59:59.819</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 13:59:59.819</td><td>28340.58</td><td>0.01</td><td>283.4058</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-283.4058</td><td>0.01</td><td>-283.4058</td><td>0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 14:59:59.315</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Sell&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 14:59:59.315</td><td>28189.99</td><td>0.01</td><td>281.8999</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>281.8999</td><td>-0.01</td><td>281.8999</td><td>-0.01</td><td>0.0</td><td>0.0</td></tr><tr><td>&quot;BTCUSDT&quot;</td><td>2023-10-18 14:59:59.315</td><td>&quot;Filled&quot;</td><td>&quot;20231018T1557-…</td><td>&quot;20231018T1557-…</td><td>&quot;Buy&quot;</td><td>&quot;MARKET&quot;</td><td>0.0</td><td>0.01</td><td>0.0</td><td>&quot;20231018T1557-…</td><td>2023-10-18 14:59:59.315</td><td>28191.0</td><td>0.01</td><td>281.91</td><td>0.0</td><td>&quot;&quot;</td><td>false</td><td>&quot;&quot;</td><td>0.0</td><td>0.0</td><td>-281.91</td><td>0.01</td><td>-281.91</td><td>0.01</td><td>0.0</td><td>0.0</td></tr></tbody></table></div>"
+      ],
+      "text/plain": [
+       "shape: (1_188, 27)\n",
+       "┌─────────┬────────────┬────────┬────────────┬───┬────────────┬────────────┬───────────┬───────────┐\n",
+       "│ symbol  ┆ create_tim ┆ status ┆ order_id   ┆ … ┆ free_home_ ┆ free_forei ┆ lock_home ┆ lock_fore │\n",
+       "│ ---     ┆ e          ┆ ---    ┆ ---        ┆   ┆ change     ┆ gn_change  ┆ _change   ┆ ign_chang │\n",
+       "│ str     ┆ ---        ┆ str    ┆ str        ┆   ┆ ---        ┆ ---        ┆ ---       ┆ e         │\n",
+       "│         ┆ datetime[μ ┆        ┆            ┆   ┆ f64        ┆ f64        ┆ f64       ┆ ---       │\n",
+       "│         ┆ s]         ┆        ┆            ┆   ┆            ┆            ┆           ┆ f64       │\n",
+       "╞═════════╪════════════╪════════╪════════════╪═══╪════════════╪════════════╪═══════════╪═══════════╡\n",
+       "│ BTCUSDT ┆ 2023-08-30 ┆ Filled ┆ 20231018T1 ┆ … ┆ 277.1908   ┆ -0.01      ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 00:00:06.2 ┆        ┆ 557-0001SE ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 26         ┆        ┆ L          ┆   ┆            ┆            ┆           ┆           │\n",
+       "│ BTCUSDT ┆ 2023-08-30 ┆ Filled ┆ 20231018T1 ┆ … ┆ -277.2009  ┆ 0.01       ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 00:00:06.2 ┆        ┆ 557-0002BU ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 26         ┆        ┆ Y          ┆   ┆            ┆            ┆           ┆           │\n",
+       "│ BTCUSDT ┆ 2023-08-30 ┆ Filled ┆ 20231018T1 ┆ … ┆ 276.2173   ┆ -0.01      ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 00:59:59.9 ┆        ┆ 557-0003SE ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 99         ┆        ┆ L          ┆   ┆            ┆            ┆           ┆           │\n",
+       "│ BTCUSDT ┆ 2023-08-30 ┆ Filled ┆ 20231018T1 ┆ … ┆ -276.2274  ┆ 0.01       ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 00:59:59.9 ┆        ┆ 557-0004BU ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 99         ┆        ┆ Y          ┆   ┆            ┆            ┆           ┆           │\n",
+       "│ …       ┆ …          ┆ …      ┆ …          ┆ … ┆ …          ┆ …          ┆ …         ┆ …         │\n",
+       "│ BTCUSDT ┆ 2023-10-18 ┆ Filled ┆ 20231018T1 ┆ … ┆ 283.3957   ┆ -0.01      ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 13:59:59.8 ┆        ┆ 557-1185SE ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 19         ┆        ┆ L          ┆   ┆            ┆            ┆           ┆           │\n",
+       "│ BTCUSDT ┆ 2023-10-18 ┆ Filled ┆ 20231018T1 ┆ … ┆ -283.4058  ┆ 0.01       ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 13:59:59.8 ┆        ┆ 557-1186BU ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 19         ┆        ┆ Y          ┆   ┆            ┆            ┆           ┆           │\n",
+       "│ BTCUSDT ┆ 2023-10-18 ┆ Filled ┆ 20231018T1 ┆ … ┆ 281.8999   ┆ -0.01      ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 14:59:59.3 ┆        ┆ 557-1187SE ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 15         ┆        ┆ L          ┆   ┆            ┆            ┆           ┆           │\n",
+       "│ BTCUSDT ┆ 2023-10-18 ┆ Filled ┆ 20231018T1 ┆ … ┆ -281.91    ┆ 0.01       ┆ 0.0       ┆ 0.0       │\n",
+       "│         ┆ 14:59:59.3 ┆        ┆ 557-1188BU ┆   ┆            ┆            ┆           ┆           │\n",
+       "│         ┆ 15         ┆        ┆ Y          ┆   ┆            ┆            ┆           ┆           │\n",
+       "└─────────┴────────────┴────────┴────────────┴───┴────────────┴────────────┴───────────┴───────────┘"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "session.log_df"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.11.6"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/src/test/binance_test/simpleagent.py b/src/test/binance_test/simpleagent.py
index babfbd18..ebb91738 100644
--- a/src/test/binance_test/simpleagent.py
+++ b/src/test/binance_test/simpleagent.py
@@ -68,20 +68,15 @@ def on_account_update(self, session, account):
 
 print(BinanceConfig.TEST_BTCUSDT)
 
-market.start_market_stream()
-#market.repave_today()
-market.start_user_stream()
 
 agent = MyAgent()
 runner = Runner()
 
-from threading import Thread
-from time import sleep
 
 init_debug_log()
 
 def run():
-    runner.run(market, agent)
+    runner.real_run(market, agent, 60)
 
 
 run()
diff --git a/src/test/binance_test/simpleagent_backtest.py b/src/test/binance_test/simpleagent_backtest.py
new file mode 100644
index 00000000..5780270e
--- /dev/null
+++ b/src/test/binance_test/simpleagent_backtest.py
@@ -0,0 +1,62 @@
+
+from rbot import Session
+from rbot import Runner
+from rbot import BinanceConfig
+from rbot import BinanceMarket
+from rbot import init_debug_log
+from rbot import init_log
+from rbot import OrderSide
+from rbot import time_string
+from rbot import OrderStatus
+
+
+class MyAgent:
+    def __init__(self):
+        self.last_update = 0
+        self.order_size = 0.001
+        self.has_position = False
+
+    def on_clock(self, session, clock):
+        print("on_clock", time_string(clock))
+
+        print("sell edge", session.sell_edge)
+        print("buy edge ", session.buy_edge)        
+    
+        order = session.market_order(OrderSide.Sell, self.order_size)                
+        print("sell order", order)
+
+    
+        order = session.market_order(OrderSide.Buy, self.order_size)                    
+        print("buy order", order)
+
+    
+    def on_update(self, session, updated_order):
+        print("on_update", updated_order)
+        
+    
+    def on_account_update(self, session, account):
+        print("account update: ", session.current_time, account)
+
+    
+market = BinanceMarket(BinanceConfig.TEST_BTCUSDT)
+
+print(BinanceConfig.TEST_BTCUSDT)
+
+agent = MyAgent()
+runner = Runner()
+
+from threading import Thread
+from time import sleep
+
+
+
+def run():
+    session = runner.back_test(market, agent, 3600, 0, 0)
+    print(session.log)
+
+run()
+
+
+
+
+    
\ No newline at end of file