Repository: yasstake/rbot (language: python)

Issue:
Client server
Implment dryrun with REST client

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **新機能**
	- サーバー設定を使用して注文簿データを取得し、表示するユニットテストを追加しました。
	- すべてのメッセージを購読し、受信者に返す新しい公開関数`subscribe_all`を追加しました。
	- 新しいサーバー関数`get_board_vec`、`start_board_server`、および`get_rest_orderbook`を追加しました。
	- クライアントモードを処理するための`run`メソッドを更新しました。
- **バグ修正**
	- `Bybit`の設定を更新して`production`を`False`に設定しました。
- **リファクタリング**
	- `MarketImpl`トレイトから`open_udp`関数の実装を削除しました。
	- UDPを介して市場メッセージを送信するためのエラー処理を改善しました。
	- `Session`構造体に新しい`client_mode`フィールドを追加し、操作モードに基づいて注文簿データの取得を調整しました。
- **テスト**
	- テストモジュールを追加しました。

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Discussion / hints:

Fix patch:
diff --git a/exchanges/bybit/src/market.rs b/exchanges/bybit/src/market.rs
index 0222e762..045bd5e0 100644
--- a/exchanges/bybit/src/market.rs
+++ b/exchanges/bybit/src/market.rs
@@ -486,10 +486,6 @@ impl MarketImpl<BybitRestApi, BybitServerConfig> for BybitMarket {
         self.server_config.history_web_base.clone()
     }
 
-    fn open_udp(&mut self) -> Arc<Mutex<UdpSender>> {
-        todo!()
-    }
-
     fn get_order_book(&self) -> Arc<RwLock<OrderBook>> {
         self.board.clone()
     }
diff --git a/rbot/Cargo.toml b/rbot/Cargo.toml
index f2f51902..d82c4d76 100644
--- a/rbot/Cargo.toml
+++ b/rbot/Cargo.toml
@@ -16,6 +16,7 @@ bybit = {path = "../exchanges/bybit"}
 
 rbot_lib = {path="../rbot_lib"}
 rbot_session = {path="../rbot_session"}
+rbot_server = {path="../rbot_server"}
 rbot_market = {path="../rbot_market"}
 once_cell = {workspace = true}
 
diff --git a/rbot/src/lib.rs b/rbot/src/lib.rs
index 604c7b77..48bb6286 100644
--- a/rbot/src/lib.rs
+++ b/rbot/src/lib.rs
@@ -1,6 +1,8 @@
 // Copyright (C) @yasstake
 // All rights reserved. Absolutely NO warranty.
 
