1from datetime import datetime, timedelta, date
2
3from AnyQt.QtCore import QDate
4from AnyQt.QtWidgets import QDateEdit, QComboBox
5
6from Orange.widgets import widget, gui, settings
7from Orange.widgets.widget import Output
8
9from orangecontrib.timeseries import Timeseries
10from orangecontrib.timeseries.datasources import finance_data
11
12
13class OWYahooFinance(widget.OWWidget):
14    name = 'Yahoo Finance'
15    description = "Generate time series from Yahoo Finance stock market data."
16    icon = 'icons/YahooFinance.svg'
17    priority = 9
18
19    class Outputs:
20        time_series = Output("Time series", Timeseries)
21
22    QT_DATE_FORMAT = 'yyyy-MM-dd'
23    PY_DATE_FORMAT = '%Y-%m-%d'
24    MIN_DATE = date(1851, 1, 1)
25
26    date_from = settings.Setting(
27        (datetime.now().date() - timedelta(5 * 365)).strftime(PY_DATE_FORMAT))
28    date_to = settings.Setting(datetime.now().date().strftime(PY_DATE_FORMAT))
29    symbols = settings.Setting(['AMZN', 'AAPL', 'GOOG', 'FB', 'SPY', '^DJI', '^TNX'])
30
31    want_main_area = False
32    resizing_enabled = False
33
34    class Error(widget.OWWidget.Error):
35        download_error = widget.Msg('Failed to download data (HTTP Error {}). '
36                                    'Wrong stock symbol?')
37
38    def __init__(self):
39        box = gui.widgetBox(self.controlArea, 'Yahoo Finance Stock Data',
40                            orientation='horizontal')
41        lbox = gui.widgetBox(box, orientation='vertical')
42        hbox = gui.widgetBox(lbox, orientation='horizontal')
43        gui.label(hbox, self, 'Ticker:')
44        self.combo = combo = QComboBox(editable=True,
45                                       insertPolicy=QComboBox.InsertAtTop)
46        combo.addItems(self.symbols)
47        hbox.layout().addWidget(combo)
48        # combo = gui.comboBox(
49        #     lbox, self, 'symbol',#, items=self.symbols,
50        #     label='Ticker:', orientation='horizontal',
51        #     editable=True, maximumContentsLength=-1)
52        gui.rubber(combo.parentWidget())
53        minDate = QDate.fromString(self.MIN_DATE.strftime(self.PY_DATE_FORMAT),
54                                   self.QT_DATE_FORMAT)
55        date_from = QDateEdit(
56            QDate.fromString(self.date_from, self.QT_DATE_FORMAT),
57            displayFormat=self.QT_DATE_FORMAT,
58            minimumDate=minDate,
59            calendarPopup=True)
60        date_to = QDateEdit(
61            QDate.fromString(self.date_to, self.QT_DATE_FORMAT),
62            displayFormat=self.QT_DATE_FORMAT,
63            minimumDate=minDate,
64            calendarPopup=True)
65        date_from.dateChanged.connect(
66            lambda date: setattr(self, 'date_from',
67                                 date.toString(self.QT_DATE_FORMAT)))
68        date_to.dateChanged.connect(
69            lambda date: setattr(self, 'date_to',
70                                 date.toString(self.QT_DATE_FORMAT)))
71        hbox = gui.hBox(lbox)
72        gui.label(hbox, self, "From:")
73        hbox.layout().addWidget(date_from)
74        hbox = gui.hBox(lbox)
75        gui.label(hbox, self, "To:")
76        hbox.layout().addWidget(date_to)
77
78        self.button = gui.button(self.controlArea, self, 'Download',
79                                 callback=self.download)
80
81    def download(self):
82        date_from = datetime.strptime(self.date_from, self.PY_DATE_FORMAT)
83        date_to = datetime.strptime(self.date_to, self.PY_DATE_FORMAT)
84
85        # Update symbol in symbols history
86        symbol = self.combo.currentText().strip().upper()
87        self.combo.removeItem(self.combo.currentIndex())
88        self.combo.insertItem(0, symbol)
89        self.combo.setCurrentIndex(0)
90        try:
91            self.symbols.remove(symbol)
92        except ValueError:
93            pass
94        self.symbols.insert(0, symbol)
95
96        if not symbol:
97            return
98
99        self.Error.clear()
100        with self.progressBar(3) as progress:
101            try:
102                progress.advance()
103                self.button.setDisabled(True)
104                data = finance_data(symbol, date_from, date_to)
105
106                self.Outputs.time_series.send(data)
107            except Exception as e:
108                self.Error.download_error(getattr(e, 'status', -1))
109            finally:
110                self.button.setDisabled(False)
111
112
113if __name__ == "__main__":
114    from AnyQt.QtWidgets import QApplication
115
116    a = QApplication([])
117    ow = OWYahooFinance()
118
119    ow.show()
120    a.exec()
121