timeandsales.rs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. use chrono::NaiveDateTime;
  2. use iced::{
  3. alignment, Element, Length
  4. };
  5. use iced::widget::{Column, Row, Container, Text, container, Space};
  6. use crate::screen::dashboard::pane::Message;
  7. use crate::{style, data_providers::Trade};
  8. struct ConvertedTrade {
  9. time: NaiveDateTime,
  10. price: f32,
  11. qty: f32,
  12. is_sell: bool,
  13. }
  14. pub struct TimeAndSales {
  15. recent_trades: Vec<ConvertedTrade>,
  16. size_filter: f32,
  17. filter_sync_heatmap: bool,
  18. }
  19. impl TimeAndSales {
  20. pub fn new() -> Self {
  21. Self {
  22. recent_trades: Vec::new(),
  23. size_filter: 0.0,
  24. filter_sync_heatmap: false,
  25. }
  26. }
  27. pub fn set_size_filter(&mut self, value: f32) {
  28. self.size_filter = value;
  29. }
  30. pub fn get_size_filter(&self) -> f32 {
  31. self.size_filter
  32. }
  33. pub fn set_filter_sync_heatmap(&mut self, value: bool) {
  34. self.filter_sync_heatmap = value;
  35. }
  36. pub fn get_filter_sync_heatmap(&self) -> bool {
  37. self.filter_sync_heatmap
  38. }
  39. pub fn update(&mut self, trades_buffer: &[Trade]) {
  40. for trade in trades_buffer {
  41. let trade_time = NaiveDateTime::from_timestamp(trade.time / 1000, (trade.time % 1000) as u32 * 1_000_000);
  42. let converted_trade = ConvertedTrade {
  43. time: trade_time,
  44. price: trade.price,
  45. qty: trade.qty,
  46. is_sell: trade.is_sell,
  47. };
  48. self.recent_trades.push(converted_trade);
  49. }
  50. if self.recent_trades.len() > 2000 {
  51. let drain_to = self.recent_trades.len() - 2000;
  52. self.recent_trades.drain(0..drain_to);
  53. }
  54. }
  55. pub fn view(&self) -> Element<'_, Message> {
  56. let mut trades_column = Column::new()
  57. .height(Length::Fill)
  58. .padding(10);
  59. let filtered_trades: Vec<_> = self.recent_trades.iter().filter(|trade| (trade.qty*trade.price) >= self.size_filter).collect();
  60. let max_qty = filtered_trades.iter().map(|trade| trade.qty).fold(0.0, f32::max);
  61. if filtered_trades.is_empty() {
  62. trades_column = trades_column.push(
  63. Text::new("No trades")
  64. .width(Length::Fill)
  65. .height(Length::Fill)
  66. .size(16)
  67. );
  68. } else {
  69. for trade in filtered_trades.iter().rev().take(80) {
  70. let trade: &ConvertedTrade = trade;
  71. let trade_row = Row::new()
  72. .push(
  73. container(Text::new(format!("{}", trade.time.format("%M:%S.%3f"))).size(14))
  74. .width(Length::FillPortion(8)).align_x(alignment::Horizontal::Center)
  75. )
  76. .push(
  77. container(Text::new(format!("{}", trade.price)).size(14))
  78. .width(Length::FillPortion(6))
  79. )
  80. .push(
  81. container(Text::new(if trade.is_sell { "Sell" } else { "Buy" }).size(14))
  82. .width(Length::FillPortion(4)).align_x(alignment::Horizontal::Left)
  83. )
  84. .push(
  85. container(Text::new(format!("{}", trade.qty)).size(14))
  86. .width(Length::FillPortion(4))
  87. );
  88. let color_alpha = trade.qty / max_qty;
  89. trades_column = trades_column.push(container(trade_row)
  90. .style( move |_| if trade.is_sell { style::sell_side_red(color_alpha) } else { style::buy_side_green(color_alpha) }));
  91. trades_column = trades_column.push(Container::new(Space::new(Length::Fixed(0.0), Length::Fixed(5.0))));
  92. }
  93. }
  94. trades_column.into()
  95. }
  96. }