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)
294 self.btc_rate = Decimal(0.0)
296 self.exchanger = Exchanger(self)
297 self.exchanger.start()
298 self.gui.exchanger = self.exchanger #
300 def set_currencies(self, currency_options):
301 self.currencies = sorted(currency_options)
302 self.win.emit(SIGNAL("refresh_currencies()"))
303 self.win.emit(SIGNAL("refresh_currencies_combo()"))
306 def set_quote_text(self, btc_balance, r):
307 r[0] = self.create_quote_text(Decimal(btc_balance) / 100000000)
309 def create_quote_text(self, btc_balance):
310 quote_currency = self.config.get("currency", "EUR")
311 self.exchanger.use_exchange = self.config.get("use_exchange", "Blockchain")
312 cur_rate = self.exchanger.exchange(Decimal(1.0), quote_currency)
316 quote_balance = btc_balance * Decimal(cur_rate)
317 self.btc_rate = cur_rate
318 quote_text = "%.2f %s" % (quote_balance, quote_currency)
321 def load_wallet(self, wallet):
324 for item in self.wallet.get_tx_history(self.wallet.storage.get("current_account", None)):
325 tx_hash, conf, is_mine, value, fee, balance, timestamp = item
326 tx_list[tx_hash] = {'value': value, 'timestamp': timestamp, 'balance': balance}
328 self.tx_list = tx_list
331 def requires_settings(self):
336 out = BasePlugin.toggle(self)
337 self.win.update_status()
342 self.exchanger.stop()
344 def history_tab_update(self):
345 if self.config.get('history_rates', 'unchecked') == "checked":
346 tx_list = self.tx_list
348 mintimestr = datetime.datetime.fromtimestamp(int(min(tx_list.items(), key=lambda x: x[1]['timestamp'])[1]['timestamp'])).strftime('%Y-%m-%d')
349 maxtimestr = datetime.datetime.now().strftime('%Y-%m-%d')
351 resp_hist = self.exchanger.get_json('api.coindesk.com', "/v1/bpi/historical/close.json?start=" + mintimestr + "&end=" + maxtimestr)
355 self.gui.main_window.is_edit = True
356 self.gui.main_window.history_list.setColumnCount(6)
357 self.gui.main_window.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance'), _('Fiat Amount')] )
358 root = self.gui.main_window.history_list.invisibleRootItem()
359 childcount = root.childCount()
360 for i in range(childcount):
363 tx_info = tx_list[str(item.data(0, Qt.UserRole).toPyObject())]
365 newtx = self.wallet.get_tx_history()
366 v = newtx[[x[0] for x in newtx].index(str(item.data(0, Qt.UserRole).toPyObject()))][3]
368 tx_info = {'timestamp':int(datetime.datetime.now().strftime("%s")), 'value': v }
370 tx_time = int(tx_info['timestamp'])
371 tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d')
373 tx_USD_val = "%.2f %s" % (Decimal(tx_info['value']) / 100000000 * Decimal(resp_hist['bpi'][tx_time_str]), "USD")
375 tx_USD_val = "%.2f %s" % (self.btc_rate * Decimal(tx_info['value'])/100000000 , "USD")
377 item.setText(5, tx_USD_val)
378 if Decimal(tx_info['value']) < 0:
379 item.setForeground(5, QBrush(QColor("#BC1E1E")))
381 for i, width in enumerate(self.gui.main_window.column_widths['history']):
382 self.gui.main_window.history_list.setColumnWidth(i, width)
383 self.gui.main_window.history_list.setColumnWidth(4, 140)
384 self.gui.main_window.history_list.setColumnWidth(5, 120)
385 self.gui.main_window.is_edit = False
388 def settings_widget(self, window):
389 return EnterButton(_('Settings'), self.settings_dialog)
391 def settings_dialog(self):
393 layout = QGridLayout(d)
394 layout.addWidget(QLabel(_('Exchange rate API: ')), 0, 0)
395 layout.addWidget(QLabel(_('Currency: ')), 1, 0)
396 layout.addWidget(QLabel(_('History Rates: ')), 2, 0)
398 combo_ex = QComboBox()
399 hist_checkbox = QCheckBox()
400 hist_checkbox.setEnabled(False)
401 if self.config.get('history_rates', 'unchecked') == 'unchecked':
402 hist_checkbox.setChecked(False)
404 hist_checkbox.setChecked(True)
405 ok_button = QPushButton(_("OK"))
409 cur_request = str(self.currencies[x])
412 if cur_request != self.config.get('currency', "EUR"):
413 self.config.set_key('currency', cur_request, True)
414 if cur_request == "USD" and self.config.get('use_exchange', "Blockchain") == "CoinDesk":
415 hist_checkbox.setEnabled(True)
417 hist_checkbox.setChecked(False)
418 hist_checkbox.setEnabled(False)
419 self.win.update_status()
422 hist_checkbox.setChecked(False)
423 hist_checkbox.setEnabled(False)
426 cur_request = str(self.exchanges[x])
427 if cur_request != self.config.get('use_exchange', "Blockchain"):
428 self.config.set_key('use_exchange', cur_request, True)
431 self.exchanger.query_rates.set()
432 if cur_request == "CoinDesk":
433 if self.config.get('currency', "EUR") == "USD":
434 hist_checkbox.setEnabled(True)
439 set_currencies(combo)
440 self.win.update_status()
442 def on_change_hist(checked):
444 self.config.set_key('history_rates', 'checked')
445 self.history_tab_update()
447 self.config.set_key('history_rates', 'unchecked')
448 self.gui.main_window.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance')] )
449 self.gui.main_window.history_list.setColumnCount(5)
450 for i,width in enumerate(self.gui.main_window.column_widths['history']):
451 self.gui.main_window.history_list.setColumnWidth(i, width)
453 def set_hist_check(hist_checkbox):
454 if self.config.get('use_exchange', "Blockchain") == "CoinDesk":
455 hist_checkbox.setEnabled(True)
457 hist_checkbox.setEnabled(False)
459 def set_currencies(combo):
460 current_currency = self.config.get('currency', "EUR")
465 combo.addItems(self.currencies)
467 index = self.currencies.index(current_currency)
470 combo.setCurrentIndex(index)
472 def set_exchanges(combo_ex):
477 combo_ex.addItems(self.exchanges)
479 index = self.exchanges.index(self.config.get('use_exchange', "Blockchain"))
482 combo_ex.setCurrentIndex(index)
487 set_exchanges(combo_ex)
488 set_currencies(combo)
489 set_hist_check(hist_checkbox)
490 combo.currentIndexChanged.connect(on_change)
491 combo_ex.currentIndexChanged.connect(on_change_ex)
492 hist_checkbox.stateChanged.connect(on_change_hist)
493 combo.connect(self.win, SIGNAL('refresh_currencies_combo()'), lambda: set_currencies(combo))
494 combo_ex.connect(d, SIGNAL('refresh_exchanges_combo()'), lambda: set_exchanges(combo_ex))
495 ok_button.clicked.connect(lambda: ok_clicked())
496 layout.addWidget(combo,1,1)
497 layout.addWidget(combo_ex,0,1)
498 layout.addWidget(hist_checkbox,2,1)
499 layout.addWidget(ok_button,3,1)