3 # Copyright (C) 2008 by Daniel Martin Yerga
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 # StocksPy: Application to get stocks data from Yahoo Finance.
29 #osso_c = osso.Context("net.yerga.stockthis", "0.3", False)
31 from marketdata import markets, idmarket, localmarkets, localids
35 #detect if is ran locally or not
37 runningpath = sys.path[0]
39 if '/usr/share' in runningpath:
40 runninglocally = False
44 HOME = os.path.expanduser("~")
46 settingsdb, imgdir, configdir, logfile = \
47 settings.define_paths(runninglocally, HOME)
50 fhsize = gtk.HILDON_SIZE_FINGER_HEIGHT
51 horbtn = hildon.BUTTON_ARRANGEMENT_HORIZONTAL
52 ui_normal = gtk.HILDON_UI_MODE_NORMAL
53 ui_edit = gtk.HILDON_UI_MODE_EDIT
54 winprogind = hildon.hildon_gtk_window_set_progress_indicator
56 gtk.gdk.threads_init()
61 self.program = hildon.Program()
62 self.program.__init__()
63 gtk.set_application_name("StockThis")
64 self.window = hildon.StackableWindow()
65 self.window.set_default_size(800, 480)
66 self.program.add_window(self.window)
67 self.window.connect("destroy", gtk.main_quit)
69 menu = hildon.AppMenu()
70 self.window.set_app_menu(menu)
71 button = gtk.Button("About")
72 button.connect("clicked", self.on_about)
77 toolbar = self.main_toolbar(False, False)
79 parea = hildon.PannableArea()
80 tv = hildon.GtkTreeView(ui_normal)
81 inmodel = self.__create_model(markets, idmarket)
82 tv.connect("row-activated", self.show_instrument_view, inmodel)
87 vbox.pack_start(parea, True, True, 0)
88 vbox.pack_start(gtk.HSeparator(), False, False, 5)
89 vbox.pack_start(toolbar, False, False, 0)
92 self.window.show_all()
94 def show_instrument_view(self, widget, path, column, inmodel):
95 market = inmodel[path][0]
97 names = localmarkets[idmarket.index(market)]
98 ids = localids[idmarket.index(market)]
100 window = hildon.StackableWindow()
103 toolbar = self.main_toolbar(False, False)
105 parea = hildon.PannableArea()
106 tv = hildon.GtkTreeView(ui_normal)
107 model = self.__create_model(names, ids)
108 tv.connect("row-activated", self.show_quotes_view, model, False)
113 vbox.pack_start(parea, True, True, 0)
114 vbox.pack_start(gtk.HSeparator(), False, False, 5)
115 vbox.pack_start(toolbar, False, False, 0)
121 def show_quotes_view(self, widget, path, column, model, portfolio):
122 quote = model[path][0], model[path][1]
124 self.stocks_id = quote[0]
125 self.stocks_name = quote[1]
128 self.quotes_win = hildon.StackableWindow()
131 toolbar = self.main_toolbar(True, portfolio)
134 self.titlelbl = gtk.Label('')
135 self.titlelbl.set_markup('<b><big>' + quote[1].replace('&', '') +
137 color = gtk.gdk.color_parse("#03A5FF")
138 self.titlelbl.modify_fg(gtk.STATE_NORMAL, color)
140 parea = hildon.PannableArea()
145 label = gtk.Label('')
146 label.set_markup('<b><big>Price:</big></b>')
147 self.lprice = gtk.Label('')
148 hbox.pack_start(label, False, False, 20)
149 hbox.pack_start(self.lprice, False, False, 245)
150 vbox1.pack_start(hbox, True, True, 0)
153 label = gtk.Label('')
154 label.set_markup('<b><big>Change:</big></b>')
155 self.lchange = gtk.Label('')
156 self.lpercent = gtk.Label('')
157 hbox.pack_start(label, False, False, 20)
158 hbox.pack_start(self.lchange, False, False, 205)
159 hbox.pack_start(self.lpercent, False, False, 0)
160 vbox1.pack_start(hbox, True, True, 0)
163 label = gtk.Label('')
164 label.set_markup('<b><big>Volume:</big></b>')
165 self.lvolume = gtk.Label('')
166 hbox.pack_start(label, False, False, 20)
167 hbox.pack_start(self.lvolume, False, False, 207)
168 vbox1.pack_start(hbox, True, True, 0)
171 label = gtk.Label('')
172 label.set_markup('<b><big>52 week high:</big></b>')
173 self.l52whigh = gtk.Label('')
174 hbox.pack_start(label, False, False, 20)
175 hbox.pack_start(self.l52whigh, False, False, 110)
176 vbox1.pack_start(hbox, True, True, 0)
179 label = gtk.Label('')
180 label.set_markup('<b><big>52 week low:</big></b>')
181 self.l52wlow = gtk.Label('')
182 hbox.pack_start(label, False, False, 20)
183 hbox.pack_start(self.l52wlow, False, False, 125)
184 vbox1.pack_start(hbox, True, True, 0)
187 button1 = hildon.PickerButton(fhsize, horbtn)
188 data = ["50", "100", "200", "300", "400", "500", "600", "700", "800",
190 selector = self.create_selector(data, True)
191 button1.set_selector(selector)
192 button1.set_title("Shares")
193 #FIXME: Improve as it's shown you have a component in your portfolio
194 shares = self.get_shares_from_symbol()
195 button1.set_value(shares)
196 hbox.pack_start(button1, True, True, 0)
198 button = hildon.Button(fhsize, horbtn)
199 button.set_title("Add to Portfolio")
200 button.connect("clicked", self.add_to_portfolio, button1)
201 hbox.pack_start(button, True, True, 0)
204 label = gtk.Label('')
205 label.set_markup('<b><big>Shares:</big></b>')
206 self.shares = gtk.Label(shares)
207 hbox1.pack_start(label, False, False, 20)
208 hbox1.pack_start(self.shares, False, False, 215)
211 label = gtk.Label('')
212 label.set_markup('<b><big>Holdings Value:</big></b>')
213 self.holdingsvalue = gtk.Label("")
214 hbox2.pack_start(label, False, False, 20)
215 hbox2.pack_start(self.holdingsvalue, False, False, 85)
218 label = gtk.Label('')
219 label.set_markup("<b><big>Day's Value Change:</big></b>")
220 self.dayvaluechange = gtk.Label("")
221 hbox3.pack_start(label, False, False, 20)
222 hbox3.pack_start(self.dayvaluechange, False, False, 10)
225 vbox1.pack_start(hbox, False, False, 0)
227 vbox1.pack_start(hbox1, True, True, 0)
228 vbox1.pack_start(hbox2, True, True, 0)
229 vbox1.pack_start(hbox3, True, True, 0)
231 parea.add_with_viewport(vbox1)
233 vbox.pack_start(self.titlelbl, False, False, 0)
234 vbox.pack_start(gtk.HSeparator(), False, False, 0)
235 vbox.pack_start(parea, True, True, 0)
236 vbox.pack_start(gtk.HSeparator(), False, False, 5)
237 vbox.pack_start(toolbar, False, False, 0)
239 self.quotes_win.add(vbox)
240 self.quotes_win.show_all()
241 self.show_data(quote[0], self.quotes_win, shares)
243 def get_shares_from_symbol(self):
244 portfolio_data = settings.load_portfolio(settingsdb)
246 for item in portfolio_data :
247 if self.stocks_id in item:
251 def add_to_portfolio(self, widget, button):
252 shares = button.get_value()
256 portfolio = settings.load_portfolio(settingsdb)
258 for item in portfolio:
259 if self.stocks_id in item:
260 index = portfolio.index(item)
262 item = [self.stocks_id, self.stocks_name, shares, '-']
265 settings.insert_new_item_to_portfolio(settingsdb, item)
267 settings.delete_item_from_portfolio(settingsdb, self.stocks_id)
268 settings.insert_new_item_to_portfolio(settingsdb, item)
270 self.show_info_banner(widget, "Added to portfolio")
273 def create_selector(self, data, entry):
275 selector = hildon.TouchSelectorEntry(text=True)
277 selector = hildon.hildon_touch_selector_new_text()
278 for i in range(len(data)):
279 selector.append_text(data[i])
283 def show_data(self, quote, win, shares):
286 thread.start_new_thread(self.get_data, (quote, win, shares))
288 def get_data(self, quote, win, shares):
289 from ystockquote import ystockquote as yt
291 data = yt.get_all(quote)
293 print 'Failed to get internet data'
294 data = {'price': 'N/A', 'change': 'N/A', 'volume':'N/A',
295 '52_week_high': 'N/A', '52_week_low': 'N/A'}
296 self.titlelbl.set_markup('<b><big>Failed to get data</big></b>')
300 100.0 * float(data['change'])/(float(data['price']) - \
301 float(data['change']))
305 self.lprice.set_label(data['price'])
306 self.lchange.set_label(data['change'])
307 self.lpercent.set_label('%6.2f %%' % ch_percent)
309 if '-' in data['change']:
310 color = gtk.gdk.color_parse("#FF0000")
312 color = gtk.gdk.color_parse("#16EB78")
314 self.lpercent.modify_fg(gtk.STATE_NORMAL, color)
315 self.lchange.modify_fg(gtk.STATE_NORMAL, color)
317 self.lvolume.set_label(data['volume'])
318 self.l52whigh.set_label(data['52_week_high'])
319 self.l52wlow.set_label(data['52_week_low'])
322 daychange = float(shares)*float(data['change'])
326 holdvalue = float(shares)*float(data['price'])
330 self.dayvaluechange.set_label(str(daychange))
331 self.holdingsvalue.set_label(str(holdvalue))
335 def refresh_stock_data(self, widget, portfolio):
337 shares = self.get_shares_from_symbol()
341 self.show_data(self.stocks_id, self.quotes_win, shares)
343 def show_graph_view(self, widget):
344 win = hildon.StackableWindow()
347 toolbar = self.main_toolbar(False, True)
349 self.graphs_title = gtk.Label(self.stocks_name)
350 color = gtk.gdk.color_parse("#03A5FF")
351 self.graphs_title.modify_fg(gtk.STATE_NORMAL, color)
353 parea = hildon.PannableArea()
356 hbox.set_homogeneous(True)
358 button = hildon.Button(fhsize, horbtn)
359 button.set_label('1d')
360 button.connect("clicked", self.show_graph, '1d', win)
361 hbox.pack_start(button)
363 button = hildon.Button(fhsize, horbtn)
364 button.set_label('5d')
365 button.connect("clicked", self.show_graph, '5d', win)
366 hbox.pack_start(button)
368 button = hildon.Button(fhsize, horbtn)
369 button.set_label('3m')
370 button.connect("clicked", self.show_graph, '3m', win)
371 hbox.pack_start(button)
373 button = hildon.Button(fhsize, horbtn)
374 button.set_label('6m')
375 button.connect("clicked", self.show_graph, '6m', win)
376 hbox.pack_start(button)
378 button = hildon.Button(fhsize, horbtn)
379 button.set_label('1y')
380 button.connect("clicked", self.show_graph, '1y', win)
381 hbox.pack_start(button)
383 button = hildon.Button(fhsize, horbtn)
384 button.set_label('2y')
385 button.connect("clicked", self.show_graph, '2y', win)
386 hbox.pack_start(button)
388 button = hildon.Button(fhsize, horbtn)
389 button.set_label('5y')
390 button.connect("clicked", self.show_graph, '5y', win)
391 hbox.pack_start(button)
393 button = hildon.Button(fhsize, horbtn)
394 button.set_label('Max')
395 button.connect("clicked", self.show_graph, 'max', win)
396 hbox.pack_start(button)
399 vbox1.pack_start(hbox, False, False, 0)
401 self.graph = gtk.Image()
402 vbox1.pack_start(self.graph, True, True, 0)
404 parea.add_with_viewport(vbox1)
406 vbox.pack_start(self.graphs_title, False, False, 0)
407 vbox.pack_start(gtk.HSeparator(), False, False, 0)
408 vbox.pack_start(parea, True, True, 0)
409 vbox.pack_start(gtk.HSeparator(), False, False, 5)
410 vbox.pack_start(toolbar, False, False, 0)
415 self.show_graph(None, '1d', win)
417 def show_graph(self, widget, option, win):
420 thread.start_new_thread(self.get_graph_data, (option, win))
422 def get_graph_data(self, option, win):
423 kind = self.stocks_id
425 url = 'http://uk.ichart.yahoo.com/b?s=%s' % kind
427 url = 'http://uk.ichart.yahoo.com/w?s=%s' % kind
429 url = 'http://chart.finance.yahoo.com/c/3m/s/%s' % kind.lower()
431 url = 'http://chart.finance.yahoo.com/c/6m/s/%s' % kind.lower()
433 url = 'http://chart.finance.yahoo.com/c/1y/s/%s' % kind.lower()
435 url = 'http://chart.finance.yahoo.com/c/2y/s/%s' % kind.lower()
437 url = 'http://chart.finance.yahoo.com/c/5y/s/%s' % kind.lower()
438 elif option == 'max':
439 url = 'http://chart.finance.yahoo.com/c/my/s/%s' % kind.lower()
442 myimg = urllib2.urlopen(url)
443 imgdata = myimg.read()
445 pbl = gtk.gdk.PixbufLoader()
448 pbuf = pbl.get_pixbuf()
450 self.graph.set_from_pixbuf(pbuf)
454 self.graphs_title.set_label('Failed to get data')
457 def _tv_columns(self, treeview):
458 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
459 column.set_visible(False)
460 treeview.append_column(column)
462 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
463 treeview.append_column(column)
465 def __create_model(self, names, ids):
466 lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING)
467 for item in range(len(names)):
468 iter = lstore.append()
469 lstore.set(iter, 0, ids[item], 1, names[item])
472 def main_toolbar(self, quotesview, portfolio):
474 toolbar.set_homogeneous(True)
476 portfolio_btn = hildon.Button(fhsize, horbtn)
477 portfolio_btn.set_title("Portfolio")
478 portfolio_btn.connect("clicked", self.show_portfolio_view)
480 graph_btn = hildon.Button(fhsize, horbtn)
481 graph_btn.set_title("Graph")
482 graph_btn.connect("clicked", self.show_graph_view)
484 refresh_btn = hildon.Button(fhsize, horbtn)
485 refresh_btn.set_title("Refresh")
486 refresh_btn.connect("clicked", self.refresh_stock_data, portfolio)
489 toolbar.pack_start(portfolio_btn)
491 toolbar.pack_start(graph_btn)
492 toolbar.pack_start(refresh_btn)
498 def show_portfolio_view(self, widget):
499 data = settings.load_portfolio(settingsdb)
501 win = hildon.StackableWindow()
505 parea = hildon.PannableArea()
506 tv = hildon.GtkTreeView(ui_normal)
507 tv.set_headers_visible(True)
508 self.portfolio_model = self._create_portfolio_model(data)
509 tv.connect("row-activated", self.show_quotes_view, self.portfolio_model, True)
510 tv.set_model(self.portfolio_model)
511 self._tv_portfolio_columns(tv)
515 button = hildon.Button(fhsize, horbtn)
516 button.set_title("Refresh All")
517 button.connect("clicked", self.refresh_portfolio, tv, win)
518 hbox.pack_start(button, True, True, 0)
520 button = hildon.Button(fhsize, horbtn)
521 button.set_title("Remove")
522 button.connect("clicked", self.remove_item)
523 hbox.pack_start(button, True, True, 0)
525 vbox.pack_start(parea, True, True, 0)
526 vbox.pack_start(hbox, False, False, 0)
530 def remove_item(self, widget):
531 win = hildon.StackableWindow()
533 toolbar = hildon.EditToolbar("Choose items to delete", "Delete")
534 win.set_edit_toolbar(toolbar)
537 parea = hildon.PannableArea()
538 tv = hildon.GtkTreeView(ui_edit)
539 selection = tv.get_selection()
540 selection.set_mode(gtk.SELECTION_MULTIPLE)
541 tv.set_model(self.portfolio_model)
542 self._tv_remove_portfolio_columns(tv)
545 toolbar.connect("button-clicked", self.delete_from_portfolio, win, tv,
547 toolbar.connect_object("arrow-clicked", gtk.Window.destroy, win)
549 vbox.pack_start(parea, True, True, 0)
553 def delete_from_portfolio(self, widget, win, tv, selection):
554 if not self.is_treeview_selected(tv):
557 conf = self.show_confirmation(win, "Delete items?")
560 selmodel, selected = selection.get_selected_rows()
561 iters = [selmodel.get_iter(path) for path in selected]
563 symbol = selmodel.get_value(i, 0)
564 settings.delete_item_from_portfolio(settingsdb, symbol)
567 def _tv_remove_portfolio_columns(self, treeview):
568 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
569 column.set_visible(False)
570 treeview.append_column(column)
572 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
573 column.set_property("expand", True)
574 treeview.append_column(column)
576 column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2)
577 column.set_visible(False)
578 treeview.append_column(column)
580 column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
581 column.set_visible(False)
582 treeview.append_column(column)
584 def refresh_portfolio(self, widget, tv, win):
585 data = settings.load_portfolio(settingsdb)
588 thread.start_new_thread(self._do_refresh_portfolio, (data, tv, win))
590 def _do_refresh_portfolio(self, data, tv, win):
592 item[3] = self.get_price(item[0])
594 self.portfolio_model = self._create_portfolio_model(data)
595 tv.set_model(self.portfolio_model)
598 def get_price(self, symbol):
599 from ystockquote import ystockquote as yt
600 price = yt.get_price(symbol)
604 def _create_portfolio_model(self, data):
605 lstore = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING,
606 gobject.TYPE_STRING, gobject.TYPE_STRING)
608 iter = lstore.append()
609 lstore.set(iter, 0, item[0], 1, item[1], 2, item[2], 3, item[3])
612 def _tv_portfolio_columns(self, treeview):
613 column = gtk.TreeViewColumn('ID', gtk.CellRendererText(), text=0)
614 column.set_visible(False)
615 treeview.append_column(column)
617 column = gtk.TreeViewColumn('Name', gtk.CellRendererText(), text=1)
618 column.set_property("expand", True)
619 treeview.append_column(column)
621 column = gtk.TreeViewColumn('Shares', gtk.CellRendererText(), text=2)
622 treeview.append_column(column)
624 column = gtk.TreeViewColumn('Price', gtk.CellRendererText(), text=3)
625 treeview.append_column(column)
627 def show_confirmation(self, window, msg):
628 dialog = hildon.hildon_note_new_confirmation(window, msg)
630 result = dialog.run()
631 if result == gtk.RESPONSE_OK:
638 def show_info_banner(self, widget, msg):
639 hildon.hildon_banner_show_information(widget, 'qgn_note_infoprint', msg)
641 def is_treeview_selected(self, treeview):
642 selection = treeview.get_selection()
643 if selection.count_selected_rows() == 0:
644 self.show_info_banner(treeview, 'No selected item')
649 def on_about(self, widget):
650 dialog = gtk.AboutDialog()
651 dialog.set_name("StockThis")
652 dialog.set_version("0.3")
653 dialog.set_copyright("Copyright © 2009")
654 dialog.set_website("http://stockthis.garage.maemo.org")
655 dialog.set_authors(["Daniel Martin Yerga <dyerga@gmail.com>"])
656 logo = gtk.gdk.pixbuf_new_from_file(imgdir + "stockthis.png")
657 dialog.set_logo(logo)
658 dialog.set_license("This program is released under the GNU\nGeneral Public License. Please visit \nhttp://www.gnu.org/copyleft/gpl.html\nfor details.")
659 dialog.set_artists(["Logo by Daniel Martin Yerga"])
663 if __name__ == "__main__":
664 stockspy = StocksPy()
665 gtk.gdk.threads_enter()
667 gtk.gdk.threads_leave()