use avin_analyse::TrendAnalytic;
use eframe::egui;
use egui_extras::{Column, TableBuilder};
use egui_file_dialog::FileDialog;
use avin_core::{
Action, Asset, AssetList, DataAction, Event, ExtremumIndicator,
MarketData, Term, TimeFrame,
};
use avin_utils::{CFG, Cmd};
pub struct AssetWidget {
asset_list: AssetList,
current_index: usize,
file_dialog: FileDialog,
event_rx: tokio::sync::mpsc::UnboundedReceiver<Event>,
action_tx: tokio::sync::mpsc::UnboundedSender<Action>,
}
impl AssetWidget {
pub fn new(
event_rx: tokio::sync::mpsc::UnboundedReceiver<Event>,
action_tx: tokio::sync::mpsc::UnboundedSender<Action>,
) -> Self {
let mut path = CFG.dir.asset();
path.push(&CFG.core.default_asset_list);
let asset_list = if Cmd::is_exist(&path) {
AssetList::load(&path).unwrap()
} else {
AssetList::new("Load")
};
let path = CFG.dir.asset();
let file_dialog = FileDialog::new().initial_directory(path);
Self {
asset_list,
current_index: 0,
file_dialog,
event_rx,
action_tx,
}
}
pub fn ui(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
self.ui_toolbar(ctx, ui);
self.ui_table(ui);
self.receive_market_data();
}
pub fn current_asset(&mut self) -> Option<&mut Asset> {
self.asset_list.get_mut(self.current_index)
}
fn ui_toolbar(&mut self, ctx: &egui::Context, ui: &mut egui::Ui) {
ui.horizontal(|ui| {
if ui.button(self.asset_list.name()).clicked() {
self.file_dialog.pick_file();
}
let _ = ui.button("...");
self.file_dialog.update(ctx);
if let Some(path) = self.file_dialog.take_picked() {
self.asset_list = AssetList::load(&path).unwrap();
self.current_index = 0;
};
});
ui.separator();
}
fn ui_table(&mut self, ui: &mut egui::Ui) {
let text_height = egui::TextStyle::Body
.resolve(ui.style())
.size
.max(ui.spacing().interact_size.y);
let available_height = ui.available_height();
let mut table = TableBuilder::new(ui)
.striped(false) .resizable(true)
.cell_layout(egui::Layout::left_to_right(egui::Align::Center))
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.column(Column::remainder())
.min_scrolled_height(0.0)
.max_scroll_height(available_height);
table = table.sense(egui::Sense::click());
table
.header(20.0, |mut header| {
header.col(|ui| {
ui.strong("Ticker");
});
header.col(|ui| {
ui.strong("1M-1");
});
header.col(|ui| {
ui.strong("1M-2");
});
header.col(|ui| {
ui.strong("1M-3");
});
header.col(|ui| {
ui.strong("1M-4");
});
header.col(|ui| {
ui.strong("1M-5");
});
header.col(|ui| {
ui.strong("10M-1");
});
header.col(|ui| {
ui.strong("10M-2");
});
header.col(|ui| {
ui.strong("10M-3");
});
header.col(|ui| {
ui.strong("10M-4");
});
header.col(|ui| {
ui.strong("10M-5");
});
header.col(|ui| {
ui.strong("1H-1");
});
header.col(|ui| {
ui.strong("1H-2");
});
header.col(|ui| {
ui.strong("1H-3");
});
header.col(|ui| {
ui.strong("1H-4");
});
header.col(|ui| {
ui.strong("1H-5");
});
header.col(|ui| {
ui.strong("D-1");
});
header.col(|ui| {
ui.strong("D-2");
});
header.col(|ui| {
ui.strong("D-3");
});
})
.body(|body| {
body.rows(text_height, self.asset_list.len(), |mut row| {
let index = row.index();
let asset = self.asset_list.get(index).unwrap();
if self.current_index == index {
row.set_selected(true);
}
row.col(|ui| {
ui.label(asset.ticker());
});
row.col(|ui| {
let chart_opt = asset.chart(TimeFrame::M1);
let p = match chart_opt {
Some(chart) => {
chart.trend_posterior(Term::T1).unwrap()
}
None => 0.0,
};
ui.label(p.to_string());
});
if row.response().clicked() {
self.current_index = row.index();
};
if row.response().double_clicked() {
self.current_index = row.index();
self.subscribe_market_data()
}
});
});
}
fn load_charts(&mut self) {
let asset = self.asset_list.get_mut(self.current_index).unwrap();
for tf in TimeFrame::all() {
match asset.chart(tf).is_some() {
true => (),
false => {
asset.load_chart(tf).unwrap();
let chart = asset.chart_mut(tf).unwrap();
ExtremumIndicator::init(chart);
TrendAnalytic::init(chart);
}
};
}
}
fn subscribe_market_data(&mut self) {
self.load_charts();
let asset = self.asset_list.get(self.current_index).unwrap();
let iid = asset.iid();
let market_data = vec![MarketData::BAR_1M];
let action =
Action::Subscribe(DataAction::new(iid.clone(), market_data));
match self.action_tx.send(action) {
Ok(_) => (),
Err(e) => log::error!("{e}"),
};
}
fn receive_market_data(&mut self) {
while let Ok(event) = self.event_rx.try_recv() {
log::debug!("Asset widget receive {event}");
let asset = self.asset_list.get_mut(self.current_index).unwrap();
match event {
Event::Bar(e) => asset.bar_event(e),
Event::Tic(e) => todo!("{:?}", e),
Event::Order(e) => todo!("{:?}", e),
}
}
}
}