--- /dev/null
+<!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>
+
use rocket::{Rocket, Build};
use round::round;
use num_format::{Locale, WriteFormatted};
+use rss::Channel;
#[macro_use] extern crate rocket;
}
+#[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();
-#[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));
- 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"];
_ => 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![];
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;
}
stock_id, intrval_str, range_str
);
- let mut curl_easy = Easy::new(); // fetch the data with the curl binding
- let mut response = String::new();
- {
- curl_easy.url(url.as_str()).unwrap();
- let mut curl_transfer = curl_easy.transfer();
+ return get_url_data(&url);
+}
- curl_transfer
- .write_function(|data| {
- response.push_str(std::str::from_utf8(data).unwrap());
- Ok(data.len())
- })
- .unwrap();
+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();
- curl_transfer.perform().unwrap();
- }
+{
+ curl_easy.url(url.as_str()).unwrap();
- let response_returned = response.clone();
+ let mut curl_transfer = curl_easy.transfer();
+
+ curl_transfer
+ .write_function(|data| {
+ let s = match std::str::from_utf8(data){
+ Err(_) => {println!("解碼錯誤"); ""}
+ Ok(cont) => { println!("解碼成功"); cont}
+ };
+ response.push_str(s);
+ Ok(data.len())
+ })
+ .unwrap();
+
+ curl_transfer.perform().unwrap();
+}
- return response_returned;
+return response.clone();
}
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])
}