+#[cfg(test)]
+mod test;
 
 use pyo3::{pymodule, types::PyModule, wrap_pyfunction, PyResult, Python};
 use rbot_lib::common::{
@@ -14,7 +16,7 @@ use rbot_session::{Logger, Session, Runner, ExecuteMode};
 
 use bybit::{Bybit, BybitConfig};
 
-use console_subscriber;
+
 
 /*
 use exchange::BoardItem;
@@ -36,9 +38,12 @@ use session::Runner;
 //use sim::session::DummySession;
 
 /// A Python module implemented in Rust.
+
+
+// use console_subscriber;
 #[pymodule]
 fn rbot(_py: Python, m: &PyModule) -> PyResult<()> {
-    // console_subscriber::init();
+    // console_subscriber::init();  // for tokio thread debug
 
     m.add("__version__", env!("CARGO_PKG_VERSION"))?;
 
diff --git a/rbot_lib/src/common/hub.rs b/rbot_lib/src/common/hub.rs
index ac411e0c..a21ddf06 100644
--- a/rbot_lib/src/common/hub.rs
+++ b/rbot_lib/src/common/hub.rs
@@ -113,6 +113,38 @@ impl MarketHub {
     }
 
 
+    pub fn subscribe_all(&self,
+    ) -> anyhow::Result<crossbeam_channel::Receiver<BroadcastMessage>> {
+        let mut ch = self.tx.subscribe();
+        let (tx, rx) = crossbeam_channel::unbounded();        
+
+        std::thread::spawn(move ||{
+            let runtime = Runtime::new().unwrap();
+
+            runtime.block_on(async move {
+                loop {
+                    let msg = ch.recv().await;
+    
+                    if msg.is_err() {
+                        break;
+                    }
+
+                    if tx.send(msg.unwrap()).is_err() {
+                        log::error!("send message error");
+                        break;
+                }
+            }
+            });
+        });
+
+        Ok(rx)
+    }
+
+
+
+
+
+
 
     pub async fn subscribe_stream<'a>(
         &self,
@@ -199,8 +231,8 @@ mod test_market_hub {
     fn test_market_hub() {
         init_debug_log();
         let tx = MARKET_HUB.open_channel();
-        let mut rx = MARKET_HUB.subscribe("a", "b", "c", "").unwrap();
-        let mut rx2 = MARKET_HUB.subscribe("a", "b", "c", "").unwrap();
+        let rx = MARKET_HUB.subscribe("a", "b", "c", "").unwrap();
+        let rx2 = MARKET_HUB.subscribe("a", "b", "c", "").unwrap();
 
         for i in 0..CHANNEL_SIZE * 2 {
             let msg = BroadcastMessage {
@@ -283,7 +315,7 @@ mod test_market_hub {
 
         handle2.await.unwrap();
 
-        sleep(Duration::from_secs(5));
+        sleep(Duration::from_secs(5)).await;
     }
 
     /*
diff --git a/rbot_lib/src/common/orderbook.rs b/rbot_lib/src/common/orderbook.rs
index 4ae82e34..5c8c0c6b 100644
--- a/rbot_lib/src/common/orderbook.rs
+++ b/rbot_lib/src/common/orderbook.rs
@@ -1,7 +1,11 @@
 // Copyright(c) 2023. yasstake. All rights reserved.
 // ABUSOLUTELY NO WARRANTY.
 
-use std::{collections::HashMap, iter::FromIterator, sync::{Arc, Mutex}};
+use std::{
+    collections::HashMap,
+    iter::FromIterator,
+    sync::{Arc, Mutex},
+};
 
 use once_cell::sync::Lazy;
 use polars_core::{
@@ -18,9 +22,11 @@ use crate::common::MarketConfig;
 
 use rmp_serde::to_vec;
 
-use super::{order, string_to_decimal, MicroSec, Order, OrderSide, OrderStatus, OrderType, ServerConfig};
+use super::{
+    order, string_to_decimal, MicroSec, Order, OrderSide, OrderStatus, OrderType, ServerConfig,
+};
 
-static ALL_BOARD: Lazy<Mutex<OrderBookList>> = Lazy::new(||Mutex::new(OrderBookList::new()));
+static ALL_BOARD: Lazy<Mutex<OrderBookList>> = Lazy::new(|| Mutex::new(OrderBookList::new()));
 
 pub struct OrderBookList {
     books: HashMap<String, Arc<Mutex<OrderBookRaw>>>,
@@ -31,8 +37,12 @@ impl OrderBookList {
         Self::make_path_from_str(exchange_name, &config.trade_category, &config.trade_symbol)
     }
 
-    pub fn make_path_from_str(exchange_name: &str, trade_category: &str, trade_symbol: &str) -> String {
-        format!("{}/{}/{}", exchange_name, trade_category, trade_symbol)        
+    pub fn make_path_from_str(
+        exchange_name: &str,
+        trade_category: &str,
+        trade_symbol: &str,
+    ) -> String {
+        format!("{}/{}/{}", exchange_name, trade_category, trade_symbol)
     }
 
     pub fn parse_path(path: &str) -> (String, String, String) {
@@ -54,12 +64,12 @@ impl OrderBookList {
         }
 
         let (exchange, category, symbol) = Self::parse_path(key);
-        
+
         Some(OrderBook {
             exchage: exchange,
             category: category,
             symbol: symbol,
-            board: board.unwrap().clone()
+            board: board.unwrap().clone(),
         })
     }
 
@@ -82,8 +92,11 @@ pub fn get_orderbook_list() -> Vec<String> {
 }
 
 #[pyfunction]
-pub fn get_order_book_json(path: &str, size: usize) -> anyhow::Result<String> {
-    let board = ALL_BOARD.lock().unwrap().get(&path.to_string())
+pub fn get_orderbook_json(path: &str, size: usize) -> anyhow::Result<String> {
+    let board = ALL_BOARD
+        .lock()
+        .unwrap()
+        .get(&path.to_string())
         .ok_or_else(|| anyhow::anyhow!("orderbook path=({})not found", path))?;
 
     board.get_json(size)
@@ -91,19 +104,53 @@ pub fn get_order_book_json(path: &str, size: usize) -> anyhow::Result<String> {
 
 #[pyfunction]
 pub fn get_orderbook_vec(path: &str) -> anyhow::Result<(Vec<BoardItem>, Vec<BoardItem>)> {
-    let board = ALL_BOARD.lock().unwrap().get(&path.to_string())
+    let board = ALL_BOARD
+        .lock()
+        .unwrap()
+        .get(&path.to_string())
         .ok_or_else(|| anyhow::anyhow!("orderbook path=({})not found", path))?;
 
     board.get_board_vec()
 }
 
+
+#[pyfunction]
+pub fn get_orderbook_bin(path: &str) -> anyhow::Result<Vec<u8>> {
+    let board = ALL_BOARD
+        .lock()
+        .unwrap()
+        .get(&path.to_string())
+        .ok_or_else(|| anyhow::anyhow!("orderbook path=({})not found", path))?;
+
+    let (bids, asks) = board.get_board_vec()?;
+    let transfer = BoardTransfer {
+        bids: bids,
+        asks: asks,
+    };
+
+    Ok(transfer.to_vec())
+}
+
+
+pub fn get_orderbook(path: &str) -> anyhow::Result<OrderBook> {
+    ALL_BOARD
+        .lock()
+        .unwrap()
+        .get(&path.to_string())
+        .ok_or_else(|| anyhow::anyhow!("orderbook path=({})not found", path))
+}
+
+/*
 pub fn get_orderbook_df(path: &str) -> anyhow::Result<(DataFrame, DataFrame)> {
-    let board = ALL_BOARD.lock().unwrap().get(&path.to_string())
+    let board = ALL_BOARD
+        .lock()
+        .unwrap()
+        .get(&path.to_string())
         .ok_or_else(|| anyhow::anyhow!("orderbook path=({})not found", path))?;
 
     board.get_board()
 }
-
+*/
 
 #[pyclass]
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -172,10 +219,9 @@ impl BoardTransfer {
     pub fn to_dataframe(&mut self) -> anyhow::Result<(DataFrame, DataFrame)> {
         Ok((
             Self::to_data_frame(&self.bids),
-            Self::to_data_frame(&self.asks)
+            Self::to_data_frame(&self.asks),
         ))
     }
-
 }
 
 /// 板上の1行を表す。（価格＆数量）
@@ -374,9 +420,9 @@ impl OrderBook {
 
         let path = OrderBookList::make_path(&exchange_name, config);
         let board = Arc::new(Mutex::new(OrderBookRaw::new(config.board_depth)));
-        
-        ALL_BOARD.lock().unwrap().register(&path,board.clone());
-        
+
+        ALL_BOARD.lock().unwrap().register(&path, board.clone());
+
         OrderBook {
             exchage: exchange_name,
             category: category,
@@ -385,10 +431,46 @@ impl OrderBook {
         }
     }
 
+    pub fn from_bin(
+        exchange_name: &str,
+        config: &MarketConfig,
+        bin: Vec<u8>,
+    ) -> anyhow::Result<Self> {
+        let exchange_name = exchange_name.to_string();
+        let category = config.trade_category.clone();
+        let symbol = config.trade_symbol.clone();
+
+        let board = Arc::new(Mutex::new(OrderBookRaw::new(config.board_depth)));
+
+        {
+            let mut board_lock = board.lock().unwrap();
+            let transfer = BoardTransfer::from_vec(bin);
+            board_lock.update(&transfer.bids, &transfer.asks, true);
+        }
+
+        Ok(OrderBook {
+            exchage: exchange_name,
+            category: category,
+            symbol: symbol,
+            board: board,
+        })
+    }
+
     pub fn clear(&mut self) {
         self.board.lock().unwrap().clear();
     }
 
+    pub fn to_binary(&self) -> anyhow::Result<Vec<u8>> {
+        let board = self.board.lock().unwrap();
+        let (bids, asks) = (board.bids.get(), board.asks.get());
+        let transfer = BoardTransfer {
+            bids: bids,
+            asks: asks,
+        };
+
+        Ok(transfer.to_vec())
+    }
+
     pub fn get_board_vec(&self) -> anyhow::Result<(Vec<BoardItem>, Vec<BoardItem>)> {
         let board = self.board.lock().unwrap();
         let bids = board.bids.get();
@@ -408,7 +490,8 @@ impl OrderBook {
         let mut bids = board.bids.get();
         let mut asks = board.asks.get();
 
-        if size != 0 {  // if there is a size limit, cut off the data.
+        if size != 0 {
+            // if there is a size limit, cut off the data.
             bids = bids[0..size.min(bids.len())].to_vec();
             asks = asks[0..size.min(asks.len())].to_vec();
         }
@@ -433,7 +516,7 @@ impl OrderBook {
     }
 
     pub fn dry_market_order(
-        &mut self, 
+        &mut self,
         create_time: MicroSec,
         order_id: &str,
         client_order_id: &str,
@@ -513,9 +596,14 @@ impl Drop for OrderBook {
         log::debug!("drop orderbook: {}", count);
 
         if count <= 1 {
-            ALL_BOARD.lock().unwrap().unregister(
-                &OrderBookList::make_path_from_str(
-                    &self.exchage, &self.category, &self.symbol));
+            ALL_BOARD
+                .lock()
+                .unwrap()
+                .unregister(&OrderBookList::make_path_from_str(
+                    &self.exchage,
+                    &self.category,
+                    &self.symbol,
+                ));
         }
     }
 }
@@ -564,7 +652,6 @@ mod board_test {
         println!("{:?}", b.get());
     }
 
-
     #[test]
     fn serialize_board_transfer() {
         let mut config = MarketConfig::new("SPOT", "USD", "JPY", 2, 2, 1000);
@@ -572,14 +659,29 @@ mod board_test {
 
         let mut b = OrderBookRaw::new(0);
 
-        b.update(&vec![
-            BoardItem{price: dec![10.0], size:dec![0.01]},
-            BoardItem{price: dec![11.0], size:dec![0.01]},
-        ],
-        &vec![
-            BoardItem{price: dec![10.0], size:dec![0.01]},
-            BoardItem{price: dec![11.0], size:dec![0.01]},
-        ],false);
+        b.update(
+            &vec![
+                BoardItem {
+                    price: dec![10.0],
+                    size: dec![0.01],
+                },
+                BoardItem {
+                    price: dec![11.0],
+                    size: dec![0.01],
+                },
+            ],
+            &vec![
+                BoardItem {
+                    price: dec![10.0],
+                    size: dec![0.01],
+                },
+                BoardItem {
+                    price: dec![11.0],
+                    size: dec![0.01],
+                },
+            ],
+            false,
+        );
 
         let transfer = BoardTransfer::from_orderbook(&b);
 
@@ -591,6 +693,5 @@ mod board_test {
 
         let t2 = BoardTransfer::from_vec(vec);
         println!("{:?}", t2);
-
     }
 }
diff --git a/rbot_lib/src/net/udp.rs b/rbot_lib/src/net/udp.rs
index 29cc4d33..d47013c0 100644
--- a/rbot_lib/src/net/udp.rs
+++ b/rbot_lib/src/net/udp.rs
@@ -2,12 +2,7 @@ use std::mem::MaybeUninit;
 use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
 use std::str::FromStr;
 
-use futures::{stream, Stream};
-use polars_core::utils::arrow::legacy::kernels::concatenate;
-use pyo3::ffi::symtable;
-use reqwest::header::AGE;
-use tokio::runtime::Runtime;
-use tokio::task::spawn;
+use futures::Stream;
 
 use crossbeam_channel::Receiver;
 use socket2::{Domain, Socket, Type};
@@ -19,13 +14,9 @@ use serde_derive::Deserialize;
 use serde_derive::Serialize;
 
 use async_stream::stream;
-use futures::stream::StreamExt;
 
 use crate::common::{env_rbot_multicast_addr, env_rbot_multicast_port, MarketMessage};
-//use crate::common::{AccountStatus, Order, Trade};
 
-/// TODO: BroadcastMessageにliniiearの種別を加える
-/// TODO: Sender,Receiverを実装する。
 
 #[pyclass]
 #[derive(Debug, Clone, Serialize, Deserialize)]
@@ -52,15 +43,12 @@ impl Into<MarketMessage> for BroadcastMessage {
 
 #[derive(Debug)]
 pub struct UdpSender {
-    exchange_name: String,
-    category: String,
-    symbol: String,
     socket: Socket,
     multicast_addr: SockAddr,
 }
 
 impl UdpSender {
-    pub fn open(market_name: &str, market_category: &str, symbol: &str) -> Self {
+    pub fn open() -> Self {
         let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP)).unwrap();
         socket.set_reuse_address(true).unwrap();
         socket.set_reuse_port(true).unwrap();
@@ -74,9 +62,6 @@ impl UdpSender {
         let multicast_addr: SocketAddr = multicast_addr.parse().unwrap();
 
         Self {
-            exchange_name: market_name.to_string(),
-            category: market_category.to_string(),
-            symbol: symbol.to_string(),
             socket: socket,
             multicast_addr: multicast_addr.into(),
         }
@@ -88,10 +73,16 @@ impl UdpSender {
             .send_to(message.as_bytes(), &self.multicast_addr)
     }
 
-    pub fn send_market_message(&self, message: &MarketMessage) -> Result<usize, std::io::Error> {
-        let exchange = self.exchange_name.clone();
-        let category = self.category.clone();
-        let symbol = self.symbol.clone();
+    pub fn send_market_message(
+        &self,
+        exchange_name: &str,
+        category: &str,
+        symbol: &str,
+        message: &MarketMessage,
+    ) -> anyhow::Result<usize> {
+        let exchange = exchange_name.to_string();
+        let category = category.to_string();
+        let symbol = symbol.to_string();
 
         let message = BroadcastMessage {
             exchange: exchange,
@@ -100,16 +91,14 @@ impl UdpSender {
             msg: message.clone(),
         };
 
-        let msg = serde_json::to_string(&message).unwrap();
-
-        log::debug!("send:[{:?}] {}", &self.multicast_addr, msg);
-
-        self.socket.send_to(msg.as_bytes(), &self.multicast_addr)
+        self.send_message(&message)
     }
 
-    pub fn send_message(&self, message: &BroadcastMessage) -> Result<usize, std::io::Error> {
+    pub fn send_message(&self, message: &BroadcastMessage) -> anyhow::Result<usize> {
         let msg = serde_json::to_string(message).unwrap();
-        self.socket.send_to(msg.as_bytes(), &self.multicast_addr)
+        let size = self.socket.send_to(msg.as_bytes(), &self.multicast_addr)?;
+
+        Ok(size)
     }
 }
 
@@ -236,42 +225,37 @@ impl UdpReceiver {
         let (tx, rx) = crossbeam_channel::unbounded::<MarketMessage>();
 
         // TOD: change to async
-        std::thread::spawn(move || {
-            let runtime = Runtime::new().unwrap();
-            runtime.block_on(async move {
-                loop {
-                    let msg = udp.async_receive_message().await;
+        std::thread::spawn(move || loop {
+            let msg = udp.receive_message();
 
-                    if msg.is_err() {
-                        break;
-                    }
+            if msg.is_err() {
+                break;
+            }
 
-                    let msg = msg.unwrap();
+            let msg = msg.unwrap();
 
-                    if msg.filter(&exchange, &category, &symbol) {
-                        let market_message = msg.msg.clone();
-
-                        match market_message {
-                            MarketMessage::Order(ref order) => {
-                                if order.is_my_order(&agent_id) {
-                                    let r = tx.send(market_message.clone());
-                                    if r.is_err() {
-                                        log::error!("open_channel: {}/{:?}", r.err().unwrap(), msg);
-                                        break;
-                                    }
-                                }
-                            }
-                            _ => {
-                                let r = tx.send(market_message.clone());
-                                if r.is_err() {
-                                    log::error!("open_channel: {}/{:?}", r.err().unwrap(), msg);
-                                    break;
-                                }
+            if msg.filter(&exchange, &category, &symbol) {
+                let market_message = msg.msg.clone();
+
+                match market_message {
+                    MarketMessage::Order(ref order) => {
+                        if order.is_my_order(&agent_id) {
+                            let r = tx.send(market_message.clone());
+                            if r.is_err() {
+                                log::error!("open_channel: {}/{:?}", r.err().unwrap(), msg);
+                                break;
                             }
                         }
                     }
+                    _ => {
+                        let r = tx.send(market_message.clone());
+                        if r.is_err() {
+                            log::error!("open_channel: {}/{:?}", r.err().unwrap(), msg);
+                            break;
+                        }
+                    }
                 }
-            });
+            }
         });
 
         Ok(rx)
@@ -286,7 +270,7 @@ mod test_udp {
 
     #[test]
     fn send_test2() {
-        let sender = super::UdpSender::open("EXA", "linear", "BCTUSD");
+        let sender = super::UdpSender::open();
         let r = sender.send("hello world");
 
         assert!(r.is_ok());
@@ -315,7 +299,7 @@ mod test_udp {
             }
         });
 
-        let sender = super::UdpSender::open("EXA", "linear", "BCTUSD");
+        let sender = super::UdpSender::open();
         for i in 0..10 {
             let r = sender.send(&format!("hello world {}", i));
             assert!(r.is_ok());
@@ -335,7 +319,7 @@ mod test_udp {
             }
         });
 
-        let sender = super::UdpSender::open("EXA", "linear", "BCTUSD");
+        let sender = super::UdpSender::open();
         for i in 0..10 {
             let r = sender.send(&format!("hello world {}", i));
             assert!(r.is_ok());
@@ -359,7 +343,7 @@ mod test_udp {
             }
         });
 
-        let sender = super::UdpSender::open("EXA", "linear", "BCTUSD");
+        let sender = super::UdpSender::open();
         for i in 0..10 {
             let r = sender.send(&format!("hello world {}", i));
             assert!(r.is_ok());
diff --git a/rbot_market/src/market.rs b/rbot_market/src/market.rs
index d9de5cb7..75c59483 100644
--- a/rbot_market/src/market.rs
+++ b/rbot_market/src/market.rs
@@ -21,7 +21,6 @@ use rbot_lib::{
         OrderSide, OrderType, ServerConfig, Trade, DAYS, NOW,
     },
     db::{df::KEY, sqlite::TradeTable},
-    net::UdpSender,
 };
 
 macro_rules! check_if_enable_order {
@@ -296,14 +295,6 @@ where
 
     fn get_history_web_base_url(&self) -> String;
 
-    fn open_udp(&mut self) -> Arc<Mutex<UdpSender>> {
-        Arc::new(Mutex::new(UdpSender::open(
-            &self.get_exchange_name(),
-            &self.get_trade_category(),
-            &self.get_trade_symbol(),
-        )))
-    }
-
     fn make_db_path(
         exchange_name: &str,
         trade_category: &str,
diff --git a/rbot_server/Cargo.toml b/rbot_server/Cargo.toml
index 48f58a72..9a02d8be 100644
--- a/rbot_server/Cargo.toml
+++ b/rbot_server/Cargo.toml
@@ -13,4 +13,7 @@ tokio={workspace=true}
 actix-rt={workspace=true}
 actix-web = {workspace=true}
 serde = {workspace=true}
+log={workspace=true}
+
+reqwest = {workspace=true}
 
diff --git a/rbot_server/src/lib.rs b/rbot_server/src/lib.rs
index 6f1ad8b0..bf4a55e8 100644
--- a/rbot_server/src/lib.rs
+++ b/rbot_server/src/lib.rs
@@ -1,3 +1,4 @@
 
 mod server;
 
+pub use server::*;
diff --git a/rbot_server/src/server.rs b/rbot_server/src/server.rs
index c99fb046..bf9bc99b 100644
--- a/rbot_server/src/server.rs
+++ b/rbot_server/src/server.rs
@@ -1,12 +1,32 @@
-use actix_web::{web, App, HttpResponse, HttpServer, Responder, get};
+use actix_web::{get, web, App, HttpResponse, HttpServer, Responder};
 
-use rbot_lib::common::{get_order_book_json, OrderBookList};
-use serde::{ser, Deserialize};
-use actix_rt::System;
+use rbot_lib::common::{get_orderbook_bin, get_orderbook_json, get_orderbook_list, MarketConfig, OrderBook, OrderBookList};
+use serde::Deserialize;
+use log;
 
 #[get("/")]
 async fn greet() -> impl Responder {
-    HttpResponse::Ok().body("Hello, World!")
+    let board = get_orderbook_list();
+
+    let mut result = String::new();
+
+    result += "<html><head></head></body><ul>";
+    for item in board.into_iter() {
+        result += &format!(r#"<li><a href='./board/json/{}'>{}</a></li>"#, item, item);
+    }
+    result += "</ul>";
+
+    let board = get_orderbook_list();    
+    result += "<ul>";
+    for item in board.into_iter() {
+        result += &format!(r#"<li><a href='./board/vec/{}'>{}</a></li>"#, item, item);
+    }
+    result += "</ul></body></html>";
+
+    HttpResponse::Ok().content_type(
+        "text/html; charset=utf-8")
+        .body(
+        result)
 }
 
 #[derive(Deserialize)]
@@ -16,13 +36,13 @@ struct PathInfo {
     symbol: String,
 }
 
-
 #[get("/board/json/{exchange}/{category}/{symbol}")]
 async fn get_board_json(path: web::Path<PathInfo>) -> impl Responder {
     let path = OrderBookList::make_path_from_str(&path.exchange, &path.category, &path.symbol);
+    log::debug!("get_board_json: {:?}", path);
+
+    let json = get_orderbook_json(&path, 0);
 
-    let json = get_order_book_json(&path, 0);
-    
     if json.is_err() {
         return HttpResponse::NotFound().body(format!("Not Found {}", path));
     }
@@ -30,50 +50,60 @@ async fn get_board_json(path: web::Path<PathInfo>) -> impl Responder {
     HttpResponse::Ok().body(json.unwrap())
 }
 
-fn start_server() -> anyhow::Result<()> {
+#[get("/board/vec/{exchange}/{category}/{symbol}")]
+async fn get_board_vec(path: web::Path<PathInfo>) -> impl Responder {
+    let path = OrderBookList::make_path_from_str(&path.exchange, &path.category, &path.symbol);
+    log::debug!("get_board_json: {:?}", path);
+
+    let vec = get_orderbook_bin(&path);
+
+    if vec.is_err() {
+        return HttpResponse::NotFound().body(format!("Not Found {}", path));
+    }
+
+    HttpResponse::Ok().body(vec.unwrap())
+}
+
+
+
+pub fn start_board_server() -> anyhow::Result<()> {
     let sys = actix_rt::System::new();
 
-    let server = HttpServer::new(|| {
+    let server = HttpServer::new(|| 
         App::new()
         .service(greet)
-    })
-    .bind("127.0.0.1:8080").expect("Failed to bind server")
-    .run();
+        .service(get_board_json)
+        .service(get_board_vec)
+        )
+        
+        .bind("127.0.0.1:8080")
+        .expect("Failed to bind server")
+        .run();
 
     sys.block_on(server).unwrap();
 
     Ok(())
 }
 
-fn start_rest_server() -> anyhow::Result<()> {
-    tokio::task::spawn_blocking(move || {
-        start_server()
-    });
 
-    Ok(())
+pub fn get_rest_orderbook(exchange_name: &str, config: &MarketConfig) -> anyhow::Result<OrderBook> {
+    let path = OrderBookList::make_path(exchange_name, config);
+
+    let r = reqwest::blocking::get(&format!("http://localhost:8080/board/vec/{}", path))?.bytes()?;
+
+    let board = OrderBook::from_bin(exchange_name, config, r.to_vec())?;
+
+    Ok(board)
 }
 
 
 #[cfg(test)]
 mod test_rest_server {
     use super::*;
-    use std::thread::sleep;
-    use std::time::Duration;
-    
 
     #[test]
-    fn test_start_server_block(){
-        let server = start_server();
+    fn test_start_server_block() {
+        let server = start_board_server();
         assert!(server.is_ok());
     }
-
-
-    #[test]
-    fn test_start_server() {
-        let server = start_rest_server();
-        assert!(server.is_ok());
-
-
-        sleep(Duration::from_secs(120));
-    }
-}
\ No newline at end of file
+}
diff --git a/rbot_session/Cargo.toml b/rbot_session/Cargo.toml
index 994574c0..2224fc4f 100644
--- a/rbot_session/Cargo.toml
+++ b/rbot_session/Cargo.toml
@@ -8,7 +8,8 @@ edition = "2021"
 [dependencies]
 rbot_lib = {workspace = true}
 rbot_market = {workspace = true}
-rbot_blockon = {workspace = true}
+rbot_server = {workspace = true}
+#rbot_server = {path = "../rbot_server"}
 
 anyhow = {workspace = true}
 
diff --git a/rbot_session/src/runner.rs b/rbot_session/src/runner.rs
index 7540c0aa..fbaf7434 100644
--- a/rbot_session/src/runner.rs
+++ b/rbot_session/src/runner.rs
@@ -2,19 +2,21 @@
 
 use crossbeam_channel::Receiver;
 use pyo3::{pyclass, pymethods, types::IntoPyDict, Py, PyAny, PyErr, PyObject, Python};
-use rbot_lib::{
-    common::{
-        flush_log, time_string, AccountPair, LogStatus, MarketConfig, MarketMessage, MarketStream,
-        MicroSec, Order, Trade, FLOOR_SEC, MARKET_HUB, NOW, SEC,
-    },
-    net::UdpReceiver,
-};
 use rust_decimal::prelude::ToPrimitive;
 
 use super::{has_method, ExecuteMode, Session};
 use async_stream::stream;
 use futures::{Stream, StreamExt};
-use rbot_blockon::BLOCK_ON;
+
+use rbot_lib::{
+    common::{
+        flush_log, time_string, AccountPair, MarketConfig, MarketMessage, MarketStream, MicroSec,
+        Order, Trade, FLOOR_SEC, MARKET_HUB, NOW, SEC,
+    },
+    net::{UdpReceiver, UdpSender},
+};
+
+use rbot_server::start_board_server;
 
 #[pyclass]
 #[derive(Debug, Clone)]
@@ -123,11 +125,8 @@ impl Runner {
         self.verbose = verbose;
         self.execute_mode = ExecuteMode::BackTest;
 
-        BLOCK_ON(async {
-            let receiver = Self::open_backtest_stream(&market, start_time, end_time).await;
-            self.async_run(exchange, market, receiver, agent, true, log_file)
-                .await
-        })
+        let receiver = Self::open_backtest_receiver(&market, start_time, end_time)?;
+        self.run(exchange, market, &receiver, agent, false, true, log_file)
     }
 
     #[pyo3(signature = (*, exchange, market, agent, log_memory=false, execute_time=0, verbose=false, log_file=None, client=false))]
@@ -155,29 +154,19 @@ impl Runner {
         let agent_id = self.agent_id.clone();
 
         if client {
-            BLOCK_ON(async {
-                let mut udp = UdpReceiver::open();
-                let receiver = udp
-                    .receive_stream(&exchange_name, &category, &symbol, &agent_id)
-                    .await;
+            let receiver =
+                UdpReceiver::open_channel(&exchange_name, &category, &symbol, &agent_id)?;
 
-                self.async_run(exchange, market, receiver, agent, log_memory, log_file)
-                    .await
-            })
+            self.run(exchange, market, &receiver, agent, client, log_memory, log_file)
         } else {
             self.prepare_data(&exchange, &market)?;
+            let receiver = MARKET_HUB.subscribe(&exchange_name, &category, &symbol, &agent_id)?;
 
-            BLOCK_ON(async {
-                let receiver = MARKET_HUB
-                    .subscribe_stream(&exchange_name, &category, &symbol, &agent_id)
-                    .await;
-
-                self.async_run(exchange, market, receiver, agent, log_memory, log_file)
-                    .await
-            })
+            self.run(exchange, market, &receiver, agent, client, log_memory, log_file)
         }
     }
 
+    /*
     #[pyo3(signature = (*,exchange,  market, agent, log_memory=false, execute_time=0, verbose=false, log_file=None, client=false))]
     pub fn real_run_bak(
         &mut self,
@@ -238,7 +227,7 @@ impl Runner {
             })
         }
     }
-
+    */
 
     #[pyo3(signature = (*,exchange,  market, agent, log_memory=false, execute_time=0, verbose=false, log_file=None, client=false))]
     pub fn real_run(
@@ -265,106 +254,34 @@ impl Runner {
         let agent_id = self.agent_id.clone();
 
         if client {
-                let receiver = UdpReceiver::
-                    open_channel(&exchange_name, &category, &symbol, &agent_id)?;
+            let receiver =
+                UdpReceiver::open_channel(&exchange_name, &category, &symbol, &agent_id)?;
 
-                self.run(exchange, market, &receiver, agent, log_memory, log_file)
+            self.run(exchange, market, &receiver, agent, client, log_memory, log_file)
         } else {
             self.prepare_data(&exchange, &market)?;
-                let receiver = MARKET_HUB
-                    .subscribe(&exchange_name, &category, &symbol, &agent_id)?;
+            let receiver = MARKET_HUB.subscribe(&exchange_name, &category, &symbol, &agent_id)?;
 
-                self.run(exchange, market, &receiver, agent, log_memory, log_file)
+            self.run(exchange, market, &receiver, agent, client, log_memory, log_file)
         }
     }
 
+    pub fn start_proxy(&mut self) -> anyhow::Result<()> {
+        self.execute_mode = ExecuteMode::Real;
 
-    /*
-    pub fn start_proxy(&mut self, market: PyObject, port: i64) -> Result<(), PyErr> {
-        let sender = UdpSender::open_with_port(port, port + 1);
-
-        let stream = Self::get_market_stream(&market);
-        let reciever = stream.reciver;
-
-        let r = Python::with_gil(|py| {
-            let r = market.call_method0(py, "start_market_stream");
-            if r.is_err() {
-                return Err(r.unwrap_err());
-            }
-            println!("--- start market stream ---");
-
-            let r = market.call_method0(py, "start_user_stream");
-            if r.is_err() {
-                return Err(r.unwrap_err());
-            }
-            println!("--- start user stream ---");
-
-            Ok(())
-        });
+        let receiver = MARKET_HUB.subscribe_all()?;
+        let sender = UdpSender::open();
 
-        if r.is_err() {
-            return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
-                format!("Proxy Send error {}", r.unwrap_err())
-            ));
-        }
+        start_board_server()?;
 
         loop {
-            let message = reciever.recv();
-
-            println!("Proxy: {:?}", message);
+            let message = receiver.recv()?;
 
-            if message.is_err() {
-                log::info!("Data stream is closed {:?}", message);
-                break;
-            }
-
-            let exchange = "BYBIT".to_string();
-            let symbol = "BTCUSDT".to_string();
-            let agent_id = "".to_string();
-
-            let message = match message.unwrap() {
-                MarketMessage {
-                    trade: Some(trade), ..
-                } => BroadcastMessage {
-                    exchange: exchange,
-                    symbol: symbol,
-                    agent_id: agent_id,
-                    msg: BroadcastMessageContent::trade(trade),
-                },
-                MarketMessage {
-                    order: Some(order), ..
-                } => BroadcastMessage {
-                    exchange: exchange,
-                    symbol: symbol,
-                    agent_id: agent_id,
-                    msg: BroadcastMessageContent::order(order),
-                },
-                MarketMessage {
-                    account: Some(account),
-                    ..
-                } => BroadcastMessage {
-                    exchange: exchange,
-                    symbol: symbol,
-                    agent_id: agent_id,
-                    msg: BroadcastMessageContent::account(account),
-                },
-                _ => {
-                    panic!("Unknown message type");
-                }
-            };
-
-            let r = sender.send_message(&message);
-
-            if r.is_err() {
-                return Err(PyErr::new::<pyo3::exceptions::PyRuntimeError, _>(
-                    "Proxy Send error"
-                ));
+            if sender.send_message(&message).is_err() {
+                log::warn!("Failed to send message: {:?}", message);
             }
         }
-
-        Ok(())
     }
-    */
 }
 
 const WARMUP_STEPS: i64 = 10;
@@ -403,7 +320,11 @@ impl Runner {
                     flush_log();
                 }
 
-                let kwargs = if self.verbose {vec![("verbose", true)]} else {vec![("verbose", false)]};
+                let kwargs = if self.verbose {
+                    vec![("verbose", true)]
+                } else {
+                    vec![("verbose", false)]
+                };
                 let kwargs = kwargs.into_py_dict(py);
 
                 market.call_method(py, "download_latest", (), Some(kwargs))?;
@@ -504,6 +425,7 @@ impl Runner {
         &self,
         exchange: PyObject,
         market: PyObject,
+        client_mode: bool, 
         log_memory: bool,
         log_file: Option<String>,
     ) -> Py<Session> {
@@ -514,6 +436,7 @@ impl Runner {
                 exchange,
                 market,
                 self.execute_mode.clone(),
+                client_mode,
                 Some(&session_name),
                 false,
             );
@@ -577,6 +500,7 @@ impl Runner {
         receiver: impl Stream<Item = anyhow::Result<MarketMessage>>,
         // receiver: &Receiver<MarketMessage>,
         agent: &PyAny,
+        client_mode: bool,
         log_memory: bool,
         log_file: Option<String>,
     ) -> anyhow::Result<Py<Session>> {
@@ -590,9 +514,9 @@ impl Runner {
             flush_log();
         }
 
-        let py_session = self.create_session(exchange, market, log_memory, log_file);
+        let py_session = self.create_session(exchange, market, client_mode, log_memory, log_file);
 
-        self.call_agent_on_init(&agent, &py_session);
+        self.call_agent_on_init(&agent, &py_session)?;
         let interval_sec = self.get_clock_interval(&py_session)?;
 
         let mut rec = Box::pin(receiver);
@@ -644,14 +568,14 @@ impl Runner {
         Ok(py_session)
     }
 
-
     pub fn run(
         &mut self,
         exchange: PyObject,
         market: PyObject,
-//        receiver: impl Stream<Item = anyhow::Result<MarketMessage>>,
+        //        receiver: impl Stream<Item = anyhow::Result<MarketMessage>>,
         receiver: &Receiver<MarketMessage>,
         agent: &PyAny,
+        client_mode: bool,
         log_memory: bool,
         log_file: Option<String>,
     ) -> anyhow::Result<Py<Session>> {
@@ -665,9 +589,9 @@ impl Runner {
             flush_log();
         }
 
-        let py_session = self.create_session(exchange, market, log_memory, log_file);
+        let py_session = self.create_session(exchange, market, client_mode, log_memory, log_file);
 
-        self.call_agent_on_init(&agent, &py_session);
+        self.call_agent_on_init(&agent, &py_session)?;
         let interval_sec = self.get_clock_interval(&py_session)?;
 
         // warm up loop
@@ -715,7 +639,6 @@ impl Runner {
         Ok(py_session)
     }
 
-
     fn print_run_result(&self, loop_start_time: i64) {
         if self.verbose {
             println!(
@@ -1137,6 +1060,7 @@ impl Runner {
         session.update_psudo_account_by_order(order)
     }
 
+    /*
     fn get_clock_interval_py(
         self: &mut Self,
         py: &Python,
@@ -1147,6 +1071,7 @@ impl Runner {
 
         Ok(interval_sec)
     }
+    */
 
     fn get_clock_interval(self: &mut Self, py_session: &Py<Session>) -> Result<i64, PyErr> {
         Python::with_gil(|py| {
@@ -1157,6 +1082,7 @@ impl Runner {
         })
     }
 
+    /*
     fn call_agent_on_init_py(
         self: &mut Self,
         py: &Python,
@@ -1169,6 +1095,7 @@ impl Runner {
 
         Ok(())
     }
+    */
 
     fn call_agent_on_init(
         self: &mut Self,
@@ -1280,16 +1207,19 @@ 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();
-            let stream = stream.extract::<MarketStream>(py).unwrap();
+    pub fn open_backtest_receiver(
+        market: &PyObject,
+        time_from: MicroSec,
+        time_to: MicroSec,
+    ) -> anyhow::Result<Receiver<MarketMessage>> {
+        let market_stream = Python::with_gil(|py| {
+            let stream = market.call_method1(py, "open_backtest_channel", (time_from, time_to))?;
+            let stream = stream.extract::<MarketStream>(py)?;
 
-            stream
-        })
+            Ok::<MarketStream, anyhow::Error>(stream)
+        })?;
+
+        Ok(market_stream.reciver)
     }
 
     pub async fn open_backtest_stream(
diff --git a/rbot_session/src/session.rs b/rbot_session/src/session.rs
index a69068fc..61996421 100644
--- a/rbot_session/src/session.rs
+++ b/rbot_session/src/session.rs
@@ -1,4 +1,4 @@
-// Copyright(c) 2022-2023. yasstake. All rights reserved.
+// Copyright(c) 2022-2024. yasstake. All rights reserved.
 
 use std::collections::VecDeque;
 use std::sync::Mutex;
@@ -6,14 +6,17 @@ use std::sync::Mutex;
 use pyo3::{pyclass, pymethods, PyAny, PyObject, Python};
 
 use pyo3_polars::PyDataFrame;
+use rbot_server::get_rest_orderbook;
 use rust_decimal::{prelude::ToPrimitive, Decimal};
 use rust_decimal_macros::dec;
 
 use super::{Logger, OrderList};
+use pyo3::prelude::*;
 use rbot_lib::common::{
-    date_string, get_orderbook_df, hour_string, min_string, time_string, AccountPair, MarketConfig, MarketMessage, MicroSec, Order, OrderBookList, OrderSide, OrderStatus, OrderType, Trade, NOW, SEC
+    date_string, get_orderbook, hour_string, min_string, time_string, AccountPair, MarketConfig,
+    MarketMessage, MicroSec, Order, OrderBookList, OrderSide, OrderStatus, OrderType, Trade, NOW,
+    SEC,
 };
-use pyo3::prelude::*;
 
 use anyhow;
 
@@ -91,17 +94,20 @@ pub struct Session {
 
     dummy_q: Mutex<VecDeque<Vec<Order>>>,
 
+    client_mode: bool,
+
     log: Logger,
 }
 
 #[pymethods]
 impl Session {
     #[new]
-    #[pyo3(signature = (exchange, market, execute_mode, session_name=None, log_memory=true))]
+    #[pyo3(signature = (exchange, market, execute_mode, client_mode=false, session_name=None, log_memory=true))]
     pub fn new(
         exchange: PyObject,
         market: PyObject,
         execute_mode: ExecuteMode,
+        client_mode: bool,
         session_name: Option<&str>,
         log_memory: bool,
     ) -> Self {
@@ -175,6 +181,8 @@ impl Session {
 
             dummy_q: Mutex::new(VecDeque::new()),
 
+            client_mode: client_mode,
+
             log: Logger::new(log_memory),
         };
 
@@ -218,10 +226,15 @@ impl Session {
     }
 
     #[getter]
-    // TODO: リモート動作時にRESTでコールする。
     pub fn get_board(&self) -> anyhow::Result<(PyDataFrame, PyDataFrame)> {
-        let path = OrderBookList::make_path(&self.exchange_name, &self.market_config);
-        let (bids, asks) = get_orderbook_df(&path)?;
+        let orderbook = if self.client_mode {
+            get_rest_orderbook(&self.exchange_name, &self.market_config)?
+        } else {
+            let path = OrderBookList::make_path(&self.exchange_name, &self.market_config);
+            get_orderbook(&path)?
+        };
+
+        let (bids, asks) = orderbook.get_board()?;
 
         Ok((PyDataFrame(bids), PyDataFrame(asks)))
     }
@@ -341,33 +354,30 @@ impl Session {
 
     pub fn expire_order(&mut self, ttl_sec: i64) -> bool {
         let mut has_expire = false;
-        
+
         let expire_time = self.current_timestamp - SEC(ttl_sec);
 
         for order in self.buy_orders.get_old_orders(expire_time) {
             if self.cancel_order(&order.order_id).is_ok() {
                 has_expire = true;
                 log::debug!("expire_orders: cancel order: {:?}", order);
-            }
-            else {
+            } else {
                 log::warn!("expire_orders: cancel order error: {:?}", order);
             }
-
         }
 
         for order in self.sell_orders.get_old_orders(expire_time) {
             if self.cancel_order(&order.order_id).is_ok() {
                 has_expire = true;
                 log::debug!("expire_orders: cancel order: {:?}", order);
-            }
-            else {
+            } else {
                 log::warn!("expire_orders: cancel order error: {:?}", order);
             }
         }
 
         has_expire
     }
-    
+
     pub fn cancel_order(&mut self, order_id: &str) -> PyResult<Py<PyAny>> {
         if self.execute_mode == ExecuteMode::BackTest || self.execute_mode == ExecuteMode::Dry {
             self.dummy_cancel_order(order_id)
@@ -381,8 +391,11 @@ impl Session {
     /// if fail return None
     pub fn real_cancel_order(&mut self, order_id: &str) -> PyResult<Py<PyAny>> {
         Python::with_gil(|py| {
-            let r = 
-                self.exchange.call_method1(py, "cancel_order", (self.market_config.clone(), order_id,));
+            let r = self.exchange.call_method1(
+                py,
+                "cancel_order",
+                (self.market_config.clone(), order_id),
+            );
 
             if r.is_err() {
                 let none = Python::None(py);
@@ -430,8 +443,11 @@ impl Session {
         let local_id = self.new_order_id();
 
         Python::with_gil(|py| {
-            self.exchange
-                .call_method1(py, "market_order", (self.market_config.clone(), side, size, local_id))
+            self.exchange.call_method1(
+                py,
+                "market_order",
+                (self.market_config.clone(), side, size, local_id),
+            )
         })
     }
 
@@ -454,7 +470,6 @@ impl Session {
         return execute_price;
     }
 
-    // TODO: 各Marketではなく、Sessionで実装する。
     pub fn dry_market_order(&mut self, side: String, size: Decimal) -> Result<Py<PyAny>, PyErr> {
         let size_scale = self.market_config.size_scale;
         let size = size.round_dp(size_scale);
@@ -464,20 +479,26 @@ impl Session {
 
         let transaction_id = self.dummy_transaction_id();
 
-        Python::with_gil(|py| {
-            self.market.call_method1(
-                py,
-                "dry_market_order",
-                (
-                    self.current_timestamp,
-                    local_id.clone(),
-                    local_id.clone(),
-                    order_side,
-                    size,
-                    transaction_id,
-                ),
-            )
-        })
+        let mut orderbook = if self.client_mode {
+            get_rest_orderbook(&self.exchange_name, &self.market_config)?
+        } else {
+            let path = OrderBookList::make_path(&self.exchange_name, &self.market_config);
+            get_orderbook(&path)?
+        };
+
+        let order = orderbook.dry_market_order(
+            self.current_timestamp,
+            &local_id.clone(),
+            &local_id.clone(),
+            &self.market_config.trade_symbol.clone(),
+            order_side,
+            size,
+            &transaction_id,
+        )?;
+
+        self.push_dummy_q(&order.clone());
+
+        Python::with_gil(|py| Ok(order.into_py(py)))
     }
 
     pub fn dummy_market_order(&mut self, side: String, size: Decimal) -> Result<Py<PyAny>, PyErr> {
@@ -556,9 +577,11 @@ impl Session {
 
         // then call market.limit_order
         let r = Python::with_gil(|py| {
-            let result =
-                self.exchange
-                    .call_method1(py, "limit_order", (self.market_config.clone(), side, pricedp, sizedp, local_id));
+            let result = self.exchange.call_method1(
+                py,
+                "limit_order",
+                (self.market_config.clone(), side, pricedp, sizedp, local_id),
+            );
 
             match result {
                 // if success update order list
@@ -636,7 +659,6 @@ impl Session {
         return Ok(vec![order]);
     }
 
-
     pub fn update_psudo_account_by_order(&mut self, order: &Order) -> bool {
         self.psudo_account.apply_order(order);
 
@@ -706,7 +728,7 @@ impl Session {
             MarketMessage::Trade(trade) => {
                 log::debug!("on_message: trade={:?}", trade);
                 new_orders = self.on_tick(trade);
-    
+
                 // ダミーモードの場合は約定キューからの処理が発生する。
                 if 0 < new_orders.len() {
                     for order in new_orders.iter_mut() {
@@ -716,11 +738,11 @@ impl Session {
                 }
             }
             MarketMessage::Order(order) => {
-                if ! order.is_my_order(&self.session_name) {
+                if !order.is_my_order(&self.session_name) {
                     log::debug!("on_message: skip my order: {:?}", order);
                     return new_orders;
                 }
-    
+
                 let mut order = order.clone();
                 log::debug!("on_message: order={:?}", order);
                 self.on_order_update(&mut order);
@@ -728,7 +750,7 @@ impl Session {
             MarketMessage::Account(coins) => {
                 log::debug!("on_message: account={:?}", coins);
 
-                let mut account: AccountPair = coins.extract_pair(&config);
+                let account: AccountPair = coins.extract_pair(&config);
                 self.on_account_update(&account);
             }
             MarketMessage::Orderbook(orderbook) => {
@@ -748,7 +770,6 @@ impl Session {
         return new_orders;
     }
 
-
     pub fn log(&mut self, order: &Order) -> Result<(), std::io::Error> {
         let time = self.calc_log_timestamp();
 
@@ -873,7 +894,9 @@ impl Session {
         let config = self.market_config.clone();
 
         let r = Python::with_gil(|py| {
-            let result = self.exchange.call_method1(py, "get_open_orders", (config.clone(),));
+            let result = self
+                .exchange
+                .call_method1(py, "get_open_orders", (config.clone(),));
 
             match result {
                 // if success update order list


Test patch:
diff --git a/rbot/src/test.rs b/rbot/src/test.rs
new file mode 100644
index 00000000..40ad78e8
--- /dev/null
+++ b/rbot/src/test.rs
@@ -0,0 +1,20 @@
+
+
+#[cfg(test)]
+mod test_all {
+    use rbot_lib::common::ServerConfig;
+
+
+    #[test]
+    fn test_rest_get_board() {
+        let config = bybit::BybitConfig::BTCUSDT();
+        let server = bybit::BybitServerConfig::new(false);
+
+        let board = rbot_server::get_rest_orderbook(&server.get_exchange_name(), &config);
+        
+        assert!(board.is_ok());
+
+        println!("{:?}", board.unwrap());
+    }
+
+}
\ No newline at end of file
diff --git a/rbot/test/bybit_test/market.py b/rbot/test/bybit_test/market.py
index b57e3886..4a27200c 100644
--- a/rbot/test/bybit_test/market.py
+++ b/rbot/test/bybit_test/market.py
@@ -1,23 +1,18 @@
 from rbot import Bybit
-from rbot import BybitConfig, BybitMarket
+from rbot import BybitConfig
+from rbot import Runner
 from rbot import init_debug_log, init_log
-from rbot.rest import register, start
-
-bybit = Bybit(testnet=True)
 
+bybit = Bybit(production=False)
 market = bybit.open_market(BybitConfig.BTCUSDT)
 
+market.start_market_stream()
+market.download_archive(ndays=1, verbose=True)
+bybit.start_user_stream()
 
+init_debug_log()
 
-#market.start_market_stream()
-#market.download(ndays=1, verbose=True)
-
-#market.start_user_stream()
-init_log()
+runner = Runner()
+runner.start_proxy()
 
-market.broadcast_message = True
-market.start_market_stream()
-market.start_user_stream()
 
-register(market)
-start(port=5000)
diff --git a/rbot/test/bybit_test/nullagent.py b/rbot/test/bybit_test/nullagent.py
index aaecc21c..ba65973d 100644
--- a/rbot/test/bybit_test/nullagent.py
+++ b/rbot/test/bybit_test/nullagent.py
@@ -80,7 +80,7 @@ def on_update(self, session, updated_order):
 #                execute_time = 10,
                 verbose=True,
                 log_file="skelton_bot.log",
-                client= False
+                client= True
             )