add chart
This commit is contained in:
parent
832c0ca5a9
commit
e90ccf59d9
3 changed files with 222 additions and 29 deletions
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "taikoothong"
|
||||
version = "0.0.0"
|
||||
workspace = "../"
|
||||
#workspace = "."
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
|
@ -16,6 +16,7 @@ chrono = "0.4.11"
|
|||
rocket = "0.5.0-rc.2"
|
||||
round = "0.1.0"
|
||||
num-format = "0.4.4"
|
||||
rss = "1.9.0"
|
||||
|
||||
[dependencies.rocket_dyn_templates]
|
||||
rocket_dyn_templates = "0.5.0-rc.3-2023-06-09"
|
||||
|
|
168
d3js/example.html
Normal file
168
d3js/example.html
Normal file
|
@ -0,0 +1,168 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
|
||||
body {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
|
||||
text {
|
||||
fill: #000;
|
||||
}
|
||||
|
||||
button {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 440px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
path.candle {
|
||||
stroke: #000000;
|
||||
}
|
||||
|
||||
path.candle.body {
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
path.candle.up {
|
||||
fill: #dc3526;
|
||||
stroke: #dc3526;
|
||||
}
|
||||
|
||||
path.candle.down {
|
||||
fill: #007d30;
|
||||
stroke: #007d30;
|
||||
}
|
||||
|
||||
</style>
|
||||
<body>
|
||||
<h3 id="chart-title"></h3>
|
||||
<div>
|
||||
<input type="radio" name="range" value="5d">5日<br>
|
||||
<input type="radio" name="range" value="30d">30日<br>
|
||||
<input type="radio" name="range" value="ytd">今年迄今<br>
|
||||
</div>
|
||||
|
||||
<script src="http://d3js.org/d3.v4.min.js"></script>
|
||||
<script src="http://techanjs.org/techan.min.js"></script>
|
||||
<script>
|
||||
|
||||
var margin = {top: 20, right: 20, bottom: 30, left: 50},
|
||||
width = 960 - margin.left - margin.right,
|
||||
height = 500 - margin.top - margin.bottom;
|
||||
|
||||
var parseDate = d3.timeParse("%d-%b-%y");
|
||||
|
||||
var x = techan.scale.financetime()
|
||||
.range([0, width]);
|
||||
|
||||
var y = d3.scaleLinear()
|
||||
.range([height, 0]);
|
||||
|
||||
var candlestick = techan.plot.candlestick()
|
||||
.xScale(x)
|
||||
.yScale(y);
|
||||
|
||||
var xAxis = d3.axisBottom()
|
||||
.scale(x)
|
||||
.tickFormat(d3.timeFormat('%y-%m-%d'));
|
||||
|
||||
var yAxis = d3.axisLeft()
|
||||
.scale(y);
|
||||
|
||||
var svg = d3.select("body").append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
|
||||
|
||||
var data_by_date = [];
|
||||
|
||||
|
||||
|
||||
d3.json(`https://gist.githubusercontent.com/Yoxem/e3e1986777af209a00f8a318056431ed/raw/b646e322a87e2bc2c061ecf32902fc543bc41aed/0050.json`, function(d) {
|
||||
console.log(d);
|
||||
|
||||
var data_by_date = [];
|
||||
var parseDate = d3.timeParse("%Y-%m-%d");
|
||||
|
||||
|
||||
for (var i=0;i<d["close"].length;i++){
|
||||
data_by_date.push({
|
||||
date: parseDate(d["date"][i]),
|
||||
open: +d["open"][i],
|
||||
high: +d["high"][i],
|
||||
low: +d["low"][i],
|
||||
close: +d["close"][i],
|
||||
volume: +d["volume"][i],
|
||||
});};
|
||||
|
||||
console.log('dataGet', data_by_date);
|
||||
|
||||
var accessor = candlestick.accessor();
|
||||
data_by_date = data_by_date.sort(function(a, b) { return d3.ascending(accessor.d(a), accessor.d(b)); });
|
||||
|
||||
console.log("===" + JSON.stringify(data_by_date));
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "candlestick");
|
||||
|
||||
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "x axis")
|
||||
.attr("transform", "translate(0," + height + ")")
|
||||
.append("text")
|
||||
.text("日期")
|
||||
.attr("transform", "translate(" + width + ", 0)");
|
||||
|
||||
|
||||
|
||||
svg.append("g")
|
||||
.attr("class", "y axis")
|
||||
.append("text")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("y", 6)
|
||||
.attr("dy", ".71em")
|
||||
.style("text-anchor", "end")
|
||||
.text("股價(NT$)");
|
||||
|
||||
// Data to display initially
|
||||
d3.select("#chart-title").text("今年迄今");
|
||||
draw(data_by_date);
|
||||
// Only want this button to be active if the data has loaded
|
||||
// d3.select("input").on("click", function() { draw(data_by_date); }).style("display", "inline");
|
||||
|
||||
d3.selectAll('input[name="range"]').on("change", function(){
|
||||
if (this.value == "ytd"){
|
||||
draw(data_by_date);
|
||||
d3.select("#chart-title").text("今年迄今");
|
||||
}
|
||||
else if(this.value == "30d"){
|
||||
draw(data_by_date.slice(data_by_date.length-30, data_by_date.length-1));
|
||||
d3.select("#chart-title").text("30日");
|
||||
}
|
||||
else{
|
||||
draw(data_by_date.slice(data_by_date.length-5, data_by_date.length-1));
|
||||
d3.select("#chart-title").text("5日");
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
} ); // 串接網址
|
||||
|
||||
|
||||
|
||||
function draw(data) {
|
||||
x.domain(data.map(candlestick.accessor().d));
|
||||
y.domain(techan.scale.plot.ohlc(data, candlestick.accessor()).domain());
|
||||
|
||||
svg.selectAll("g.candlestick").datum(data).call(candlestick);
|
||||
svg.selectAll("g.x.axis").call(xAxis);
|
||||
svg.selectAll("g.y.axis").call(yAxis);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
58
src/main.rs
58
src/main.rs
|
@ -11,6 +11,7 @@ use rocket_dyn_templates::Template;
|
|||
use rocket::{Rocket, Build};
|
||||
use round::round;
|
||||
use num_format::{Locale, WriteFormatted};
|
||||
use rss::Channel;
|
||||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
|
@ -35,14 +36,23 @@ impl Date {
|
|||
}
|
||||
|
||||
|
||||
|
||||
#[get("/<stock_id>")]
|
||||
fn get_tw_stock(stock_id: String) -> Template {
|
||||
// let a = "👋 Hello, stock no: a22";
|
||||
|
||||
let response_body = get_stock_data(stock_id.as_str(), Date::Day(1), Date::Day(5));
|
||||
#[get("/<stock_id>/json")]
|
||||
fn get_tw_stock_json(stock_id: String) -> String {
|
||||
let response_body = get_stock_data(stock_id.as_str(), Date::Day(1), Date::YearToDate);
|
||||
let response_json: Value = serde_json::from_str(response_body.as_str()).unwrap();
|
||||
|
||||
|
||||
let mut stock_total_data = tw_stock_process_json(&response_json);
|
||||
|
||||
let stock_total_data_json = serde_json::json!(stock_total_data);
|
||||
|
||||
return stock_total_data_json.to_string();
|
||||
}
|
||||
|
||||
|
||||
fn tw_stock_process_json(response_json : &Value) -> HashMap<&str, Vec<String>>{
|
||||
|
||||
|
||||
let days_in_unix_time = &response_json["chart"]["result"][0]["timestamp"];
|
||||
|
||||
let mut stock_total_data = HashMap::new();
|
||||
|
@ -55,8 +65,6 @@ fn get_tw_stock(stock_id: String) -> Template {
|
|||
_ => vec![format!("Not a series of date")],
|
||||
};
|
||||
|
||||
println!("{:?}", &days_in_unix_time);
|
||||
|
||||
stock_total_data.insert("date", days_in_custom_format);
|
||||
|
||||
let mut open_prices : Vec<String> = vec![];
|
||||
|
@ -99,25 +107,33 @@ fn get_tw_stock(stock_id: String) -> Template {
|
|||
stock_total_data.insert("low", low_prices);
|
||||
stock_total_data.insert("volume", volumes);
|
||||
|
||||
return stock_total_data;
|
||||
}
|
||||
|
||||
#[get("/<stock_id>")]
|
||||
fn get_tw_stock(stock_id: String) -> Template {
|
||||
|
||||
let response_body = get_stock_data(stock_id.as_str(), Date::Day(1), Date::YearToDate);
|
||||
let response_json: Value = serde_json::from_str(response_body.as_str()).unwrap();
|
||||
|
||||
|
||||
let mut stock_total_data = tw_stock_process_json(&response_json);
|
||||
stock_total_data.insert("stock_id", vec![stock_id]);
|
||||
|
||||
let mut stock_total_data_by_date = transverse_stock_data_by_date(stock_total_data.clone());
|
||||
//let mut stock_total_data_by_date_wrapper = HashMap::new();
|
||||
|
||||
//stock_total_data_by_date_wrapper.insert("data", stock_total_data_by_date);
|
||||
|
||||
//println!("{:?}", stock_total_data_by_date_wrapper);
|
||||
println!("{:?}", stock_total_data);
|
||||
return Template::render("tw_stock", stock_total_data);
|
||||
}
|
||||
|
||||
fn json_unix_time_to_date(json_value: &Value) -> String {
|
||||
let unix_time = json_value.as_i64().unwrap();
|
||||
println!("{:?}", unix_time);
|
||||
|
||||
let naive_time = Utc.timestamp_opt(unix_time, 0).unwrap();
|
||||
|
||||
let date = format!("{}", naive_time.format("%Y-%m-%d"));
|
||||
println!("{:?}", date);
|
||||
|
||||
return date;
|
||||
}
|
||||
|
@ -156,6 +172,12 @@ fn get_stock_data(stock_id: &str, interval: Date, range: Date) -> String {
|
|||
stock_id, intrval_str, range_str
|
||||
);
|
||||
|
||||
|
||||
|
||||
return get_url_data(&url);
|
||||
}
|
||||
|
||||
fn get_url_data(url : &String) -> String{
|
||||
let mut curl_easy = Easy::new(); // fetch the data with the curl binding
|
||||
let mut response = String::new();
|
||||
|
||||
|
@ -166,7 +188,11 @@ fn get_stock_data(stock_id: &str, interval: Date, range: Date) -> String {
|
|||
|
||||
curl_transfer
|
||||
.write_function(|data| {
|
||||
response.push_str(std::str::from_utf8(data).unwrap());
|
||||
let s = match std::str::from_utf8(data){
|
||||
Err(_) => {println!("解碼錯誤"); ""}
|
||||
Ok(cont) => { println!("解碼成功"); cont}
|
||||
};
|
||||
response.push_str(s);
|
||||
Ok(data.len())
|
||||
})
|
||||
.unwrap();
|
||||
|
@ -174,9 +200,7 @@ fn get_stock_data(stock_id: &str, interval: Date, range: Date) -> String {
|
|||
curl_transfer.perform().unwrap();
|
||||
}
|
||||
|
||||
let response_returned = response.clone();
|
||||
|
||||
return response_returned;
|
||||
return response.clone();
|
||||
}
|
||||
|
||||
|
||||
|
@ -184,7 +208,7 @@ fn get_stock_data(stock_id: &str, interval: Date, range: Date) -> String {
|
|||
fn rocket() -> Rocket<Build> {
|
||||
// rocket::ignite().mount("/", routes![index]).launch();
|
||||
rocket::build().attach(Template::fairing())
|
||||
.mount("/tw", routes![get_tw_stock])
|
||||
.mount("/tw", routes![get_tw_stock, get_tw_stock_json])
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue