Skip to main content

tradingview_get/
tradingview_get.rs

1use std::collections::HashMap;
2
3use anyhow::{Context, Result};
4use clap::{Args, Parser, Subcommand};
5use easytradeview::{Field, FieldWithInterval, Interval, Screener, SymbolValues, TradingView};
6use serde_json::{json, Value};
7
8#[derive(Parser)]
9struct Opts {
10    #[command(subcommand)]
11    subcmd: SubCommand,
12}
13
14#[derive(Subcommand)]
15enum SubCommand {
16    /// Searches for symbols by given exchanges and types.
17    Search(SerachArgs),
18    /// Retrieves the specified fields for the given symbols.
19    Scan(ScanArgs),
20    /// Retrieves the specified fields for a given symbol.
21    Get(GetArgs),
22    /// List all known screeners.
23    ListScreeners,
24    /// List all known intervals.
25    ListIntervals,
26}
27
28#[derive(Debug, Args)]
29struct SerachArgs {
30    #[arg(long, default_value = "crypto")]
31    screener: String,
32
33    #[arg(long)]
34    exchanges: Vec<String>,
35
36    #[arg(long)]
37    types: Vec<String>,
38
39    /// name filter
40    #[arg(long, default_value = "")]
41    name: String,
42
43    #[arg(long, value_parser, num_args = 1.., value_delimiter = ',')]
44    fields: Vec<String>,
45}
46
47#[derive(Debug, Args)]
48struct ScanArgs {
49    #[arg(long, default_value = "crypto")]
50    screener: String,
51
52    #[arg(long, default_value = "OKX")]
53    exchange: String,
54
55    #[arg(short, long, default_value = "1d")]
56    default_interval: String,
57
58    #[arg(long, default_value = "BTCUSDT", value_parser, num_args = 1.., value_delimiter = ',')]
59    symbols: Vec<String>,
60
61    #[arg(long, default_value = "1d", value_parser, num_args = 1.., value_delimiter = ',')]
62    fields: Vec<String>,
63}
64
65#[derive(Debug, Args)]
66struct GetArgs {
67    #[arg(long, default_value = "crypto")]
68    screener: String,
69
70    #[arg(long, default_value = "OKX")]
71    exchange: String,
72
73    #[arg(short, long, default_value = "1d")]
74    interval: String,
75
76    #[arg(long, default_value = "BTCUSDT")]
77    symbol: String,
78
79    #[arg(long, default_value = "1d", value_parser, num_args = 1.., value_delimiter = ',')]
80    fields: Vec<String>,
81
82    #[arg(long, default_value_t = false)]
83    json: bool,
84}
85
86#[tokio::main]
87async fn main() -> Result<()> {
88    let opts = Opts::parse();
89    match opts.subcmd {
90        SubCommand::Search(args) => {
91            // Parse arguments
92            let screener = args.screener;
93            let exchanges = args.exchanges;
94            let types = args.types;
95            let name_filter = args.name;
96            // Prepare extra fields if specified
97            let extra_fields: Vec<FieldWithInterval> = args
98                .fields
99                .into_iter()
100                .map(|x| FieldWithInterval::parse_undefined(&x))
101                .collect();
102
103            // Initialize TradingView client and search symbols with given parameters
104            let tv = TradingView::new(&screener, "");
105            let values = tv
106                .search_symbols(&exchanges, &types, name_filter, &extra_fields)
107                .await
108                .context("search symbols error")?;
109
110            // Prepare fields to be printed
111            let fields = [
112                Field::Exchange,
113                Field::Name,
114                Field::Type,
115                Field::Description,
116            ]
117            .into_iter()
118            .map(|x| x.with_interval(&Interval::default()))
119            .chain(extra_fields.into_iter())
120            .collect::<Vec<_>>();
121
122            // Assemble data rows for output
123            let mut table: Vec<Vec<String>> = vec![];
124            for vals in values {
125                let mut row = vec![];
126                for field in fields.iter() {
127                    let val = vals
128                        .values()
129                        .get(&field)
130                        .map_or("".into(), |x| x.to_string());
131                    row.push(val);
132                }
133                table.push(row);
134            }
135
136            // Print csv header
137            println!(
138                "{}",
139                fields
140                    .iter()
141                    .map(|x| x.to_string())
142                    .collect::<Vec<_>>()
143                    .join(",")
144            );
145            // Print csv data rows
146            for vals in table {
147                println!("{}", vals.join(","));
148            }
149        }
150        SubCommand::Scan(args) => {
151            // Parse arguments
152            let screener = args.screener;
153            let exchange = args.exchange;
154            let interval = Interval::parse_undefined(&args.default_interval);
155            let symbols = args.symbols;
156            // Prepare fields with default intervals if not specified
157            let fields = args
158                .fields
159                .iter()
160                .map(|x| FieldWithInterval::parse_undefined_with_default_interval(x, &interval))
161                .collect::<Vec<_>>();
162
163            // Initialize TradingView client and retrieve data for specified symbols and fields
164            let tv = TradingView::new(&screener, exchange);
165            let symbols = tv
166                .scan_symbols(&symbols, &fields)
167                .await
168                .context("scan symbols error")?;
169
170            // Assemble data rows for output
171            let mut table: Vec<Vec<String>> = vec![];
172            for vals in symbols {
173                let mut row = vec![format!("\"{}\"", vals.symbol())];
174                for field in fields.iter() {
175                    let val = vals
176                        .values()
177                        .get(&field)
178                        .map_or("".into(), |x| x.to_string());
179                    row.push(val);
180                }
181                table.push(row);
182            }
183
184            // Print csv header
185            println!(
186                "symbol,{}",
187                fields
188                    .iter()
189                    .map(|x| x.to_string())
190                    .collect::<Vec<_>>()
191                    .join(",")
192            );
193            // Print csv data rows
194            for vals in table {
195                println!("{}", vals.join(","));
196            }
197        }
198        SubCommand::Get(args) => {
199            // Parse arguments
200            let screener = args.screener;
201            let exchange = args.exchange;
202            let interval = Interval::parse_undefined(&args.interval);
203            let symbol = args.symbol;
204            // Prepare fields with default intervals if not specified
205            let fields = args
206                .fields
207                .iter()
208                .map(|x| FieldWithInterval::parse_undefined_with_default_interval(x, &interval))
209                .collect::<Vec<_>>();
210
211            // Initialize TradingView client and retrieve data for specified symbol and fields
212            let tv = TradingView::new(&screener, exchange);
213            let symbol = tv
214                .get_symbol_fields_with_interval(&symbol, &fields)
215                .await
216                .context("Failed to retrieve symbol fields")?;
217
218            // Collect key-value pairs for output, including screener, symbol, interval, and field data
219            let mut values: Vec<(String, Value)> = Vec::new();
220            values.extend(vec![
221                ("screener".to_string(), json!(screener.to_string())),
222                ("symbol".to_string(), json!(symbol.symbol().to_string())),
223                ("interval".to_string(), json!(interval.to_string())),
224            ]);
225            for field in &fields {
226                if let Some(val) = symbol.values().get(field) {
227                    values.push((field.to_string(), val.clone()));
228                }
229            }
230
231            // Output data, formatted as JSON if specified, else as plain key-value pairs
232            if args.json {
233                println!(
234                    "{}",
235                    serde_json::to_string_pretty(&values.into_iter().collect::<HashMap<_, _>>())?
236                );
237            } else {
238                for (k, v) in values {
239                    println!("{:>13} : {}", k, v.to_string());
240                }
241            }
242        }
243        SubCommand::ListScreeners => {
244            for screener in Screener::all_screeners() {
245                println!("{:?}", screener);
246            }
247        }
248        SubCommand::ListIntervals => {
249            for interval in Interval::all_intervals() {
250                println!("{}", interval);
251            }
252        }
253    };
254    Ok(())
255}