|
@@ -16,7 +16,7 @@ use tickers_table::TickersTable;
|
|
|
use layout::{SerializableDashboard, Sidebar};
|
|
use layout::{SerializableDashboard, Sidebar};
|
|
|
use style::{get_icon_text, Icon, ICON_BYTES};
|
|
use style::{get_icon_text, Icon, ICON_BYTES};
|
|
|
use screen::{
|
|
use screen::{
|
|
|
- create_button, dashboard, handle_error, Notification, UserTimezone,
|
|
|
|
|
|
|
+ create_button, dashboard, handle_error, Notification, UserTimezone,
|
|
|
dashboard::{Dashboard, pane},
|
|
dashboard::{Dashboard, pane},
|
|
|
modal::{confirmation_modal, dashboard_modal}
|
|
modal::{confirmation_modal, dashboard_modal}
|
|
|
};
|
|
};
|
|
@@ -29,7 +29,7 @@ use iced::{
|
|
|
padding, Alignment, Element, Length, Point, Size, Subscription, Task, Theme,
|
|
padding, Alignment, Element, Length, Point, Size, Subscription, Task, Theme,
|
|
|
};
|
|
};
|
|
|
use iced_futures::MaybeSend;
|
|
use iced_futures::MaybeSend;
|
|
|
-use futures::TryFutureExt;
|
|
|
|
|
|
|
+use futures::{StreamExt, TryFutureExt};
|
|
|
use std::{collections::HashMap, vec, future::Future};
|
|
use std::{collections::HashMap, vec, future::Future};
|
|
|
|
|
|
|
|
fn main() {
|
|
fn main() {
|
|
@@ -164,7 +164,7 @@ impl State {
|
|
|
Exchange::MARKET_TYPES.iter()
|
|
Exchange::MARKET_TYPES.iter()
|
|
|
.flat_map(|(exchange, market_type)| {
|
|
.flat_map(|(exchange, market_type)| {
|
|
|
tickers_info.insert(*exchange, HashMap::new());
|
|
tickers_info.insert(*exchange, HashMap::new());
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
let ticksizes_task = match exchange {
|
|
let ticksizes_task = match exchange {
|
|
|
Exchange::BinanceFutures | Exchange::BinanceSpot => {
|
|
Exchange::BinanceFutures | Exchange::BinanceSpot => {
|
|
|
fetch_ticker_info(*exchange, binance::fetch_ticksize(*market_type))
|
|
fetch_ticker_info(*exchange, binance::fetch_ticksize(*market_type))
|
|
@@ -286,7 +286,7 @@ impl State {
|
|
|
opened_windows.push(self.main_window.id);
|
|
opened_windows.push(self.main_window.id);
|
|
|
|
|
|
|
|
return window::collect_window_specs(
|
|
return window::collect_window_specs(
|
|
|
- opened_windows,
|
|
|
|
|
|
|
+ opened_windows,
|
|
|
Message::SaveAndExit
|
|
Message::SaveAndExit
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
@@ -360,7 +360,7 @@ impl State {
|
|
|
Message::ErrorOccurred(err) => {
|
|
Message::ErrorOccurred(err) => {
|
|
|
return match err {
|
|
return match err {
|
|
|
InternalError::Fetch(err) => handle_error(
|
|
InternalError::Fetch(err) => handle_error(
|
|
|
- &err,
|
|
|
|
|
|
|
+ &err,
|
|
|
"Failed to fetch data",
|
|
"Failed to fetch data",
|
|
|
Message::Notification,
|
|
Message::Notification,
|
|
|
),
|
|
),
|
|
@@ -380,13 +380,13 @@ impl State {
|
|
|
.map(|&popout_id| window::close(popout_id))
|
|
.map(|&popout_id| window::close(popout_id))
|
|
|
.collect::<Vec<_>>(),
|
|
.collect::<Vec<_>>(),
|
|
|
)
|
|
)
|
|
|
- .then(|_: Task<window::Id>| Task::none());
|
|
|
|
|
|
|
+ .then(|_: Task<window::Id>| Task::none());
|
|
|
|
|
|
|
|
return window_tasks.chain(
|
|
return window_tasks.chain(
|
|
|
dashboard
|
|
dashboard
|
|
|
.reset_layout()
|
|
.reset_layout()
|
|
|
.map(Message::Dashboard)
|
|
.map(Message::Dashboard)
|
|
|
- );
|
|
|
|
|
|
|
+ );
|
|
|
}
|
|
}
|
|
|
Message::LayoutSelected(new_layout_id) => {
|
|
Message::LayoutSelected(new_layout_id) => {
|
|
|
let active_popout_keys = self
|
|
let active_popout_keys = self
|
|
@@ -402,15 +402,15 @@ impl State {
|
|
|
.map(|&popout_id| window::close(popout_id))
|
|
.map(|&popout_id| window::close(popout_id))
|
|
|
.collect::<Vec<_>>(),
|
|
.collect::<Vec<_>>(),
|
|
|
)
|
|
)
|
|
|
- .then(|_: Task<window::Id>| Task::none());
|
|
|
|
|
|
|
+ .then(|_: Task<window::Id>| Task::none());
|
|
|
|
|
|
|
|
return window::collect_window_specs(
|
|
return window::collect_window_specs(
|
|
|
active_popout_keys,
|
|
active_popout_keys,
|
|
|
dashboard::Message::SavePopoutSpecs,
|
|
dashboard::Message::SavePopoutSpecs,
|
|
|
)
|
|
)
|
|
|
- .map(Message::Dashboard)
|
|
|
|
|
- .chain(window_tasks)
|
|
|
|
|
- .chain(Task::done(Message::LoadLayout(new_layout_id)));
|
|
|
|
|
|
|
+ .map(Message::Dashboard)
|
|
|
|
|
+ .chain(window_tasks)
|
|
|
|
|
+ .chain(Task::done(Message::LoadLayout(new_layout_id)));
|
|
|
}
|
|
}
|
|
|
Message::LoadLayout(layout_id) => {
|
|
Message::LoadLayout(layout_id) => {
|
|
|
self.last_active_layout = layout_id;
|
|
self.last_active_layout = layout_id;
|
|
@@ -452,8 +452,8 @@ impl State {
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
return Task::batch(vec![
|
|
return Task::batch(vec![
|
|
|
- bybit_linear_fetch,
|
|
|
|
|
- binance_linear_fetch,
|
|
|
|
|
|
|
+ bybit_linear_fetch,
|
|
|
|
|
+ binance_linear_fetch,
|
|
|
binance_spot_fetch,
|
|
binance_spot_fetch,
|
|
|
bybit_spot_fetch,
|
|
bybit_spot_fetch,
|
|
|
]);
|
|
]);
|
|
@@ -495,7 +495,7 @@ impl State {
|
|
|
self.layouts.values_mut().for_each(|dashboard| {
|
|
self.layouts.values_mut().for_each(|dashboard| {
|
|
|
dashboard.toggle_trade_fetch(checked, &self.main_window);
|
|
dashboard.toggle_trade_fetch(checked, &self.main_window);
|
|
|
});
|
|
});
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if checked {
|
|
if checked {
|
|
|
self.confirmation_dialog = None;
|
|
self.confirmation_dialog = None;
|
|
|
}
|
|
}
|
|
@@ -513,23 +513,23 @@ impl State {
|
|
|
if id != self.main_window.id {
|
|
if id != self.main_window.id {
|
|
|
return container(
|
|
return container(
|
|
|
dashboard
|
|
dashboard
|
|
|
- .view_window(
|
|
|
|
|
- id,
|
|
|
|
|
- &self.main_window,
|
|
|
|
|
- self.layout_locked,
|
|
|
|
|
- &self.timezone,
|
|
|
|
|
- )
|
|
|
|
|
- .map(Message::Dashboard)
|
|
|
|
|
|
|
+ .view_window(
|
|
|
|
|
+ id,
|
|
|
|
|
+ &self.main_window,
|
|
|
|
|
+ self.layout_locked,
|
|
|
|
|
+ &self.timezone,
|
|
|
|
|
+ )
|
|
|
|
|
+ .map(Message::Dashboard)
|
|
|
)
|
|
)
|
|
|
- .padding(padding::top(if cfg!(target_os = "macos") { 20 } else { 0 }))
|
|
|
|
|
- .into();
|
|
|
|
|
|
|
+ .padding(padding::top(if cfg!(target_os = "macos") { 20 } else { 0 }))
|
|
|
|
|
+ .into();
|
|
|
} else {
|
|
} else {
|
|
|
let tooltip_position = if self.sidebar_location == Sidebar::Left {
|
|
let tooltip_position = if self.sidebar_location == Sidebar::Left {
|
|
|
tooltip::Position::Right
|
|
tooltip::Position::Right
|
|
|
} else {
|
|
} else {
|
|
|
tooltip::Position::Left
|
|
tooltip::Position::Left
|
|
|
};
|
|
};
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
let sidebar = {
|
|
let sidebar = {
|
|
|
let nav_buttons = {
|
|
let nav_buttons = {
|
|
|
let layout_lock_button = {
|
|
let layout_lock_button = {
|
|
@@ -539,13 +539,13 @@ impl State {
|
|
|
Icon::Locked
|
|
Icon::Locked
|
|
|
} else {
|
|
} else {
|
|
|
Icon::Unlocked
|
|
Icon::Unlocked
|
|
|
- },
|
|
|
|
|
|
|
+ },
|
|
|
14,
|
|
14,
|
|
|
).width(24).align_x(Alignment::Center),
|
|
).width(24).align_x(Alignment::Center),
|
|
|
Message::ToggleLayoutLock,
|
|
Message::ToggleLayoutLock,
|
|
|
Some("Layout Lock"),
|
|
Some("Layout Lock"),
|
|
|
tooltip_position,
|
|
tooltip_position,
|
|
|
- |theme: &Theme, status: button::Status|
|
|
|
|
|
|
|
+ |theme: &Theme, status: button::Status|
|
|
|
style::button_transparent(theme, status, false),
|
|
style::button_transparent(theme, status, false),
|
|
|
)
|
|
)
|
|
|
};
|
|
};
|
|
@@ -570,7 +570,7 @@ impl State {
|
|
|
};
|
|
};
|
|
|
let layout_modal_button = {
|
|
let layout_modal_button = {
|
|
|
let is_active = matches!(self.active_modal, DashboardModal::Layout);
|
|
let is_active = matches!(self.active_modal, DashboardModal::Layout);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
create_button(
|
|
create_button(
|
|
|
get_icon_text(Icon::Layout, 14)
|
|
get_icon_text(Icon::Layout, 14)
|
|
|
.width(24)
|
|
.width(24)
|
|
@@ -589,7 +589,7 @@ impl State {
|
|
|
};
|
|
};
|
|
|
let ticker_search_button = {
|
|
let ticker_search_button = {
|
|
|
let is_active = self.show_tickers_dashboard;
|
|
let is_active = self.show_tickers_dashboard;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
create_button(
|
|
create_button(
|
|
|
get_icon_text(Icon::Search, 14)
|
|
get_icon_text(Icon::Search, 14)
|
|
|
.width(24)
|
|
.width(24)
|
|
@@ -610,8 +610,8 @@ impl State {
|
|
|
Space::with_height(Length::Fill),
|
|
Space::with_height(Length::Fill),
|
|
|
settings_modal_button,
|
|
settings_modal_button,
|
|
|
]
|
|
]
|
|
|
- .width(32)
|
|
|
|
|
- .spacing(4)
|
|
|
|
|
|
|
+ .width(32)
|
|
|
|
|
+ .spacing(4)
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let tickers_table = {
|
|
let tickers_table = {
|
|
@@ -621,7 +621,7 @@ impl State {
|
|
|
self.tickers_table.view(size).map(Message::TickersTable)
|
|
self.tickers_table.view(size).map(Message::TickersTable)
|
|
|
})
|
|
})
|
|
|
]
|
|
]
|
|
|
- .width(200)
|
|
|
|
|
|
|
+ .width(200)
|
|
|
} else {
|
|
} else {
|
|
|
column![]
|
|
column![]
|
|
|
}
|
|
}
|
|
@@ -641,12 +641,12 @@ impl State {
|
|
|
]
|
|
]
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- .spacing(4)
|
|
|
|
|
|
|
+ .spacing(4)
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let dashboard_view = dashboard
|
|
let dashboard_view = dashboard
|
|
|
.view(
|
|
.view(
|
|
|
- &self.main_window,
|
|
|
|
|
|
|
+ &self.main_window,
|
|
|
self.layout_locked,
|
|
self.layout_locked,
|
|
|
&self.timezone,
|
|
&self.timezone,
|
|
|
)
|
|
)
|
|
@@ -697,22 +697,22 @@ impl State {
|
|
|
|
|
|
|
|
let trade_fetch_checkbox = {
|
|
let trade_fetch_checkbox = {
|
|
|
let is_active = dashboard.trade_fetch_enabled;
|
|
let is_active = dashboard.trade_fetch_enabled;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
let checkbox = iced::widget::checkbox("Fetch trades (Binance)", is_active)
|
|
let checkbox = iced::widget::checkbox("Fetch trades (Binance)", is_active)
|
|
|
.on_toggle(|checked| {
|
|
.on_toggle(|checked| {
|
|
|
- if checked {
|
|
|
|
|
- Message::ToggleDialogModal(
|
|
|
|
|
- Some(
|
|
|
|
|
- "This might be unreliable and take some time to complete"
|
|
|
|
|
|
|
+ if checked {
|
|
|
|
|
+ Message::ToggleDialogModal(
|
|
|
|
|
+ Some(
|
|
|
|
|
+ "This might be unreliable and take some time to complete"
|
|
|
.to_string()
|
|
.to_string()
|
|
|
- ),
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
- Message::ToggleTradeFetch(false)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ ),
|
|
|
|
|
+ )
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Message::ToggleTradeFetch(false)
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
);
|
|
);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
tooltip(
|
|
tooltip(
|
|
|
checkbox,
|
|
checkbox,
|
|
|
Some("Try to fetch trades for footprint charts"),
|
|
Some("Try to fetch trades for footprint charts"),
|
|
@@ -722,7 +722,7 @@ impl State {
|
|
|
|
|
|
|
|
let theme_picklist =
|
|
let theme_picklist =
|
|
|
pick_list(all_themes, Some(self.theme.clone()), Message::ThemeSelected);
|
|
pick_list(all_themes, Some(self.theme.clone()), Message::ThemeSelected);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
let timezone_picklist = pick_list(
|
|
let timezone_picklist = pick_list(
|
|
|
[UserTimezone::Utc, UserTimezone::Local],
|
|
[UserTimezone::Utc, UserTimezone::Local],
|
|
|
Some(self.timezone),
|
|
Some(self.timezone),
|
|
@@ -748,12 +748,12 @@ impl State {
|
|
|
trade_fetch_checkbox,
|
|
trade_fetch_checkbox,
|
|
|
].spacing(4),
|
|
].spacing(4),
|
|
|
]
|
|
]
|
|
|
- .spacing(16),
|
|
|
|
|
|
|
+ .spacing(16),
|
|
|
)
|
|
)
|
|
|
- .align_x(Alignment::Start)
|
|
|
|
|
- .max_width(500)
|
|
|
|
|
- .padding(24)
|
|
|
|
|
- .style(style::dashboard_modal)
|
|
|
|
|
|
|
+ .align_x(Alignment::Start)
|
|
|
|
|
+ .max_width(500)
|
|
|
|
|
+ .padding(24)
|
|
|
|
|
+ .style(style::dashboard_modal)
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let (align_x, padding) = match self.sidebar_location {
|
|
let (align_x, padding) = match self.sidebar_location {
|
|
@@ -783,15 +783,15 @@ impl State {
|
|
|
]
|
|
]
|
|
|
.spacing(8),
|
|
.spacing(8),
|
|
|
]
|
|
]
|
|
|
- .align_x(Alignment::Center)
|
|
|
|
|
- .spacing(16),
|
|
|
|
|
|
|
+ .align_x(Alignment::Center)
|
|
|
|
|
+ .spacing(16),
|
|
|
)
|
|
)
|
|
|
- .padding(24)
|
|
|
|
|
- .style(style::dashboard_modal);
|
|
|
|
|
-
|
|
|
|
|
|
|
+ .padding(24)
|
|
|
|
|
+ .style(style::dashboard_modal);
|
|
|
|
|
+
|
|
|
confirmation_modal(
|
|
confirmation_modal(
|
|
|
- base_content,
|
|
|
|
|
- dialog_content,
|
|
|
|
|
|
|
+ base_content,
|
|
|
|
|
+ dialog_content,
|
|
|
Message::ToggleDialogModal(None)
|
|
Message::ToggleDialogModal(None)
|
|
|
)
|
|
)
|
|
|
} else {
|
|
} else {
|
|
@@ -818,7 +818,7 @@ impl State {
|
|
|
Some("Layouts won't be saved if app exited abruptly"),
|
|
Some("Layouts won't be saved if app exited abruptly"),
|
|
|
tooltip::Position::Top,
|
|
tooltip::Position::Top,
|
|
|
);
|
|
);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// Pane management
|
|
// Pane management
|
|
|
let reset_pane_button = tooltip(
|
|
let reset_pane_button = tooltip(
|
|
|
button(text("Reset").align_x(Alignment::Center))
|
|
button(text("Reset").align_x(Alignment::Center))
|
|
@@ -871,19 +871,19 @@ impl State {
|
|
|
.align_x(Alignment::Center)
|
|
.align_x(Alignment::Center)
|
|
|
.spacing(8),
|
|
.spacing(8),
|
|
|
]
|
|
]
|
|
|
- .align_x(Alignment::Center)
|
|
|
|
|
- .spacing(32),
|
|
|
|
|
|
|
+ .align_x(Alignment::Center)
|
|
|
|
|
+ .spacing(32),
|
|
|
)
|
|
)
|
|
|
- .width(280)
|
|
|
|
|
- .padding(24)
|
|
|
|
|
- .style(style::dashboard_modal)
|
|
|
|
|
|
|
+ .width(280)
|
|
|
|
|
+ .padding(24)
|
|
|
|
|
+ .style(style::dashboard_modal)
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let (align_x, padding) = match self.sidebar_location {
|
|
let (align_x, padding) = match self.sidebar_location {
|
|
|
Sidebar::Left => (Alignment::Start, padding::left(48).top(40)),
|
|
Sidebar::Left => (Alignment::Start, padding::left(48).top(40)),
|
|
|
Sidebar::Right => (Alignment::End, padding::right(48).top(40)),
|
|
Sidebar::Right => (Alignment::End, padding::right(48).top(40)),
|
|
|
};
|
|
};
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
dashboard_modal(
|
|
dashboard_modal(
|
|
|
base,
|
|
base,
|
|
|
manage_layout_modal,
|
|
manage_layout_modal,
|
|
@@ -927,26 +927,16 @@ impl State {
|
|
|
let ticker: Ticker = *ticker;
|
|
let ticker: Ticker = *ticker;
|
|
|
|
|
|
|
|
let depth_stream = match exchange {
|
|
let depth_stream = match exchange {
|
|
|
- Exchange::BinanceFutures => Subscription::run_with_id(
|
|
|
|
|
- ticker,
|
|
|
|
|
- binance::connect_market_stream(ticker),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
- Exchange::BybitLinear => Subscription::run_with_id(
|
|
|
|
|
- ticker,
|
|
|
|
|
- bybit::connect_market_stream(ticker),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
- Exchange::BinanceSpot => Subscription::run_with_id(
|
|
|
|
|
- ticker,
|
|
|
|
|
- binance::connect_market_stream(ticker),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
- Exchange::BybitSpot => Subscription::run_with_id(
|
|
|
|
|
- ticker,
|
|
|
|
|
- bybit::connect_market_stream(ticker),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
|
|
+ Exchange::BinanceFutures | Exchange::BinanceSpot => {
|
|
|
|
|
+ let stream = binance::connect_market_stream(ticker)
|
|
|
|
|
+ .map(move |evt| create_market_event(exchange, evt));
|
|
|
|
|
+ Subscription::run_with_id(ticker, stream)
|
|
|
|
|
+ },
|
|
|
|
|
+ Exchange::BybitLinear | Exchange::BybitSpot => {
|
|
|
|
|
+ let stream = bybit::connect_market_stream(ticker)
|
|
|
|
|
+ .map(move |evt| create_market_event(exchange, evt));
|
|
|
|
|
+ Subscription::run_with_id(ticker, stream)
|
|
|
|
|
+ },
|
|
|
};
|
|
};
|
|
|
depth_streams.push(depth_stream);
|
|
depth_streams.push(depth_stream);
|
|
|
}
|
|
}
|
|
@@ -956,27 +946,30 @@ impl State {
|
|
|
if !kline_streams.is_empty() {
|
|
if !kline_streams.is_empty() {
|
|
|
let kline_streams_id: Vec<(Ticker, Timeframe)> = kline_streams.clone();
|
|
let kline_streams_id: Vec<(Ticker, Timeframe)> = kline_streams.clone();
|
|
|
|
|
|
|
|
|
|
+ let market_type = match exchange {
|
|
|
|
|
+ Exchange::BinanceFutures | Exchange::BybitLinear => MarketType::LinearPerps,
|
|
|
|
|
+ Exchange::BinanceSpot | Exchange::BybitSpot => MarketType::Spot,
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
let kline_subscription = match exchange {
|
|
let kline_subscription = match exchange {
|
|
|
- Exchange::BinanceFutures => Subscription::run_with_id(
|
|
|
|
|
- kline_streams_id,
|
|
|
|
|
- binance::connect_kline_stream(kline_streams, MarketType::LinearPerps),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
- Exchange::BybitLinear => Subscription::run_with_id(
|
|
|
|
|
- kline_streams_id,
|
|
|
|
|
- bybit::connect_kline_stream(kline_streams, MarketType::LinearPerps),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
- Exchange::BinanceSpot => Subscription::run_with_id(
|
|
|
|
|
- kline_streams_id,
|
|
|
|
|
- binance::connect_kline_stream(kline_streams, MarketType::Spot),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
- Exchange::BybitSpot => Subscription::run_with_id(
|
|
|
|
|
- kline_streams_id,
|
|
|
|
|
- bybit::connect_kline_stream(kline_streams, MarketType::Spot),
|
|
|
|
|
- )
|
|
|
|
|
- .map(move |event| Message::MarketWsEvent(exchange, event)),
|
|
|
|
|
|
|
+ Exchange::BinanceFutures | Exchange::BinanceSpot => {
|
|
|
|
|
+ let stream = binance::connect_kline_stream(
|
|
|
|
|
+ kline_streams,
|
|
|
|
|
+ market_type,
|
|
|
|
|
+ )
|
|
|
|
|
+ .map(move |evt| create_market_event(exchange, evt));
|
|
|
|
|
+
|
|
|
|
|
+ Subscription::run_with_id(kline_streams_id, stream)
|
|
|
|
|
+ },
|
|
|
|
|
+ Exchange::BybitLinear | Exchange::BybitSpot => {
|
|
|
|
|
+ let stream = bybit::connect_kline_stream(
|
|
|
|
|
+ kline_streams,
|
|
|
|
|
+ market_type,
|
|
|
|
|
+ )
|
|
|
|
|
+ .map(move |evt| create_market_event(exchange, evt));
|
|
|
|
|
+
|
|
|
|
|
+ Subscription::run_with_id(kline_streams_id, stream)
|
|
|
|
|
+ },
|
|
|
};
|
|
};
|
|
|
market_subscriptions.push(kline_subscription);
|
|
market_subscriptions.push(kline_subscription);
|
|
|
}
|
|
}
|
|
@@ -989,7 +982,7 @@ impl State {
|
|
|
let tickers_table_fetch = iced::time::every(std::time::Duration::from_secs(
|
|
let tickers_table_fetch = iced::time::every(std::time::Duration::from_secs(
|
|
|
if self.show_tickers_dashboard { 25 } else { 300 },
|
|
if self.show_tickers_dashboard { 25 } else { 300 },
|
|
|
))
|
|
))
|
|
|
- .map(|_| Message::FetchAndUpdateTickersTable);
|
|
|
|
|
|
|
+ .map(|_| Message::FetchAndUpdateTickersTable);
|
|
|
|
|
|
|
|
let window_events = window_events().map(Message::WindowEvent);
|
|
let window_events = window_events().map(Message::WindowEvent);
|
|
|
|
|
|
|
@@ -1009,15 +1002,19 @@ impl State {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+fn create_market_event(exchange: Exchange, event: data_providers::Event) -> Message {
|
|
|
|
|
+ Message::MarketWsEvent(exchange, event)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
fn fetch_ticker_info<F>(exchange: Exchange, fetch_fn: F) -> Task<Message>
|
|
fn fetch_ticker_info<F>(exchange: Exchange, fetch_fn: F) -> Task<Message>
|
|
|
where
|
|
where
|
|
|
F: Future<
|
|
F: Future<
|
|
|
- Output = Result<
|
|
|
|
|
- HashMap<Ticker, Option<data_providers::TickerInfo>>,
|
|
|
|
|
- data_providers::StreamError,
|
|
|
|
|
- >,
|
|
|
|
|
- > + MaybeSend
|
|
|
|
|
- + 'static,
|
|
|
|
|
|
|
+ Output = Result<
|
|
|
|
|
+ HashMap<Ticker, Option<data_providers::TickerInfo>>,
|
|
|
|
|
+ data_providers::StreamError,
|
|
|
|
|
+ >,
|
|
|
|
|
+ > + MaybeSend
|
|
|
|
|
+ + 'static,
|
|
|
{
|
|
{
|
|
|
Task::perform(
|
|
Task::perform(
|
|
|
fetch_fn.map_err(|err| format!("{err}")),
|
|
fetch_fn.map_err(|err| format!("{err}")),
|
|
@@ -1031,8 +1028,8 @@ where
|
|
|
fn fetch_ticker_prices<F>(exchange: Exchange, fetch_fn: F) -> Task<Message>
|
|
fn fetch_ticker_prices<F>(exchange: Exchange, fetch_fn: F) -> Task<Message>
|
|
|
where
|
|
where
|
|
|
F: Future<Output = Result<HashMap<Ticker, TickerStats>, data_providers::StreamError>>
|
|
F: Future<Output = Result<HashMap<Ticker, TickerStats>, data_providers::StreamError>>
|
|
|
- + MaybeSend
|
|
|
|
|
- + 'static,
|
|
|
|
|
|
|
+ + MaybeSend
|
|
|
|
|
+ + 'static,
|
|
|
{
|
|
{
|
|
|
Task::perform(
|
|
Task::perform(
|
|
|
fetch_fn.map_err(|err| format!("{err}")),
|
|
fetch_fn.map_err(|err| format!("{err}")),
|