Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,997 changes: 50 additions & 1,947 deletions servers/rust-server/Cargo.lock

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions servers/rust-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ opt-level = 3
askama = "0.14.0"
axum = { version = "0.8.4", features = ["macros"] }
displaydoc = "0.2.5"
plotly = { version = "0.13.5", default-features = false, features = ["kaleido"] }
polars = { version = "0.50.0", features = ["lazy"] }
pretty-error-debug = "0.3.2"
rand = "0.9.2"
sea-orm = { version = "1.1.16", features = ["sqlx-postgres", "runtime-tokio-rustls", "macros", "with-uuid", "debug-print"] }
Expand Down
2 changes: 0 additions & 2 deletions servers/rust-server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ RUN --mount=type=cache,target=/usr/local/cargo/registry \
cargo chef cook --release --recipe-path recipe.json

COPY Cargo.toml Cargo.lock ./
COPY local ./local
COPY templates ./templates
COPY entity ./entity
COPY migration ./migration
Expand All @@ -51,7 +50,6 @@ WORKDIR /app

COPY --from=builder /build/target/release/finwar-market /app/finwar-market
COPY templates ./templates
COPY local ./local
COPY static ./static

ENV RUST_LOG=info
Expand Down
11 changes: 0 additions & 11 deletions servers/rust-server/src/data.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
use polars::prelude::*;

pub fn read_csv(path: &str) -> Result<DataFrame, PolarsError> {
CsvReadOptions::default()
.with_has_header(true)
.with_parse_options(
CsvParseOptions::default().with_try_parse_dates(true),
)
.try_into_reader_with_file_path(Some(path.into()))?
.finish()
}

