Skip to content

Commit 37a7592

Browse files
committed
✨ added yahoo finance fetcher for bin-app
1 parent cd72662 commit 37a7592

3 files changed

Lines changed: 111 additions & 29 deletions

File tree

src/bin/cli/get_args.rs

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,40 @@
11
use clap::{App, Arg};
2-
use std::str::FromStr;
32

4-
pub enum ReadingMode {
3+
#[derive(PartialEq)]
4+
pub enum CandlesRetrievalMode {
55
Stdin,
66
CsvFile,
7-
}
8-
9-
impl FromStr for ReadingMode {
10-
type Err = &'static str;
11-
12-
fn from_str(s: &str) -> Result<Self, Self::Err> {
13-
match s {
14-
"Stdin" => Ok(ReadingMode::Stdin),
15-
"CsvFile" => Ok(ReadingMode::CsvFile),
16-
_ => Err("no match"),
17-
}
18-
}
7+
Yahoo,
198
}
209

2110
pub struct CliOptions {
22-
pub reading_mode: ReadingMode,
11+
pub mode: CandlesRetrievalMode,
2312
pub file_path: Option<String>,
2413
pub chart_name: Option<String>,
2514
pub bear_color: Option<String>,
2615
pub bull_color: Option<String>,
16+
pub ticker: Option<String>,
17+
pub interval: String,
2718
}
2819

2920
pub fn get_args() -> CliOptions {
3021
let matches = App::new(env!("CARGO_PKG_NAME"))
3122
.version(env!("CARGO_PKG_VERSION"))
3223
.author(env!("CARGO_PKG_AUTHORS"))
3324
.arg(
34-
Arg::with_name("READING_MODE")
35-
.short("r")
36-
.long("reading-mode")
37-
.help("Make the program reads and parse candles from stdin.")
38-
.possible_values(&["stdin", "csv-file", "json-file"])
25+
Arg::with_name("MODE")
26+
.short("m")
27+
.long("mode")
28+
.help("Select the method for retrieving the candles.")
29+
.possible_values(&["stdin", "csv-file", "json-file", "yahoo-fetch"])
3930
.takes_value(true)
4031
.required(true),
4132
)
4233
.arg(
4334
Arg::with_name("FILE")
4435
.short("f")
4536
.long("file")
46-
.help("File to read candles from, if reading-mode is `*-file.`")
37+
.help("[MODE:*-file] File to read candles from.`")
4738
.takes_value(true),
4839
)
4940
.arg(
@@ -64,14 +55,35 @@ pub fn get_args() -> CliOptions {
6455
.help("Sets the ascending candles color in hexadecimal.")
6556
.takes_value(true),
6657
)
58+
.arg(
59+
Arg::with_name("TICKER")
60+
.long("ticker")
61+
.takes_value(true)
62+
.required_if("MODE", "yahoo-fetch")
63+
.help("[MODE:*-fetch] The broker-side ticker of the asset you want to plot."),
64+
)
65+
.arg(
66+
Arg::with_name("INTERVAL")
67+
.long("interval")
68+
.default_value("1d")
69+
.possible_values(&[
70+
"1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h", "1d", "5d", "1wk", "1mo",
71+
"3mo",
72+
])
73+
.takes_value(true)
74+
.help("[MODE:*-fetch] The interval you want to retrieve the candles from the API"),
75+
)
6776
.get_matches();
6877

6978
return CliOptions {
70-
reading_mode: match matches.value_of("READING_MODE").unwrap() {
71-
"stdin" => ReadingMode::Stdin,
72-
"csv-file" => ReadingMode::CsvFile,
79+
mode: match matches.value_of("MODE").unwrap() {
80+
"stdin" => CandlesRetrievalMode::Stdin,
81+
"csv-file" => CandlesRetrievalMode::CsvFile,
82+
"yahoo-fetch" => CandlesRetrievalMode::Yahoo,
7383
_ => panic!("Invalid reading mode."),
7484
},
85+
interval: matches.value_of("INTERVAL").unwrap().to_string(),
86+
ticker: matches.value_of("TICKER").map(|s| s.to_string()),
7587
file_path: matches.value_of("FILE").map(|s| s.to_string()),
7688
chart_name: matches.value_of("CHART_NAME").map(|s| s.to_string()),
7789
bear_color: matches.value_of("BEAR_COLOR").map(|s| s.to_string()),

src/bin/cli/main.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use clap::{App, Arg};
21
use cli_candlestick_chart::{Candle, Chart};
32
use std::error::Error;
43
use std::io::{self, BufRead};
@@ -7,7 +6,10 @@ mod utils;
76
use utils::hexa_to_rgb;
87

98
mod get_args;
10-
use get_args::{get_args, ReadingMode};
9+
use get_args::{get_args, CandlesRetrievalMode};
10+
11+
mod yahoo_api;
12+
use yahoo_api::get_yahoo_klines;
1113

1214
fn parse_candles_from_stdin() -> Vec<Candle> {
1315
let stdin = io::stdin();
@@ -38,18 +40,25 @@ fn main() {
3840
let options = get_args();
3941
let mut candles: Vec<Candle> = Vec::new();
4042

41-
match options.reading_mode {
42-
ReadingMode::Stdin => {
43+
match options.mode {
44+
CandlesRetrievalMode::Stdin => {
4345
candles = parse_candles_from_stdin();
4446
}
45-
ReadingMode::CsvFile => {
47+
CandlesRetrievalMode::CsvFile => {
4648
let filepath = options.file_path.expect("No file path provided.");
4749
candles = parse_candles_from_csv(&filepath).unwrap();
4850
}
51+
CandlesRetrievalMode::Yahoo => {
52+
candles = get_yahoo_klines(&options.ticker.to_owned().unwrap(), &options.interval);
53+
}
4954
};
5055

5156
let mut chart = Chart::new(&candles);
5257

58+
if !options.ticker.is_none() && options.mode == CandlesRetrievalMode::Yahoo {
59+
chart.set_name(options.ticker.unwrap().to_string());
60+
}
61+
5362
if let Some(chart_name) = options.chart_name {
5463
chart.set_name(chart_name);
5564
}

src/bin/cli/yahoo_api.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use cli_candlestick_chart::Candle;
2+
3+
pub fn get_range_from_interval(interval: &str) -> String {
4+
let output = match interval {
5+
"1m" => "1d",
6+
"2m" => "1d",
7+
"5m" => "1d",
8+
"15m" => "5d",
9+
"30m" => "5d",
10+
"60m" => "5d",
11+
"90m" => "5d",
12+
"1h" => "1mo",
13+
"1d" => "1y",
14+
"5d" => "1y",
15+
"1wk" => "1y",
16+
"1mo" => "1y",
17+
"3mo" => "1y",
18+
_ => panic!("Invalid interval"),
19+
};
20+
21+
output.to_owned()
22+
}
23+
24+
pub fn get_yahoo_klines(symbol: &str, interval: &str) -> Vec<Candle> {
25+
let base_url = "https://query1.finance.yahoo.com";
26+
27+
let range = get_range_from_interval(interval);
28+
29+
let url = format!(
30+
"{}/v8/finance/chart/{}?range={}&interval={}",
31+
base_url, symbol, range, interval
32+
);
33+
34+
let client = reqwest::blocking::get(url.as_str()).unwrap();
35+
let value: serde_json::Value = serde_json::from_str(client.text().unwrap().as_str()).unwrap();
36+
37+
let nb_candles = value["chart"]["result"][0]["timestamp"]
38+
.as_array()
39+
.expect("Ticker seems to be invalid.")
40+
.len();
41+
42+
let mut candles: Vec<Candle> = Vec::new();
43+
for i in 0..nb_candles {
44+
let base = &value["chart"]["result"][0]["indicators"]["quote"][0];
45+
46+
let open = base["open"][i].as_f64().unwrap();
47+
let high = base["high"][i].as_f64().unwrap();
48+
let low = base["low"][i].as_f64().unwrap();
49+
let close = base["close"][i].as_f64().unwrap();
50+
51+
let candle = Candle::new(
52+
open.to_owned(),
53+
high.to_owned(),
54+
low.to_owned(),
55+
close.to_owned(),
56+
);
57+
candles.push(candle);
58+
}
59+
60+
candles
61+
}

0 commit comments

Comments
 (0)