2026/1/13 プロトタイプ ------------------------------ use chrono::{Datelike, Duration, NaiveDate, TimeZone, Utc}; use plotters::backend::BitMapBackend; use plotters::drawing::IntoDrawingArea; use plotters::prelude::BLUE; use plotters::prelude::BindKeyPoints; use plotters::prelude::ChartBuilder; use plotters::prelude::LineSeries; use plotters::prelude::WHITE; use plotters::style::Color; use plotters::style::IntoFont; use polars::df; use polars::frame::DataFrame; use polars::prelude::IntoLazy; use polars::prelude::JoinArgs; use polars::prelude::col; use std::{error::Error, time::Instant}; use yfinance_rs::{Ticker, YfClient, core::conversions::money_to_f64}; async fn load_vt_price() -> Result> { let client = YfClient::default(); let ticker = Ticker::new(&client, "VT"); let start = Utc.with_ymd_and_hms(2008, 6, 26, 0, 0, 0).unwrap(); let end = Utc::now(); let history = ticker .history_builder() .between(start, end) .interval(yfinance_rs::Interval::D1) .fetch() .await?; let dates: Vec = history.iter().map(|c| c.ts.date_naive()).collect(); let closes: Vec = history .iter() .map(|c| money_to_f64(&c.close_unadj.as_ref().unwrap())) .collect(); let price = df![ "date" => &dates, "vt_close_usd" => &closes ]?; Ok(price) } async fn load_jpyx_price() -> Result> { let client = YfClient::default(); let ticker = Ticker::new(&client, "JPY=X"); let start = Utc.with_ymd_and_hms(1996, 10, 30, 0, 0, 0).unwrap(); let end = Utc::now(); let history = ticker .history_builder() .between(start, end) .interval(yfinance_rs::Interval::D1) .fetch() .await?; let dates: Vec = history .iter() .map(|c| (c.ts + Duration::hours(14) + Duration::minutes(30)).date_naive()) .collect(); let closes: Vec = history .iter() .map(|c| money_to_f64(&c.close_unadj.as_ref().unwrap())) .collect(); let price = df![ "date" => &dates, "jpyx_close" => &closes ]?; Ok(price) } async fn load_price() -> Result> { let vt_price = load_vt_price().await?; let jpyx_price = load_jpyx_price().await?; // println!("{:?}", vt_price); // println!("{:?}", jpyx_price); let price = vt_price.lazy().join( jpyx_price.lazy(), [col("date")], [col("date")], JoinArgs::new(polars::prelude::JoinType::Inner), ); let price = price .with_column((col("vt_close_usd") * col("jpyx_close")).alias("vt_close_yen")) .collect()?; Ok(price) } #[tokio::main] async fn main() -> Result<(), Box> { let start = Instant::now(); let price = load_price().await.unwrap(); println!("{:?}", price); let dates: Vec = price .column("date")? .date()? .as_date_iter() .flatten() .collect(); let closes: Vec = price .column("vt_close_usd")? .f64()? .into_iter() .flatten() .collect(); let closes_yen: Vec = price .column("vt_close_yen")? .f64()? .into_iter() .flatten() .collect(); let today = Utc::now().format("%Y%m%d").to_string(); let png_filename = format!("image/VT価格推移_{}.png", today); let root = BitMapBackend::new(&png_filename, (3840, 3840)).into_drawing_area(); root.fill(&WHITE)?; let areas = root.split_evenly((2, 1)); let x_min = NaiveDate::from_ymd_opt(2008, 1, 1).unwrap(); let x_max = NaiveDate::from_ymd_opt(2027, 1, 1).unwrap(); let x_ticks: Vec = (2008..=2027) .map(|y| NaiveDate::from_ymd_opt(y, 1, 1).unwrap()) .collect(); let y_min: f64 = 0.0; let y_max: f64 = 160.0; let y_min_yen: f64 = 0.0; let y_max_yen: f64 = 25000.0; let mut chart = ChartBuilder::on(&areas[0]) .caption("VT価格推移 - テスト中", ("Yu Gothic", 80)) .margin(100) .x_label_area_size(100) .y_label_area_size(150) .build_cartesian_2d( (x_min..x_max).with_key_points(x_ticks.clone()), y_min..y_max, )?; chart .configure_mesh() .label_style(("Yu Gothic", 50).into_font()) .x_label_formatter(&|x| format!("{}", x.year())) .y_desc("価格(USD)") .y_label_formatter(&|y| format!("{:.0}", y)) .draw()?; chart.draw_series(LineSeries::new( dates.iter().cloned().zip(closes.iter().cloned()), BLUE.stroke_width(1), ))?; let mut chart2 = ChartBuilder::on(&areas[1]) .caption("円建VT価格推移 - テスト中", ("Yu Gothic", 80)) .margin(100) .x_label_area_size(100) .y_label_area_size(150) .build_cartesian_2d( (x_min..x_max).with_key_points(x_ticks.clone()), y_min_yen..y_max_yen, )?; chart2 .configure_mesh() .label_style(("Yu Gothic", 50).into_font()) .x_label_formatter(&|x| format!("{}", x.year())) .y_desc("価格(円)") .y_label_formatter(&|y| format!("{:.0}", y)) .draw()?; chart2.draw_series(LineSeries::new( dates.iter().cloned().zip(closes_yen.iter().cloned()), BLUE.stroke_width(1), ))?; let elapsed = start.elapsed(); println!("処理時間(sec) = {:.3}", elapsed.as_secs_f64()); Ok(()) } ------------------------------