pub fn list_files_in_folder(path: &str) -> std::io::Result<Vec<String>> {
use std::fs;
Expand Down
4 changes: 1 addition & 3 deletions servers/rust-server/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,6 @@ pub enum AppError {
NotFound,
/// Internal server error
Render(#[from] askama::Error),
/// Data error
Data(#[from] polars::prelude::PolarsError),
/// IO error
Io(#[from] std::io::Error),
/// App state
Expand All @@ -62,7 +60,7 @@ impl IntoResponse for AppError {
let status = match &self {
AppError::Render(_) => StatusCode::INTERNAL_SERVER_ERROR,
AppError::NotFound => StatusCode::NOT_FOUND,
AppError::Data(_) => StatusCode::INTERNAL_SERVER_ERROR,

AppError::Io(_) => StatusCode::INTERNAL_SERVER_ERROR,
AppError::State(_) => StatusCode::INTERNAL_SERVER_ERROR,
AppError::Trade(e) => match e {
Expand Down
24 changes: 3 additions & 21 deletions servers/rust-server/src/home.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,18 @@ use axum::{

use crate::{
error::AppError,
render::{PlotlyHtml, plot_candlesticks, plot_histogram},
state::AppState,
};

#[derive(Template)]
#[template(path = "home.html")]
struct HomeTemplate {
plots: Vec<PlotlyHtml>,

}

pub async fn home(
State(state): State<AppState>,
State(_state): State<AppState>,
) -> Result<impl IntoResponse, AppError> {
let dataframes = state.dataframes;
let random_files = state.randos;
let plot_pie = state.pie_distrubtion;
let histogram = plot_histogram(&dataframes, &random_files)?;

let mut plots: Vec<PlotlyHtml> = dataframes
.iter()
.zip(random_files.iter())
.map(|(df, file)| {
let plot = plot_candlesticks(df.clone(), Some(&file))?;
Ok(plot.to_inline_html(Some(&file)))
})
.collect::<Result<_, AppError>>()?;

plots.insert(0, plot_pie);
plots.insert(1, histogram.to_inline_html(Some("histogram")));

let template = HomeTemplate { plots };
let template = HomeTemplate { };
Ok(Html(template.render()?))
}
181 changes: 0 additions & 181 deletions servers/rust-server/src/render.rs
Original file line number Diff line number Diff line change
@@ -1,181 +0,0 @@
use plotly::{
color::Rgba,
common::{Line, Marker},
layout::{Axis, themes::BuiltinTheme},
*,
};
use polars::{error::PolarsError, frame::DataFrame};

use plotly::histogram::HistFunc;
use plotly::{Candlestick, Plot};

pub type PlotlyHtml = String;

pub fn a_nice_layout(title: &str) -> Layout {
let theme = BuiltinTheme::PlotlyDark;
let theme_template = theme.build();
Layout::new()
.title(title)
// .x_axis(Axis::new().title("Time"))
.y_axis(Axis::new().title("Price (USD $)"))
.font(plotly::common::Font::new().size(14))
// .width(1200)
// .height(800)
.template(theme_template)
}

pub fn plot_candlesticks(
df: DataFrame, df_name: Option<&str>,
) -> Result<Plot, PolarsError> {
let x = df.column("Date")?.date()?.to_string("%Y-%m-%d")?;
let a = x.into_no_null_iter().map(|s| s.to_string()).collect::<Vec<_>>();
let open =
df.column("Open")?.f64()?.into_no_null_iter().collect::<Vec<_>>();
let high =
df.column("High")?.f64()?.into_no_null_iter().collect::<Vec<_>>();
let low = df.column("Low")?.f64()?.into_no_null_iter().collect::<Vec<_>>();
let close =
df.column("Close")?.f64()?.into_no_null_iter().collect::<Vec<_>>();

let trace = Candlestick::new(a, open, high, low, close);
let mut plot = Plot::new();
let title = df_name.unwrap_or("Some stocks example");
let df_name = title.to_uppercase();
plot.add_trace(trace);
plot.set_layout(a_nice_layout(
format!("{} Stock Prices", df_name).as_str(),
));
Ok(plot)
}

pub fn plot_pie_files(files: &Vec<String>) -> Result<Plot, PolarsError> {
use std::fs;

// Shift up the size bins in bits
let bins = [
(0, 8_192), // 0-8KB
(8_192, 81_920), // 8KB-80KB
(81_920, 819_200), // 80KB-800KB
(819_200, u64::MAX), // 800KB+
];
let mut counts = vec![0; bins.len()];

// Count files in each bin
for file in files {
let path = format!("./local/data/Stocks/{}", file);
let metadata = fs::metadata(&path);
if let Ok(meta) = metadata {
let size = meta.len(); // bytes
let size_bits = size * 8;
for (i, (min, max)) in bins.iter().enumerate() {
if size_bits >= *min && size_bits < *max {
counts[i] += 1;
break;
}
}
}
}

// Create labels with counts (after counts are calculated)
let bin_labels: Vec<String> = bins
.iter()
.enumerate()
.map(|(i, _)| {
let label = match i {
0 => "0-8KB",
1 => "8KB-80KB",
2 => "80KB-800KB",
3 => "800KB+",
_ => "Other",
};
format!("{} ({})", label, counts[i])
})
.collect();

let trace = Pie::new(counts.clone()).labels(bin_labels);

let mut plot = Plot::new();
plot.add_trace(trace);
plot.set_layout(a_nice_layout("Files Size Distribution"));
Ok(plot)
}

// fn colored_and_styled_histograms(show: bool, file_name: &str) {
// let n = 500;
// let x1 = sample_uniform_distribution(n, 0.0, 5.0);
// let x2 = sample_uniform_distribution(n, 0.0, 10.0);
// let y1 = sample_uniform_distribution(n, 0.0, 1.0);
// let y2 = sample_uniform_distribution(n, 0.0, 2.0);

// let trace1 = Histogram::new_xy(x1, y1)
// .name("control")
// .hist_func(HistFunc::Count)
// .marker(
// Marker::new()
// .color(Rgba::new(255, 100, 102, 0.7))
// .line(Line::new().color(Rgba::new(255, 100, 102, 1.0)).width(1.0)),
// )
// .opacity(0.5)
// .auto_bin_x(false)
// .x_bins(Bins::new(0.5, 2.8, 0.06));
// let trace2 = Histogram::new_xy(x2, y2)
// .name("experimental")
// .hist_func(HistFunc::Count)
// .marker(
// Marker::new()
// .color(Rgba::new(100, 200, 102, 0.7))
// .line(Line::new().color(Rgba::new(100, 200, 102, 1.0)).width(1.0)),
// )
// .opacity(0.75)
// .auto_bin_x(false)
// .x_bins(Bins::new(-3.2, 4.0, 0.06));
// let layout = Layout::new()
// .title("Colored and Styled Histograms")
// .x_axis(Axis::new().title("Value"))
// .y_axis(Axis::new().title("Count"))
// .bar_mode(BarMode::Overlay)
// .bar_gap(0.05)
// .bar_group_gap(0.2);

// let mut plot = Plot::new();
// plot.set_layout(layout);
// plot.add_trace(trace1);
// plot.add_trace(trace2);

// let path = write_example_to_html(&plot, file_name);
// if show {
// plot.show_html(path);

pub fn plot_histogram(
dfs: &Vec<DataFrame>, names: &Vec<String>,
) -> Result<Plot, PolarsError> {
let colors = [
Rgba::new(255, 100, 102, 0.7),
Rgba::new(100, 200, 102, 0.7),
Rgba::new(100, 100, 255, 0.7),
Rgba::new(255, 200, 100, 0.7),
];

let mut plot = Plot::new();

for (i, df) in dfs.iter().enumerate() {
let close =
df.column("Close")?.f64()?.into_no_null_iter().collect::<Vec<_>>();
let trace =
Histogram::new(close)
.name(format!(
"Stock {}",
names.get(i).unwrap_or(&"Unknown".to_string())
))
.hist_func(HistFunc::Count)
.marker(Marker::new().color(colors[i % colors.len()]).line(
Line::new().color(colors[i % colors.len()]).width(1.0),
))
.opacity(0.7)
.auto_bin_x(false);
plot.add_trace(trace);
}

plot.set_layout(a_nice_layout("Close Price Distribution"));
Ok(plot)
}
29 changes: 0 additions & 29 deletions servers/rust-server/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
use crate::{
data::{list_files_in_folder, read_csv},
render::{PlotlyHtml, plot_pie_files},
};
use polars::frame::DataFrame;
use rand::seq::SliceRandom;
use sea_orm::DatabaseConnection;
use std::fmt;

Expand All @@ -30,10 +24,6 @@ impl std::error::Error for StateError {}

#[derive(Clone)]
pub struct AppState {
pub all_files: Vec<String>,
pub randos: Vec<String>,
pub dataframes: Vec<DataFrame>,
pub pie_distrubtion: PlotlyHtml,
pub db: sea_orm::DatabaseConnection,
pub uuid_prefix_length: usize,
pub starting_cash: f64,
Expand All @@ -42,26 +32,7 @@ pub struct AppState {

impl AppState {
pub async fn new(db: DatabaseConnection) -> Result<Self, StateError> {
let mut all_files = list_files_in_folder("./local/data/Stocks")
.map_err(|_| StateError::InitState)?;
let plot_pie = plot_pie_files(&all_files)
.map_err(|_| StateError::InitState)?
.to_inline_html(Some("file size distribution"));
let mut rng = rand::rng();
all_files.shuffle(&mut rng);
let randos = all_files.clone().into_iter().take(3).collect::<Vec<_>>();

let dataframes = randos
.iter()
.map(|file| read_csv(&format!("./local/data/Stocks/{}", file)))
.collect::<Result<Vec<_>, _>>()
.map_err(|_| StateError::InitState)?;

Ok(AppState {
all_files,
randos,
dataframes,
pie_distrubtion: plot_pie,
db,
uuid_prefix_length: 18,
starting_cash: 10000.0,
Expand Down
1 change: 0 additions & 1 deletion servers/rust-server/templates/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,5 @@ <h1>Finwar</h1>
<br />
</p>
<hr />
{% for plot in plots %} {{ plot|safe }} {% endfor %}
<!-- {% call super() %} -->
{% endblock %}