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",
27 class Exchanger(threading.Thread):
29 def __init__(self, parent):
30 threading.Thread.__init__(self)
33 self.quote_currencies = None
34 self.lock = threading.Lock()
35 self.use_exchange = self.parent.config.get('use_exchange', "Blockchain")
36 self.parent.exchanges = EXCHANGES
37 self.parent.currencies = ["EUR","GBP","USD"]
38 self.parent.win.emit(SIGNAL("refresh_exchanges_combo()"))
39 self.parent.win.emit(SIGNAL("refresh_currencies_combo()"))
40 self.is_running = False
42 def get_json(self, site, get_string):
44 connection = httplib.HTTPSConnection(site)
45 connection.request("GET", get_string)
48 resp = connection.getresponse()
49 if resp.reason == httplib.responses[httplib.NOT_FOUND]:
52 json_resp = json.loads(resp.read())
58 def exchange(self, btc_amount, quote_currency):
60 if self.quote_currencies is None:
62 quote_currencies = self.quote_currencies.copy()
63 if quote_currency not in quote_currencies:
65 if self.use_exchange == "CoinDesk":
67 resp_rate = self.get_json('api.coindesk.com', "/v1/bpi/currentprice/" + str(quote_currency) + ".json")
70 return btc_amount * decimal.Decimal(str(resp_rate["bpi"][str(quote_currency)]["rate_float"]))
71 return btc_amount * decimal.Decimal(quote_currencies[quote_currency])
74 self.is_running = False
76 def update_rate(self):
77 self.use_exchange = self.parent.config.get('use_exchange', "Blockchain")
79 "BitcoinAverage": self.update_ba,
80 "BitPay": self.update_bp,
81 "Blockchain": self.update_bc,
82 "BTCChina": self.update_CNY,
83 "CaVirtEx": self.update_cv,
84 "CoinDesk": self.update_cd,
85 "Coinbase": self.update_cb,
86 "LocalBitcoins": self.update_lb,
87 "Winkdex": self.update_wd,
90 update_rates[self.use_exchange]()
95 self.is_running = True
96 while self.is_running:
103 resp_currencies = self.get_json('api.coindesk.com', "/v1/bpi/supported-currencies.json")
107 quote_currencies = {}
108 for cur in resp_currencies:
109 quote_currencies[str(cur["currency"])] = 0.0
111 self.quote_currencies = quote_currencies
112 self.parent.set_currencies(quote_currencies)
116 winkresp = self.get_json('winkdex.com', "/static/data/0_600_288.json")
117 ####could need nonce value in GET, no Docs available
120 quote_currencies = {"USD": 0.0}
121 ####get y of highest x in "prices"
122 lenprices = len(winkresp["prices"])
123 usdprice = winkresp["prices"][lenprices-1]["y"]
125 quote_currencies["USD"] = decimal.Decimal(usdprice)
127 self.quote_currencies = quote_currencies
130 self.parent.set_currencies(quote_currencies)
134 jsonresp = self.get_json('www.cavirtex.com', "/api/CAD/ticker.json")
137 quote_currencies = {"CAD": 0.0}
138 cadprice = jsonresp["last"]
140 quote_currencies["CAD"] = decimal.Decimal(cadprice)
142 self.quote_currencies = quote_currencies
145 self.parent.set_currencies(quote_currencies)
147 def update_CNY(self):
149 jsonresp = self.get_json('data.btcchina.com', "/data/ticker")
152 quote_currencies = {"CNY": 0.0}
153 cnyprice = jsonresp["ticker"]["last"]
155 quote_currencies["CNY"] = decimal.Decimal(cnyprice)
157 self.quote_currencies = quote_currencies
160 self.parent.set_currencies(quote_currencies)
164 jsonresp = self.get_json('bitpay.com', "/api/rates")
167 quote_currencies = {}
170 quote_currencies[str(r["code"])] = decimal.Decimal(r["rate"])
172 self.quote_currencies = quote_currencies
175 self.parent.set_currencies(quote_currencies)
179 jsonresp = self.get_json('coinbase.com', "/api/v1/currencies/exchange_rates")
183 quote_currencies = {}
186 if r[:7] == "btc_to_":
187 quote_currencies[r[7:].upper()] = self._lookup_rate_cb(jsonresp, r)
189 self.quote_currencies = quote_currencies
192 self.parent.set_currencies(quote_currencies)
197 jsonresp = self.get_json('blockchain.info', "/ticker")
200 quote_currencies = {}
203 quote_currencies[r] = self._lookup_rate(jsonresp, r)
205 self.quote_currencies = quote_currencies
208 self.parent.set_currencies(quote_currencies)
209 # print "updating exchange rate", self.quote_currencies["USD"]
213 jsonresp = self.get_json('localbitcoins.com', "/bitcoinaverage/ticker-all-currencies/")
216 quote_currencies = {}
219 quote_currencies[r] = self._lookup_rate_lb(jsonresp, r)
221 self.quote_currencies = quote_currencies
224 self.parent.set_currencies(quote_currencies)
229 jsonresp = self.get_json('api.bitcoinaverage.com', "/ticker/global/all")
232 quote_currencies = {}
235 if not r == "timestamp":
236 quote_currencies[r] = self._lookup_rate_ba(jsonresp, r)
238 self.quote_currencies = quote_currencies
241 self.parent.set_currencies(quote_currencies)
244 def get_currencies(self):
245 return [] if self.quote_currencies == None else sorted(self.quote_currencies.keys())
247 def _lookup_rate(self, response, quote_id):
248 return decimal.Decimal(str(response[str(quote_id)]["15m"]))
249 def _lookup_rate_cb(self, response, quote_id):
250 return decimal.Decimal(str(response[str(quote_id)]))
251 def _lookup_rate_ba(self, response, quote_id):
252 return decimal.Decimal(response[str(quote_id)]["last"])
253 def _lookup_rate_lb(self, response, quote_id):
254 return decimal.Decimal(response[str(quote_id)]["rates"]["last"])
257 class Plugin(BasePlugin):
260 return "Exchange rates"
262 def description(self):
263 return """exchange rates, retrieved from blockchain.info, CoinDesk, or Coinbase"""
266 def __init__(self,a,b):
267 BasePlugin.__init__(self,a,b)
268 self.currencies = [self.config.get('currency', "EUR")]
269 self.exchanges = [self.config.get('use_exchange', "Blockchain")]
272 self.win = self.gui.main_window
273 self.win.connect(self.win, SIGNAL("refresh_currencies()"), self.win.update_status)
275 self.exchanger = Exchanger(self)
276 self.exchanger.start()
277 self.gui.exchanger = self.exchanger #
279 def set_currencies(self, currency_options):
280 self.currencies = sorted(currency_options)
281 self.win.emit(SIGNAL("refresh_currencies()"))
282 self.win.emit(SIGNAL("refresh_currencies_combo()"))
285 def set_quote_text(self, btc_balance, r):
286 r[0] = self.create_quote_text(Decimal(btc_balance) / 100000000)
288 def create_quote_text(self, btc_balance):
289 quote_currency = self.config.get("currency", "EUR")
290 self.exchanger.use_exchange = self.config.get("use_exchange", "Blockchain")
291 quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
292 if quote_balance is None:
295 quote_text = "%.2f %s" % (quote_balance, quote_currency)
298 def load_wallet(self, wallet):
301 for item in self.wallet.get_tx_history(self.wallet.storage.get("current_account", None)):
302 tx_hash, conf, is_mine, value, fee, balance, timestamp = item
303 tx_list[tx_hash] = {'value': value, 'timestamp': timestamp, 'balance': balance}
305 self.tx_list = tx_list
308 def requires_settings(self):
313 out = BasePlugin.toggle(self)
314 self.win.update_status()
319 self.exchanger.stop()
321 def history_tab_update(self):
322 if self.config.get('history_rates', 'unchecked') == "checked":
323 tx_list = self.tx_list
325 mintimestr = datetime.datetime.fromtimestamp(int(min(tx_list.items(), key=lambda x: x[1]['timestamp'])[1]['timestamp'])).strftime('%Y-%m-%d')
326 maxtimestr = datetime.datetime.now().strftime('%Y-%m-%d')
328 resp_hist = self.exchanger.get_json('api.coindesk.com', "/v1/bpi/historical/close.json?start=" + mintimestr + "&end=" + maxtimestr)
332 self.gui.main_window.is_edit = True
333 self.gui.main_window.history_list.setColumnCount(6)
334 self.gui.main_window.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance'), _('Fiat Amount')] )
335 root = self.gui.main_window.history_list.invisibleRootItem()
336 childcount = root.childCount()
337 for i in range(childcount):
340 tx_info = tx_list[str(item.data(0, Qt.UserRole).toPyObject())]
342 newtx = self.wallet.get_tx_history()
343 v = newtx[[x[0] for x in newtx].index(str(item.data(0, Qt.UserRole).toPyObject()))][3]
345 tx_info = {'timestamp':int(datetime.datetime.now().strftime("%s")), 'value': v }
347 tx_time = int(tx_info['timestamp'])
348 tx_time_str = datetime.datetime.fromtimestamp(tx_time).strftime('%Y-%m-%d')
349 tx_USD_val = "%.2f %s" % (Decimal(tx_info['value']) / 100000000 * Decimal(resp_hist['bpi'][tx_time_str]), "USD")
351 item.setText(5, tx_USD_val)
352 if Decimal(tx_info['value']) < 0:
353 item.setForeground(5, QBrush(QColor("#BC1E1E")))
355 for i, width in enumerate(self.gui.main_window.column_widths['history']):
356 self.gui.main_window.history_list.setColumnWidth(i, width)
357 self.gui.main_window.history_list.setColumnWidth(4, 140)
358 self.gui.main_window.history_list.setColumnWidth(5, 120)
359 self.gui.main_window.is_edit = False
362 def settings_widget(self, window):
363 return EnterButton(_('Settings'), self.settings_dialog)
365 def settings_dialog(self):
367 layout = QGridLayout(d)
368 layout.addWidget(QLabel(_('Exchange rate API: ')), 0, 0)
369 layout.addWidget(QLabel(_('Currency: ')), 1, 0)
370 layout.addWidget(QLabel(_('History Rates: ')), 2, 0)
372 combo_ex = QComboBox()
373 hist_checkbox = QCheckBox()
374 hist_checkbox.setEnabled(False)
375 if self.config.get('history_rates', 'unchecked') == 'unchecked':
376 hist_checkbox.setChecked(False)
378 hist_checkbox.setChecked(True)
379 ok_button = QPushButton(_("OK"))
382 cur_request = str(self.currencies[x])
383 if cur_request != self.config.get('currency', "EUR"):
384 self.config.set_key('currency', cur_request, True)
385 if cur_request == "USD" and self.config.get('use_exchange', "Blockchain") == "CoinDesk":
386 hist_checkbox.setEnabled(True)
388 hist_checkbox.setChecked(False)
389 hist_checkbox.setEnabled(False)
390 self.win.update_status()
393 hist_checkbox.setChecked(False)
394 hist_checkbox.setEnabled(False)
397 cur_request = str(self.exchanges[x])
398 if cur_request != self.config.get('use_exchange', "Blockchain"):
399 self.config.set_key('use_exchange', cur_request, True)
400 self.exchanger.update_rate()
401 if cur_request == "CoinDesk":
402 if self.config.get('currency', "EUR") == "USD":
403 hist_checkbox.setEnabled(True)
408 set_currencies(combo)
409 self.win.update_status()
411 def on_change_hist(checked):
413 self.config.set_key('history_rates', 'checked')
414 self.history_tab_update()
416 self.config.set_key('history_rates', 'unchecked')
417 self.gui.main_window.history_list.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance')] )
418 self.gui.main_window.history_list.setColumnCount(5)
419 for i,width in enumerate(self.gui.main_window.column_widths['history']):
420 self.gui.main_window.history_list.setColumnWidth(i, width)
422 def set_hist_check(hist_checkbox):
423 if self.config.get('use_exchange', "Blockchain") == "CoinDesk":
424 hist_checkbox.setEnabled(True)
426 hist_checkbox.setEnabled(False)
428 def set_currencies(combo):
429 current_currency = self.config.get('currency', "EUR")
434 combo.addItems(self.currencies)
436 index = self.currencies.index(current_currency)
439 combo.setCurrentIndex(index)
441 def set_exchanges(combo_ex):
446 combo_ex.addItems(self.exchanges)
448 index = self.exchanges.index(self.config.get('use_exchange', "Blockchain"))
451 combo_ex.setCurrentIndex(index)
456 set_exchanges(combo_ex)
457 set_currencies(combo)
458 set_hist_check(hist_checkbox)
459 combo.currentIndexChanged.connect(on_change)
460 combo_ex.currentIndexChanged.connect(on_change_ex)
461 hist_checkbox.stateChanged.connect(on_change_hist)
462 combo.connect(d, SIGNAL('refresh_currencies_combo()'), lambda: set_currencies(combo))
463 combo_ex.connect(d, SIGNAL('refresh_exchanges_combo()'), lambda: set_exchanges(combo_ex))
464 ok_button.clicked.connect(lambda: ok_clicked())
465 layout.addWidget(combo,1,1)
466 layout.addWidget(combo_ex,0,1)
467 layout.addWidget(hist_checkbox,2,1)
468 layout.addWidget(ok_button,3,1)