Repository: tarkah/tickrs (language: rust)

Issue:
Crash on Crypto
Crashes on unwrap of None value when looking at options of Crypto assets.


Discussion / hints:
Can you give me the full panic? What "symbol" do you enter to get it to crash? I don't support crypto but the program shouldn't panic, it should just show a blank screen for unknown symbols
![F2982F6D-6675-42D2-93F3-569F6275E986](https://user-images.githubusercontent.com/7826229/97213576-29483d00-1798-11eb-90a6-1d7df1a7caf3.GIF)

```
PS C:\Users\ianm> tickrs

Backtrace omitted. Run with RUST_BACKTRACE=1 to display it.
Run with RUST_BACKTRACE=full to include source snippets.

The application panicked (crashed).
  called `Option::unwrap()` on a `None` value
in C:\Users\ianm\.cargo\registry\src\github.com-1ecc6299db9ec823\tickrs-0.7.1\src\widget\options.rs, line 87
thread: main
```
wow... TIL you can get to crypto via that Yahoo Finance API and it basically works :P Knowing this, I can make sure crypto is fully supported properly and that the market hours are being accounted for accurately for it on the 1D view. I'll probably just disable the options pane since that won't be applicable for crypto. Thanks for the bug report!
Options would be applicable, but not via Yahoo API, something like [tradingview](https://www.tradingview.com/rest-api-spec/) that combines stock and multiple crypto exchanges would have both combined and keep the same API

Another option would be RobinHood's API as they do both, I think tradingview requires some brokerage account etc.

Edit: Link
Got it. Honestly the options pane isn't even that useful since the options data from Yahoo finance is pretty delayed.

Unfortunately I don't know of any other api that I can get **free** intraday & historical charting data, and realtime options data. On top of that crypto options data.  I don't plan on supporting any premium api with this program that'd require the user to plug in an API key. I want it to just work out of the box for anyone.

I'll go ahead and disable the options pane for any crypto instruments from the Yahoo finance api.
reasonable action. I'm building mining tooling and monitoring because i'm tired of paid mining OS and extra fees. I found your project and took some inspiration from it for my daily/weekly/monthly profitability estimations. Most miners have an exchange API for crypto so it's something more feasible for my use case. 
Sidenote, if you use Brave Rewards or setup a BAT wallet, folks can send you a Tip, maybe accept donations via crypto with a public wallet too. BTC, ETH, XMR etc. I for one like to Tip folks who share good open source software and i find it useful.
Interesting! Glad my project could serve for some inspiration. 

I appreciate the recommendation for accepting donations. As this is just a hobby for me, I don't expect donations of any kind. If I were to gain a large user base and needed to dedicate more of my free time to the project, then I'd definitely consider it. Thank you!

Fix patch:
diff --git a/api/src/model.rs b/api/src/model.rs
index dc708f3..625d650 100644
--- a/api/src/model.rs
+++ b/api/src/model.rs
@@ -68,6 +68,7 @@ pub struct ChartData {
 #[serde(rename_all = "camelCase")]
 #[derive(Debug, Deserialize, Clone)]
 pub struct ChartMeta {
+    pub instrument_type: Option<String>,
     pub regular_market_price: f32,
     pub chart_previous_close: f32,
     pub current_trading_period: Option<ChartCurrentTradingPeriod>,
diff --git a/src/event.rs b/src/event.rs
index 458c4af..25a3c43 100644
--- a/src/event.rs
+++ b/src/event.rs
@@ -58,9 +58,10 @@ pub fn handle_keys_display_stock(
                 let _ = request_redraw.try_send(());
             }
             KeyCode::Char('o') => {
-                app.stocks[app.current_tab].toggle_options();
-                app.mode = app::Mode::DisplayOptions;
-                let _ = request_redraw.try_send(());
+                if app.stocks[app.current_tab].toggle_options() {
+                    app.mode = app::Mode::DisplayOptions;
+                    let _ = request_redraw.try_send(());
+                }
             }
             KeyCode::Char('x') => {
                 let mut show_x_labels = SHOW_X_LABELS.write().unwrap();
diff --git a/src/widget/options.rs b/src/widget/options.rs
index b6b782a..ec935d1 100644
--- a/src/widget/options.rs
+++ b/src/widget/options.rs
@@ -78,7 +78,9 @@ impl OptionsState {
             OptionType::Put => self.selected_type = OptionType::Call,
         }
 
-        self.set_selected_as_closest();
+        if self.data().is_some() {
+            self.set_selected_as_closest();
+        }
     }
 
     fn set_selected_as_closest(&mut self) {
diff --git a/src/widget/stock.rs b/src/widget/stock.rs
index 73e0aaf..a5c2f44 100644
--- a/src/widget/stock.rs
+++ b/src/widget/stock.rs
@@ -91,7 +91,19 @@ impl StockState {
         }
     }
 
-    pub fn toggle_options(&mut self) {
+    fn options_enabled(&self) -> bool {
+        self.chart_meta
+            .as_ref()
+            .map(|m| m.instrument_type.as_deref())
+            .flatten()
+            != Some("CRYPTOCURRENCY")
+    }
+
+    pub fn toggle_options(&mut self) -> bool {
+        if !self.options_enabled() {
+            return false;
+        }
+
         self.show_options = !self.show_options;
 
         if self.options.is_some() {
@@ -99,6 +111,8 @@ impl StockState {
         } else {
             self.options = Some(OptionsState::new(self.symbol.clone()));
         }
+
+        true
     }
 
     pub fn min_max(&self) -> (f32, f32) {
@@ -398,26 +412,32 @@ impl StatefulWidget for StockWidget {
                 toggle_block.render(info_chunks[1], buf);
                 info_chunks[1].x += 2;
                 info_chunks[1].width -= 2;
+                info_chunks[1].y += 2;
+                info_chunks[1].height -= 2;
 
-                let toggle_info = [
-                    Text::styled(
-                        "\n\nOptions  'o'\n",
-                        Style::default().bg(if state.show_options {
+                let mut toggle_info = vec![Text::styled("Summary  's'", Style::default())];
+
+                if loaded {
+                    toggle_info.push(Text::styled(
+                        "\nX Labels 'x'",
+                        Style::default().bg(if show_x_labes {
                             Color::DarkGray
                         } else {
                             Color::Reset
                         }),
-                    ),
-                    Text::styled("Summary  's'\n", Style::default()),
-                    Text::styled(
-                        "X Labels 'x'",
-                        Style::default().bg(if show_x_labes {
+                    ));
+                }
+
+                if state.options_enabled() && loaded {
+                    toggle_info.push(Text::styled(
+                        "\nOptions  'o'",
+                        Style::default().bg(if state.show_options {
                             Color::DarkGray
                         } else {
                             Color::Reset
                         }),
-                    ),
-                ];
+                    ));
+                }
 
                 Paragraph::new(toggle_info.iter())
                     .style(Style::default().fg(Color::White))


Test patch: