1use bson::doc;
4use bson::Bson;
5use bson::Document;
6use debot_utils::get_local_time;
7use debot_utils::HasId;
8use mongodb::Collection;
9use mongodb::{
10 options::{ClientOptions, Tls, TlsOptions},
11 Database,
12};
13use rust_decimal::Decimal;
14use serde::{Deserialize, Serialize};
15use shared_mongodb::{database, ClientHolder};
16use std::collections::HashMap;
17use std::error;
18use std::fs::File;
19use std::io::{Read, Write};
20use std::path::Path;
21use std::sync::Arc;
22use std::time::SystemTime;
23use tokio::sync::Mutex;
24
25use crate::delete_item_all;
26use crate::SearchMode;
27use crate::TradingStrategy;
28use crate::{
29 create_unique_index, insert_item, search_item, search_items, update_item, Counter, CounterType,
30 Entity,
31};
32
33async fn get_last_id<T: Default + Entity + HasId>(db: &Database) -> u32 {
34 let item = T::default();
35 match search_items(
36 db,
37 &item,
38 crate::SearchMode::Descending,
39 Some(1),
40 None,
41 Some("id"),
42 )
43 .await
44 {
45 Ok(mut items) => items.pop().and_then(|item| item.id()).unwrap_or(0),
46 Err(e) => {
47 log::info!("get_last_id: {:?}", e);
48 0
49 }
50 }
51}
52
53#[derive(Serialize, Deserialize, Clone, Debug)]
54pub enum SampleTerm {
55 TradingTerm,
56 ShortTerm,
57 LongTerm,
58}
59
60impl SampleTerm {
61 pub fn to_numeric(&self) -> Decimal {
62 match self {
63 SampleTerm::TradingTerm => Decimal::new(1, 0),
64 SampleTerm::ShortTerm => Decimal::new(2, 0),
65 SampleTerm::LongTerm => Decimal::new(3, 0),
66 }
67 }
68}
69
70#[derive(Serialize, Deserialize, Clone, Debug)]
71pub struct FundConfig {
72 pub token: String,
73 pub trading_strategy: TradingStrategy,
74 pub balance_per_strategy: Decimal,
75 pub risk_reward: Decimal,
76 pub take_profit_ratio: Option<Decimal>,
77 pub atr_spread: Option<Decimal>,
78 pub atr_term: SampleTerm,
79 pub open_minutes: i64,
80}
81
82#[derive(Serialize, Deserialize, Clone, Debug)]
83pub struct AppState {
84 pub id: u32,
85 pub last_execution_time: Option<SystemTime>,
86 pub last_equity: Option<Decimal>,
87 pub ave_dd: Option<Decimal>,
88 pub max_dd: Option<Decimal>,
89 pub cumulative_return: Decimal,
90 pub cumulative_dd: Decimal,
91 pub score: Option<Decimal>,
92 pub score_2: Option<Decimal>,
93 pub score_3: Option<Decimal>,
94 pub curcuit_break: bool,
95 pub error_time: Vec<String>,
96 pub max_invested_amount: Decimal,
97 pub fund_configs: Option<Vec<FundConfig>>,
98}
99
100impl Default for AppState {
101 fn default() -> Self {
102 Self {
103 id: 1,
104 last_execution_time: None,
105 last_equity: None,
106 ave_dd: None,
107 max_dd: None,
108 cumulative_return: Decimal::ZERO,
109 cumulative_dd: Decimal::ZERO,
110 score: None,
111 score_2: None,
112 score_3: None,
113 curcuit_break: false,
114 error_time: vec![],
115 max_invested_amount: Decimal::ZERO,
116 fund_configs: Some(vec![]),
117 }
118 }
119}
120
121#[derive(Serialize, Deserialize, Clone, Debug, Default)]
122pub struct PnlLog {
123 pub id: Option<u32>,
124 pub date: String,
125 pub pnl: Decimal,
126}
127
128impl HasId for PnlLog {
129 fn id(&self) -> Option<u32> {
130 self.id
131 }
132}
133
134#[derive(Serialize, Deserialize, Debug, Clone, Default)]
135pub struct PricePoint {
136 pub timestamp: i64,
137 pub timestamp_str: String,
138 pub price: Decimal,
139 pub volume: Option<Decimal>,
140 pub num_trades: Option<u64>,
141 pub funding_rate: Option<Decimal>,
142 pub open_interest: Option<Decimal>,
143 pub oracle_price: Option<Decimal>,
144}
145
146impl PricePoint {
147 pub fn new(
148 price: Decimal,
149 timestamp: Option<i64>,
150 volume: Option<Decimal>,
151 num_trades: Option<u64>,
152 funding_rate: Option<Decimal>,
153 open_interest: Option<Decimal>,
154 oracle_price: Option<Decimal>,
155 ) -> Self {
156 let (local_timestamp, timestamp_str) = get_local_time();
157 let timestamp = timestamp.unwrap_or(local_timestamp);
158 Self {
159 timestamp,
160 timestamp_str,
161 price,
162 volume,
163 num_trades,
164 funding_rate,
165 open_interest,
166 oracle_price,
167 }
168 }
169}
170
171#[derive(Serialize, Deserialize, Clone, Debug, Default)]
172pub struct PriceLog {
173 pub id: Option<u32>,
174 pub name: String,
175 pub token_name: String,
176 pub price_point: PricePoint,
177}
178
179impl HasId for PriceLog {
180 fn id(&self) -> Option<u32> {
181 self.id
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
186pub enum CandlePattern {
187 #[default]
188 None,
189 Hammer,
190 InvertedHammer,
191 BullishEngulfing,
192 BearishEngulfing,
193 Doji,
194 Marubozu,
195 MorningStar,
196 EveningStar,
197 ThreeWhiteSoldiers,
198 ThreeBlackCrows,
199 PiercingPattern,
200 DarkCloudCover,
201 Harami,
202 HaramiCross,
203 SpinningTop,
204}
205
206impl CandlePattern {
207 pub fn to_one_hot(&self) -> [Decimal; 16] {
208 let mut one_hot = [Decimal::ZERO; 16];
209
210 match self {
211 CandlePattern::None => one_hot[0] = Decimal::ONE,
212 CandlePattern::Hammer => one_hot[1] = Decimal::ONE,
213 CandlePattern::InvertedHammer => one_hot[2] = Decimal::ONE,
214 CandlePattern::BullishEngulfing => one_hot[3] = Decimal::ONE,
215 CandlePattern::BearishEngulfing => one_hot[4] = Decimal::ONE,
216 CandlePattern::Doji => one_hot[5] = Decimal::ONE,
217 CandlePattern::Marubozu => one_hot[6] = Decimal::ONE,
218 CandlePattern::MorningStar => one_hot[7] = Decimal::ONE,
219 CandlePattern::EveningStar => one_hot[8] = Decimal::ONE,
220 CandlePattern::ThreeWhiteSoldiers => one_hot[9] = Decimal::ONE,
221 CandlePattern::ThreeBlackCrows => one_hot[10] = Decimal::ONE,
222 CandlePattern::PiercingPattern => one_hot[11] = Decimal::ONE,
223 CandlePattern::DarkCloudCover => one_hot[12] = Decimal::ONE,
224 CandlePattern::Harami => one_hot[13] = Decimal::ONE,
225 CandlePattern::HaramiCross => one_hot[14] = Decimal::ONE,
226 CandlePattern::SpinningTop => one_hot[15] = Decimal::ONE,
227 }
228
229 one_hot
230 }
231}
232
233#[derive(Serialize, Deserialize, Clone, Debug, Default)]
234pub struct DebugLog {
235 pub input_1: Decimal,
236 pub input_2: Decimal,
237 pub input_3: Decimal,
238 pub input_4: Decimal,
239 pub input_5: Decimal,
240 pub input_6: Decimal,
241 pub input_7: Decimal,
242 pub input_8: Decimal,
243 pub input_9: Decimal,
244 pub input_10: Decimal,
245 pub input_11: Decimal,
246 pub input_12: Decimal,
247 pub input_13: Decimal,
248 pub input_14: Decimal,
249 pub input_15: Decimal,
250 pub input_16: Decimal,
251 pub input_17: Decimal,
252 pub input_18: Decimal,
253 pub input_19: Decimal,
254 pub input_20: Decimal,
255 pub input_21: Decimal,
256 pub input_22: Decimal,
257 pub input_23: Decimal,
258 pub input_24: Decimal,
259 pub input_25: Decimal,
260 pub input_26: Decimal,
261 pub input_27: Decimal,
262 pub input_28: Decimal,
263 pub input_29: Decimal,
264 pub input_30: CandlePattern,
265 pub input_31: CandlePattern,
266 pub input_32: CandlePattern,
267 pub input_33: CandlePattern,
268 pub input_34: CandlePattern,
269 pub input_35: CandlePattern,
270 pub input_36: CandlePattern,
271 pub input_37: CandlePattern,
272 pub input_38: CandlePattern,
273 pub input_39: CandlePattern,
274 pub output_1: Decimal,
275 pub output_2: Decimal,
276 pub output_3: Option<Decimal>,
277 pub output_4: Option<Decimal>,
278 pub output_5: Option<Decimal>,
279}
280
281#[derive(Serialize, Deserialize, Clone, Debug, Default)]
282pub struct PositionLog {
283 pub id: Option<u32>,
284 pub fund_name: String,
285 pub order_id: String,
286 pub ordered_price: Decimal,
287 pub state: String,
288 pub token_name: String,
289 pub open_time_str: String,
290 pub open_timestamp: i64,
291 pub close_time_str: String,
292 pub average_open_price: Decimal,
293 pub position_type: String,
294 pub close_price: Decimal,
295 pub asset_in_usd: Decimal,
296 pub pnl: Decimal,
297 pub fee: Decimal,
298 pub debug: DebugLog,
299}
300
301#[derive(Serialize, Deserialize)]
302pub struct SerializableModel {
303 pub model: Vec<u8>,
304}
305
306impl HasId for PositionLog {
307 fn id(&self) -> Option<u32> {
308 self.id
309 }
310}
311
312pub struct TransactionLog {
313 counter: Counter,
314 db_r_name: String,
315 db_w_name: String,
316 client_holder: Arc<Mutex<ClientHolder>>,
317}
318
319impl TransactionLog {
320 pub async fn new(
321 max_position_counter: Option<u32>,
322 max_price_counter: Option<u32>,
323 max_pnl_counter: Option<u32>,
324 mongodb_uri: &str,
325 db_r_name: &str,
326 db_w_name: &str,
327 back_test: bool,
328 ) -> Self {
329 let mut client_options = match ClientOptions::parse(mongodb_uri).await {
331 Ok(client_options) => client_options,
332 Err(e) => {
333 panic!("{:?}", e);
334 }
335 };
336 let tls_options = TlsOptions::builder().build();
337 client_options.tls = Some(Tls::Enabled(tls_options));
338 let client_holder = Arc::new(Mutex::new(ClientHolder::new(client_options)));
339
340 let db = shared_mongodb::database::get(&client_holder, &db_w_name)
341 .await
342 .unwrap();
343
344 create_unique_index(&db)
345 .await
346 .expect("Error creating unique index");
347
348 if back_test {
349 if let Err(e) = Self::delete_all_positions(&db).await {
350 panic!("delete_all_positions failed: {:?}", e);
351 }
352 if let Err(e) = Self::delete_app_state(&db).await {
353 panic!("delete_app_state failed: {:?}", e);
354 }
355 }
356
357 let last_position_counter =
358 TransactionLog::get_last_transaction_id(&db, CounterType::Position).await;
359 let last_price_counter =
360 TransactionLog::get_last_transaction_id(&db, CounterType::Price).await;
361 let last_pnl_counter = TransactionLog::get_last_transaction_id(&db, CounterType::Pnl).await;
362
363 let counter = Counter::new(
364 max_position_counter,
365 max_price_counter,
366 max_pnl_counter,
367 last_position_counter,
368 last_price_counter,
369 last_pnl_counter,
370 );
371
372 log::warn!(
373 "position = {}/{:?}, price = {}/{:?}, pnl = {}/{:?}",
374 last_position_counter,
375 max_position_counter,
376 last_price_counter,
377 max_price_counter,
378 last_pnl_counter,
379 max_pnl_counter,
380 );
381
382 TransactionLog {
383 counter,
384 db_r_name: db_r_name.to_owned(),
385 db_w_name: db_w_name.to_owned(),
386 client_holder,
387 }
388 }
389
390 pub fn increment_counter(&self, counter_type: CounterType) -> u32 {
391 self.counter.increment(counter_type)
392 }
393
394 pub async fn get_last_transaction_id(db: &Database, counter_type: CounterType) -> u32 {
395 match counter_type {
396 CounterType::Position => get_last_id::<PositionLog>(db).await,
397 CounterType::Price => get_last_id::<PriceLog>(db).await,
398 CounterType::Pnl => get_last_id::<PnlLog>(db).await,
399 }
400 }
401
402 pub async fn get_w_db(&self) -> Option<Database> {
403 self.get_db(false).await
404 }
405
406 pub async fn get_r_db(&self) -> Option<Database> {
407 self.get_db(true).await
408 }
409
410 async fn get_db(&self, read: bool) -> Option<Database> {
411 let db_name = if read {
412 &self.db_r_name
413 } else {
414 &self.db_w_name
415 };
416 let db = match database::get(&self.client_holder, db_name).await {
417 Ok(db) => Some(db),
418 Err(e) => {
419 log::error!("get_db: {:?}", e);
420 None
421 }
422 };
423 db
424 }
425
426 pub async fn update_transaction(
427 db: &Database,
428 item: &PositionLog,
429 ) -> Result<(), Box<dyn error::Error>> {
430 update_item(db, item).await?;
431 Ok(())
432 }
433
434 pub async fn update_price(db: &Database, item: PriceLog) -> Result<(), Box<dyn error::Error>> {
435 update_item(db, &item).await?;
436 Ok(())
437 }
438
439 pub async fn copy_price(db_r: &Database, db_w: &Database, limit: Option<u32>) {
440 let item = PriceLog::default();
441 let items = {
442 match search_items(db_r, &item, SearchMode::Ascending, limit, None, Some("id")).await {
443 Ok(items) => items,
444 Err(e) => {
445 log::error!("get price: {:?}", e);
446 return;
447 }
448 }
449 };
450 log::debug!("get prices: num = {}", items.len());
451
452 for item in &items {
453 match insert_item(db_w, item).await {
454 Ok(_) => {}
455 Err(e) => {
456 log::error!("write price: {:?}", e);
457 return;
458 }
459 }
460 }
461 }
462
463 pub async fn copy_position(db_r: &Database, db_w: &Database, limit: Option<u32>) {
464 let item = PositionLog::default();
465 let items = {
466 match search_items(db_r, &item, SearchMode::Ascending, limit, None, Some("id")).await {
467 Ok(items) => items,
468 Err(e) => {
469 log::error!("get position: {:?}", e);
470 return;
471 }
472 }
473 };
474 log::debug!("get position: num = {}", items.len());
475
476 for item in &items {
477 match insert_item(db_w, item).await {
478 Ok(_) => {}
479 Err(e) => {
480 log::error!("write position: {:?}", e);
481 return;
482 }
483 }
484 }
485 }
486
487 pub async fn get_price_market_data(
488 db: &Database,
489 limit: Option<u32>,
490 id: Option<u32>,
491 is_ascend: bool,
492 ) -> HashMap<String, HashMap<String, Vec<PricePoint>>> {
493 let search_mode = if is_ascend {
494 SearchMode::Ascending
495 } else {
496 SearchMode::Descending
497 };
498 let sort_key = Some("price_point.timestamp");
499 let item = PriceLog::default();
500
501 let items = match id {
502 Some(id) => search_item(db, &item, Some(id), sort_key)
503 .await
504 .map(|item| vec![item]),
505 None => search_items(db, &item, search_mode, limit, None, sort_key).await,
506 };
507
508 let Ok(mut items) = items else {
509 log::warn!("get_price_market_data: search failed");
510 return HashMap::new();
511 };
512
513 items.sort_by_key(|p| p.price_point.timestamp);
514
515 let mut result = HashMap::new();
516 for price_log in items {
517 result
518 .entry(price_log.name)
519 .or_insert_with(HashMap::new)
520 .entry(price_log.token_name)
521 .or_insert_with(Vec::new)
522 .push(price_log.price_point);
523 }
524
525 result
526 }
527
528 pub async fn get_all_positions(
529 db: &Database,
530 limit: Option<u32>,
531 id: Option<u32>,
532 is_ascend: bool,
533 ) -> Vec<PositionLog> {
534 let search_mode = if is_ascend {
535 SearchMode::Ascending
536 } else {
537 SearchMode::Descending
538 };
539 let sort_key = Some("open_timestamp");
540 let item = PositionLog::default();
541
542 let items = if let Some(id) = id {
543 match search_item(db, &item, Some(id), sort_key).await {
544 Ok(position) => vec![position],
545 Err(e) => {
546 log::warn!("get_all_positions: {:?}", e);
547 vec![]
548 }
549 }
550 } else {
551 match search_items(db, &item, search_mode, limit, None, sort_key).await {
552 Ok(positions) => positions,
553 Err(e) => {
554 log::warn!("get_all_positions: {:?}", e);
555 vec![]
556 }
557 }
558 };
559
560 items
561 }
562
563 async fn delete_all_positions(db: &Database) -> Result<(), Box<dyn error::Error>> {
564 let item = PositionLog::default();
565 delete_item_all(db, &item).await
566 }
567
568 pub async fn insert_pnl(db: &Database, item: PnlLog) -> Result<(), Box<dyn error::Error>> {
569 insert_item(db, &item).await?;
570 Ok(())
571 }
572
573 pub async fn get_app_state(db: &Database) -> AppState {
574 let item = AppState::default();
575 match search_item(db, &item, Some(1), Some("id")).await {
576 Ok(item) => item,
577 Err(e) => {
578 log::warn!("get_app_state: {:?}", e);
579 item
580 }
581 }
582 }
583
584 async fn delete_app_state(db: &Database) -> Result<(), Box<dyn error::Error>> {
585 let item = AppState::default();
586 delete_item_all(db, &item).await
587 }
588
589 pub async fn update_app_state(
590 db: &Database,
591 last_execution_time: Option<SystemTime>,
592 last_equity: Option<Decimal>,
593 ave_dd: Option<Decimal>,
594 max_dd: Option<Decimal>,
595 cumulative_return: Option<Decimal>,
596 cumulative_dd: Option<Decimal>,
597 score: Option<Decimal>,
598 score_2: Option<Decimal>,
599 score_3: Option<Decimal>,
600 curcuit_break: bool,
601 error_time: Option<String>,
602 max_invested_amount: Option<Decimal>,
603 fund_configs: Option<Vec<FundConfig>>,
604 ) -> Result<(), Box<dyn error::Error>> {
605 let item = AppState::default();
606 let mut item = match search_item(db, &item, Some(1), Some("id")).await {
607 Ok(prev_item) => prev_item,
608 Err(_) => item,
609 };
610
611 if last_execution_time.is_some() {
612 item.last_execution_time = last_execution_time;
613 }
614
615 if let Some(last_equity) = last_equity {
616 item.last_equity = Some(last_equity.round());
617 }
618
619 if let Some(ave_dd) = ave_dd {
620 item.ave_dd = Some(ave_dd.round());
621 }
622
623 if let Some(max_dd_val) = max_dd {
624 if item
625 .max_dd
626 .map_or(true, |item_max_dd| max_dd_val > item_max_dd)
627 {
628 item.max_dd = Some(max_dd_val.round());
629 }
630 }
631
632 if let Some(cumulative_return) = cumulative_return {
633 item.cumulative_return += cumulative_return.round();
634 }
635
636 if let Some(cumulative_dd) = cumulative_dd {
637 item.cumulative_dd += cumulative_dd.round();
638 }
639
640 if score.is_some() {
641 item.score = score;
642 }
643
644 if score_2.is_some() {
645 item.score_2 = score_2;
646 }
647
648 if score_3.is_some() {
649 item.score_3 = score_3;
650 }
651
652 item.curcuit_break = curcuit_break;
653
654 if let Some(error_time) = error_time {
655 item.error_time.push(error_time);
656 }
657
658 if let Some(max_invested_amount) = max_invested_amount {
659 item.max_invested_amount = max_invested_amount.round();
660 }
661
662 if let Some(fund_configs) = fund_configs {
663 item.fund_configs = Some(fund_configs);
664 }
665
666 update_item(db, &item).await?;
667 Ok(())
668 }
669}
670
671#[derive(Clone)]
672pub struct ModelParams {
673 db_name: String,
674 client_holder: Arc<Mutex<ClientHolder>>,
675 collection_name: String,
676 save_to_db: bool,
677 file_path: Option<String>,
678}
679
680impl ModelParams {
681 pub async fn new(
682 mongodb_uri: &str,
683 db_name: &str,
684 save_to_db: bool,
685 file_path: Option<String>,
686 ) -> Self {
687 let mut client_options = match ClientOptions::parse(mongodb_uri).await {
689 Ok(client_options) => client_options,
690 Err(e) => {
691 panic!("{:?}", e);
692 }
693 };
694 let tls_options = TlsOptions::builder().build();
695 client_options.tls = Some(Tls::Enabled(tls_options));
696 let client_holder = Arc::new(Mutex::new(ClientHolder::new(client_options)));
697
698 ModelParams {
699 db_name: db_name.to_owned(),
700 client_holder,
701 collection_name: "model_params".to_owned(),
702 save_to_db,
703 file_path,
704 }
705 }
706
707 async fn get_db(&self) -> Option<Database> {
708 let db = match database::get(&self.client_holder, &self.db_name).await {
709 Ok(db) => Some(db),
710 Err(e) => {
711 log::error!("get_db: {:?}", e);
712 None
713 }
714 };
715 db
716 }
717
718 pub async fn save_model(
719 &self,
720 key: &str,
721 model: &SerializableModel,
722 ) -> Result<(), Box<dyn std::error::Error>> {
723 if self.save_to_db {
724 self.save_model_to_db(key, model).await
725 } else {
726 self.save_model_to_file(key, model).await
727 }
728 }
729
730 pub async fn load_model(
731 &self,
732 key: &str,
733 ) -> Result<SerializableModel, Box<dyn std::error::Error>> {
734 if self.save_to_db {
735 self.load_model_from_db(key).await
736 } else {
737 self.load_model_from_file(key).await
738 }
739 }
740
741 async fn save_model_to_db(
742 &self,
743 key: &str,
744 model: &SerializableModel,
745 ) -> Result<(), Box<dyn std::error::Error>> {
746 let db = self.get_db().await.ok_or("no db")?;
747 let collection: Collection<Document> = db.collection(&self.collection_name);
748 let serialized_model = bincode::serialize(model)?;
749
750 let document = doc! {
751 "key": key,
752 "model": Bson::Binary(mongodb::bson::Binary {
753 subtype: mongodb::bson::spec::BinarySubtype::Generic,
754 bytes: serialized_model
755 })
756 };
757
758 collection
759 .update_one(
760 doc! { "key": key },
761 doc! { "$set": document },
762 mongodb::options::UpdateOptions::builder()
763 .upsert(true)
764 .build(),
765 )
766 .await?;
767 Ok(())
768 }
769
770 async fn load_model_from_db(
771 &self,
772 key: &str,
773 ) -> Result<SerializableModel, Box<dyn std::error::Error>> {
774 let db = self.get_db().await.ok_or("no db")?;
775 let collection: Collection<Document> = db.collection(&self.collection_name);
776
777 let filter = doc! { "key": key };
778 let document = collection
779 .find_one(filter, None)
780 .await?
781 .ok_or("No model found in the collection")?;
782
783 if let Some(Bson::Binary(model_bytes)) = document.get("model") {
784 let model: SerializableModel = bincode::deserialize(&model_bytes.bytes)?;
785 Ok(model)
786 } else {
787 Err("Invalid data format".into())
788 }
789 }
790
791 async fn save_model_to_file(
792 &self,
793 key: &str,
794 model: &SerializableModel,
795 ) -> Result<(), Box<dyn std::error::Error>> {
796 let serialized_model = bincode::serialize(model)?;
797 let file_name = format!("{}.bin", key);
798
799 let file_path = if let Some(ref dir) = self.file_path {
800 Path::new(dir).join(file_name)
801 } else {
802 Path::new(&file_name).to_path_buf()
803 };
804
805 let mut file = File::create(&file_path)?;
806 file.write_all(&serialized_model)?;
807 Ok(())
808 }
809
810 async fn load_model_from_file(
811 &self,
812 key: &str,
813 ) -> Result<SerializableModel, Box<dyn std::error::Error>> {
814 let file_name = format!("{}.bin", key);
815
816 let file_path = if let Some(ref dir) = self.file_path {
817 Path::new(dir).join(file_name)
818 } else {
819 Path::new(&file_name).to_path_buf()
820 };
821
822 let mut file = File::open(&file_path)?;
823 let mut buffer = Vec::new();
824 file.read_to_end(&mut buffer)?;
825 let model: SerializableModel = bincode::deserialize(&buffer)?;
826 Ok(model)
827 }
828}