1 from PyQt4.QtGui import *
2 from PyQt4.QtCore import *
10 from decimal import Decimal
11 from electrum.plugins import BasePlugin
12 from electrum.i18n import _
13 from electrum_gui.qt.util import *
16 EXCHANGES = ["BitcoinAverage",
28 class Exchanger(threading.Thread):
30 def __init__(self, parent):
31 threading.Thread.__init__(self)
34 self.quote_currencies = None
35 self.lock = threading.Lock()
36 self.query_rates = threading.Event()
37 self.use_exchange = self.parent.config.get('use_exchange', "Blockchain")
38 self.parent.exchanges = EXCHANGES
39 self.parent.currencies = ["EUR","GBP","USD"]
40 self.parent.win.emit(SIGNAL("refresh_exchanges_combo()"))
41 self.parent.win.emit(SIGNAL("refresh_currencies_combo()"))
42 self.is_running = False
44 def get_json(self, site, get_string):
46 connection = httplib.HTTPSConnection(site)
47 connection.request("GET", get_string)
50 resp = connection.getresponse()
51 if resp.reason == httplib.responses[httplib.NOT_FOUND]:
54 json_resp = json.loads(resp.read())
60 def exchange(self, btc_amount, quote_currency):
62 if self.quote_currencies is None:
64 quote_currencies = self.quote_currencies.copy()
65 if quote_currency not in quote_currencies:
67 if self.use_exchange == "CoinDesk":
69 resp_rate = self.get_json('api.coindesk.com', "/v1/bpi/currentprice/" + str(quote_currency) + ".json")
72 return btc_amount * decimal.Decimal(str(resp_rate["bpi"][str(quote_currency)]["rate_float"]))
73 return btc_amount * decimal.Decimal(quote_currencies[quote_currency])
76 self.is_running = False
78 def update_rate(self):
79 self.use_exchange = self.parent.config.get('use_exchange', "Blockchain")
81 "BitcoinAverage": self.update_ba,
82 "BitcoinVenezuela": self.update_bv,
83 "BitPay": self.update_bp,
84 "Blockchain": self.update_bc,
85 "BTCChina": self.update_CNY,
86 "CaVirtEx": self.update_cv,
87 "CoinDesk": self.update_cd,
88 "Coinbase": self.update_cb,
89 "LocalBitcoins": self.update_lb,
90 "Winkdex": self.update_wd,
93 update_rates[self.use_exchange]()
98 self.is_running = True
99 while self.is_running:
100 self.query_rates.clear()
102 self.query_rates.wait(150)
107 resp_currencies = self.get_json('api.coindesk.com', "/v1/bpi/supported-currencies.json")
111 quote_currencies = {}
112 for cur in resp_currencies:
113 quote_currencies[str(cur["currency"])] = 0.0
115 self.quote_currencies = quote_currencies
116 self.parent.set_currencies(quote_currencies)
120 winkresp = self.get_json('winkdex.com', "/static/data/0_600_288.json")
121 ####could need nonce value in GET, no Docs available
124 quote_currencies = {"USD": 0.0}
125 ####get y of highest x in "prices"
126 lenprices = len(winkresp["prices"])
127 usdprice = winkresp["prices"][lenprices-1]["y"]
129 quote_currencies["USD"] = decimal.Decimal(usdprice)
131 self.quote_currencies = quote_currencies
134 self.parent.set_currencies(quote_currencies)
138 jsonresp = self.get_json('www.cavirtex.com', "/api/CAD/ticker.json")
141 quote_currencies = {"CAD": 0.0}
142 cadprice = jsonresp["last"]
144 quote_currencies["CAD"] = decimal.Decimal(cadprice)
146 self.quote_currencies = quote_currencies
149 self.parent.set_currencies(quote_currencies)
151 def update_CNY(self):
153 jsonresp = self.get_json('data.btcchina.com', "/data/ticker")
156 quote_currencies = {"CNY": 0.0}
157 cnyprice = jsonresp["ticker"]["last"]
159 quote_currencies["CNY"] = decimal.Decimal(cnyprice)
161 self.quote_currencies = quote_currencies
164 self.parent.set_currencies(quote_currencies)
168 jsonresp = self.get_json('bitpay.com', "/api/rates")
171 quote_currencies = {}
174 quote_currencies[str(r["code"])] = decimal.Decimal(r["rate"])
176 self.quote_currencies = quote_currencies
179 self.parent.set_currencies(quote_currencies)
183 jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates")
187 quote_currencies = {}
190 if r[:7] == "btc_to_":
191 quote_currencies[r[7:].upper()] = self._lookup_rate_cb(jsonresp, r)
193 self.quote_currencies = quote_currencies
196 self.parent.set_currencies(quote_currencies)
201 jsonresp = self.get_json('blockchain.info', "/ticker")
204 quote_currencies = {}
207 quote_currencies[r] = self._lookup_rate(jsonresp, r)
209 self.quote_currencies = quote_currencies
212 self.parent.set_currencies(quote_currencies)
213 # print "updating exchange rate", self.quote_currencies["USD"]
217 jsonresp = self.get_json('localbitcoins.com', "/bitcoinaverage/ticker-all-currencies/")
220 quote_currencies = {}
223 quote_currencies[r] = self._lookup_rate_lb(jsonresp, r)
225 self.quote_currencies = quote_currencies
228 self.parent.set_currencies(quote_currencies)
233 jsonresp = self.get_json('api.bitcoinvenezuela.com', "/")
236 quote_currencies = {}
238 for r in jsonresp["BTC"]:
239 quote_currencies[r] = Decimal(jsonresp["BTC"][r])
241 self.quote_currencies = quote_currencies
244 self.parent.set_currencies(quote_currencies)
249 jsonresp = self.get_json('api.bitcoinaverage.com', "/ticker/global/all")
252 quote_currencies = {}
255 if not r == "timestamp":
256 quote_currencies[r] = self._lookup_rate_ba(jsonresp, r)
258 self.quote_currencies = quote_currencies
261 self.parent.set_currencies(quote_currencies)
264 def get_currencies(self):
265 return [] if self.quote_currencies == None else sorted(self.quote_currencies.keys())
267 def _lookup_rate(self, response, quote_id):
268 return decimal.Decimal(str(response[str(quote_id)]["15m"]))
269 def _lookup_rate_cb(self, response, quote_id):
270 return decimal.Decimal(str(response[str(quote_id)]))
271 def _lookup_rate_ba(self, response, quote_id):
272 return decimal.Decimal(response[str(quote_id)]["last"])
273 def _lookup_rate_lb(self, response, quote_id):
274 return decimal.Decimal(response[str(quote_id)]["rates"]["last"])
277 class Plugin(BasePlugin):
280 return "Exchange rates"
282 def description(self):
283 return """exchange rates, retrieved from blockchain.info, CoinDesk, or Coinbase"""
286 def __init__(self,a,b):
287 BasePlugin.__init__(self,a,b)
288 self.currencies = [self.config.get('currency', "EUR")]
289 self.exchanges = [self.config.get('use_exchange', "Blockchain")]
292 self.win = self.gui.main_window
293 self.win.connect(self.win, SIGNAL("refresh_currencies()"), self.win.update_status)
295 self.exchanger = Exchanger(self)
296 self.exchanger.start()
297 self.gui.exchanger = self.exchanger #
299 def set_currencies(self, currency_options):
300 self.currencies = sorted(currency_options)
301 self.win.emit(SIGNAL("refresh_currencies()"))
302 self.win.emit(SIGNAL("refresh_currencies_combo()"))
305 def set_quote_text(self, btc_balance, r):
306 r[0] = self.create_quote_text(Decimal(btc_balance) / 100000000)
308 def create_quote_text(self, btc_balance):
309 quote_currency = self.config.get("currency", "EUR")
310 self.exchanger.use_exchange = self.config.get("use_exchange", "Blockchain")
311 quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
312 if quote_balance is None:
315 quote_text = "%.2f %s" % (quote_balance, quote_currency)
318 def load_wallet(self, wallet):
321 for item in self.wallet.get_tx_history(self.wallet.storage.get("current_account", None)):
322 tx_hash, conf, is_mine, value, fee, balance, timestamp = item
323 tx_list[tx_hash] = {'value': value, 'timestamp': timestamp, 'balance': balance}
325 self.tx_list = tx_list
328 def requires_settings(self):
333 out = BasePlugin.toggle(self)
334 self.win.update_status()
339 self.exchanger.stop()
341 def history_tab_update(self):
342 if self.config.get('history_rates', 'unchecked') == "checked":
343 tx_list = self.tx_list
345 mintimestr = datetime.datetime.fromtimestamp(int(min(tx_list.items(), key=lambda x: x[1]['timestamp'])[1]['timestamp'])).strftime('%Y-%m-%d')
346 maxtimestr = datetime.datetime.now().strftime('%Y-%m-%d')
348 resp_hist = self.exchanger.get_json('api.coindesk.com', "/v1/bpi/historical/close.json?start=" + mintimestr + "&end=" + maxtimestr)
352 self.gui.main_window.is_edit = True
353 self.gui.main_window.history_list.setColumnCount(6)
354 self.gui.main_window.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance'), _('Fiat Amount')] )
355 root = self.gui.main_window.history_list.invisibleRootItem()
356 childcount = root.childCount()
357 for i in range(childcount):
360 tx_info = tx_list[str(item.data(0, Qt.UserRole).toPyObject())]
362 newtx = self.wallet.get_tx_history()
363 v = newtx[[x[0] for x in newtx].index(str(item.data(0, Qt.UserRole).toPyObject()))][3]
365 tx_info = {'timestamp':int(datetime.datetime.now().strftime("%s")), 'value': v }
367 tx_time = int(tx_info['timestamp'])
368 tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d')
369 tx_USD_val = "%.2f %s" % (Decimal(tx_info['value']) / 100000000 * Decimal(resp_hist['bpi'][tx_time_str]), "USD")
371 item.setText(5, tx_USD_val)
372 if Decimal(tx_info['value']) < 0:
373 item.setForeground(5, QBrush(QColor("#BC1E1E")))
375 for i, width in enumerate(self.gui.main_window.column_widths['history']):
376 self.gui.main_window.history_list.setColumnWidth(i, width)
377 self.gui.main_window.history_list.setColumnWidth(4, 140)
378 self.gui.main_window.history_list.setColumnWidth(5, 120)
379 self.gui.main_window.is_edit = False
382 def settings_widget(self, window):
383 return EnterButton(_('Settings'), self.settings_dialog)
385 def settings_dialog(self):
387 layout = QGridLayout(d)
388 layout.addWidget(QLabel(_('Exchange rate API: ')), 0, 0)
389 layout.addWidget(QLabel(_('Currency: ')), 1, 0)
390 layout.addWidget(QLabel(_('History Rates: ')), 2, 0)
392 combo_ex = QComboBox()
393 hist_checkbox = QCheckBox()
394 hist_checkbox.setEnabled(False)
395 if self.config.get('history_rates', 'unchecked') == 'unchecked':
396 hist_checkbox.setChecked(False)
398 hist_checkbox.setChecked(True)
399 ok_button = QPushButton(_("OK"))
403 cur_request = str(self.currencies[x])
406 if cur_request != self.config.get('currency', "EUR"):
407 self.config.set_key('currency', cur_request, True)
408 if cur_request == "USD" and self.config.get('use_exchange', "Blockchain") == "CoinDesk":
409 hist_checkbox.setEnabled(True)
411 hist_checkbox.setChecked(False)
412 hist_checkbox.setEnabled(False)
413 self.win.update_status()
416 hist_checkbox.setChecked(False)
417 hist_checkbox.setEnabled(False)
420 cur_request = str(self.exchanges[x])
421 if cur_request != self.config.get('use_exchange', "Blockchain"):
422 self.config.set_key('use_exchange', cur_request, True)
425 self.exchanger.query_rates.set()
426 if cur_request == "CoinDesk":
427 if self.config.get('currency', "EUR") == "USD":
428 hist_checkbox.setEnabled(True)
433 set_currencies(combo)
434 self.win.update_status()
436 def on_change_hist(checked):
438 self.config.set_key('history_rates', 'checked')
439 self.history_tab_update()
441 self.config.set_key('history_rates', 'unchecked')
442 self.gui.main_window.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance')] )
443 self.gui.main_window.history_list.setColumnCount(5)
444 for i,width in enumerate(self.gui.main_window.column_widths['history']):
445 self.gui.main_window.history_list.setColumnWidth(i, width)
447 def set_hist_check(hist_checkbox):
448 if self.config.get('use_exchange', "Blockchain") == "CoinDesk":
449 hist_checkbox.setEnabled(True)
451 hist_checkbox.setEnabled(False)
453 def set_currencies(combo):
454 current_currency = self.config.get('currency', "EUR")
459 combo.addItems(self.currencies)
461 index = self.currencies.index(current_currency)
464 combo.setCurrentIndex(index)
466 def set_exchanges(combo_ex):
471 combo_ex.addItems(self.exchanges)
473 index = self.exchanges.index(self.config.get('use_exchange', "Blockchain"))
476 combo_ex.setCurrentIndex(index)
481 set_exchanges(combo_ex)
482 set_currencies(combo)
483 set_hist_check(hist_checkbox)
484 combo.currentIndexChanged.connect(on_change)
485 combo_ex.currentIndexChanged.connect(on_change_ex)
486 hist_checkbox.stateChanged.connect(on_change_hist)
487 combo.connect(self.win, SIGNAL('refresh_currencies_combo()'), lambda: set_currencies(combo))
488 combo_ex.connect(d, SIGNAL('refresh_exchanges_combo()'), lambda: set_exchanges(combo_ex))
489 ok_button.clicked.connect(lambda: ok_clicked())
490 layout.addWidget(combo,1,1)
491 layout.addWidget(combo_ex,0,1)
492 layout.addWidget(hist_checkbox,2,1)
493 layout.addWidget(ok_button,3,1)