Add Coinbase exchange rate option
[electrum-nvc.git] / plugins / exchange_rate.py
1 from PyQt4.QtGui import *
2 from PyQt4.QtCore import *
3
4 import decimal
5 import httplib
6 import json
7 import threading
8 import re
9 from decimal import Decimal
10 from electrum.plugins import BasePlugin
11 from electrum.i18n import _
12 from electrum_gui.qt.util import *
13
14
15 class Exchanger(threading.Thread):
16
17     def __init__(self, parent):
18         threading.Thread.__init__(self)
19         self.daemon = True
20         self.parent = parent
21         self.quote_currencies = None
22         self.lock = threading.Lock()
23         self.use_exchange = self.parent.config.get('use_exchange', "Blockchain")
24         self.parent.exchanges = ["CoinDesk", "Blockchain", "Coinbase"]
25         self.parent.currencies = ["EUR","GBP","USD"]
26         self.parent.win.emit(SIGNAL("refresh_exchanges_combo()"))
27         self.parent.win.emit(SIGNAL("refresh_currencies_combo()"))
28         self.is_running = False
29
30     def exchange(self, btc_amount, quote_currency):
31         with self.lock:
32             if self.quote_currencies is None:
33                 return None
34             quote_currencies = self.quote_currencies.copy()
35         if quote_currency not in quote_currencies:
36             return None
37         if self.use_exchange == "CoinDesk":
38             try:
39                 connection = httplib.HTTPSConnection('api.coindesk.com')
40                 connection.request("GET", "/v1/bpi/currentprice/" + str(quote_currency) + ".json")
41             except Exception:
42                 return
43             resp = connection.getresponse()
44             if resp.reason == httplib.responses[httplib.NOT_FOUND]:
45                 return
46             try:
47                 resp_rate = json.loads(resp.read())
48             except Exception:
49                 return
50             return btc_amount * decimal.Decimal(str(resp_rate["bpi"][str(quote_currency)]["rate_float"]))
51         return btc_amount * decimal.Decimal(quote_currencies[quote_currency])
52
53     def stop(self):
54         self.is_running = False
55
56     def run(self):
57         self.is_running = True
58         while self.is_running:
59             self.use_exchange = self.parent.config.get('use_exchange', "Blockchain")
60             if self.use_exchange == "Blockchain":
61                 self.update_bc()
62             elif self.use_exchange == "CoinDesk":
63                 self.update_cd()
64             elif self.use_exchange == "Coinbase":
65                 self.update_cb()
66             time.sleep(150)
67
68
69     def update_cd(self):
70         try:
71             connection = httplib.HTTPSConnection('api.coindesk.com')
72             connection.request("GET", "/v1/bpi/supported-currencies.json")
73         except Exception:
74             return
75         response = connection.getresponse()
76         if response.reason == httplib.responses[httplib.NOT_FOUND]:
77             return
78         try:
79             resp_currencies = json.loads(response.read())
80         except Exception:
81             return
82
83         quote_currencies = {}
84         for cur in resp_currencies:
85             quote_currencies[str(cur["currency"])] = 0.0
86         with self.lock:
87             self.quote_currencies = quote_currencies
88         self.parent.set_currencies(quote_currencies)
89
90     def update_cb(self):
91         try:
92             connection = httplib.HTTPSConnection('coinbase.com')
93             connection.request("GET", "/api/v1/currencies/exchange_rates")
94         except Exception:
95             return
96         response = connection.getresponse()
97         if response.reason == httplib.responses[httplib.NOT_FOUND]:
98             return
99
100         try:
101             response = json.loads(response.read())
102         except Exception:
103             return
104
105         quote_currencies = {}
106         try:
107             for r in response:
108                 if r[:7] == "btc_to_":
109                     quote_currencies[r[7:].upper()] = self._lookup_rate_cb(response, r)
110             with self.lock:
111                 self.quote_currencies = quote_currencies
112         except KeyError:
113             pass
114         self.parent.set_currencies(quote_currencies)
115
116
117     def update_bc(self):
118         try:
119             connection = httplib.HTTPSConnection('blockchain.info')
120             connection.request("GET", "/ticker")
121         except Exception:
122             return
123         response = connection.getresponse()
124         if response.reason == httplib.responses[httplib.NOT_FOUND]:
125             return
126         try:
127             response = json.loads(response.read())
128         except Exception:
129             return
130         quote_currencies = {}
131         try:
132             for r in response:
133                 quote_currencies[r] = self._lookup_rate(response, r)
134             with self.lock:
135                 self.quote_currencies = quote_currencies
136         except KeyError:
137             pass
138         self.parent.set_currencies(quote_currencies)
139         # print "updating exchange rate", self.quote_currencies["USD"]
140
141             
142     def get_currencies(self):
143         return [] if self.quote_currencies == None else sorted(self.quote_currencies.keys())
144
145     def _lookup_rate(self, response, quote_id):
146         return decimal.Decimal(str(response[str(quote_id)]["15m"]))
147     def _lookup_rate_cb(self, response, quote_id):
148         return decimal.Decimal(str(response[str(quote_id)]))
149
150
151
152 class Plugin(BasePlugin):
153
154     def fullname(self):
155         return "Exchange rates"
156
157     def description(self):
158         return """exchange rates, retrieved from blockchain.info"""
159
160
161     def __init__(self,a,b):
162         BasePlugin.__init__(self,a,b)
163         self.currencies = [self.config.get('currency', "EUR")]
164         self.exchanges = [self.config.get('use_exchange', "Blockchain")]
165
166     def init(self):
167         self.win = self.gui.main_window
168         self.win.connect(self.win, SIGNAL("refresh_currencies()"), self.win.update_status)
169         # Do price discovery
170         self.exchanger = Exchanger(self)
171         self.exchanger.start()
172         self.gui.exchanger = self.exchanger #
173
174     def set_currencies(self, currency_options):
175         self.currencies = sorted(currency_options)
176         self.win.emit(SIGNAL("refresh_currencies()"))
177         self.win.emit(SIGNAL("refresh_currencies_combo()"))
178
179
180     def set_quote_text(self, btc_balance, r):
181         r[0] = self.create_quote_text(Decimal(btc_balance) / 100000000)
182
183     def create_quote_text(self, btc_balance):
184         quote_currency = self.config.get("currency", "EUR")
185         self.exchanger.use_exchange = self.config.get("use_exchange", "Blockchain")
186         quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
187         if quote_balance is None:
188             quote_text = ""
189         else:
190             quote_text = "%.2f %s" % (quote_balance, quote_currency)
191         return quote_text
192
193
194     def requires_settings(self):
195         return True
196
197
198     def toggle(self):
199         out = BasePlugin.toggle(self)
200         self.win.update_status()
201         return out
202
203
204     def close(self):
205         self.exchanger.stop()
206
207
208     def settings_widget(self, window):
209         return EnterButton(_('Settings'), self.settings_dialog)
210
211     def settings_dialog(self):
212         d = QDialog()
213         layout = QGridLayout(d)
214         layout.addWidget(QLabel(_('Exchange rate API: ')), 0, 0)
215         layout.addWidget(QLabel(_('Currency: ')), 1, 0)
216         combo = QComboBox()
217         combo_ex = QComboBox()
218         ok_button = QPushButton(_("OK"))
219
220         def on_change(x):
221             cur_request = str(self.currencies[x])
222             if cur_request != self.config.get('currency', "EUR"):
223                 self.config.set_key('currency', cur_request, True)
224                 self.win.update_status()
225
226         def on_change_ex(x):
227             cur_request = str(self.exchanges[x])
228             if cur_request != self.config.get('use_exchange', "Blockchain"):
229                 self.config.set_key('use_exchange', cur_request, True)
230                 self.win.update_status()
231                 if cur_request == "Blockchain":
232                     self.exchanger.update_bc()
233                 elif cur_request == "CoinDesk":
234                     self.exchanger.update_cd()
235                 elif cur_request == "Coinbase":
236                     self.exchanger.update_cb()
237                 set_currencies(combo)
238
239         def set_currencies(combo):
240             current_currency = self.config.get('currency', "EUR")
241             try:
242                 combo.clear()
243             except Exception:
244                 return
245             combo.addItems(self.currencies)
246             try:
247                 index = self.currencies.index(current_currency)
248             except Exception:
249                 index = 0
250             combo.setCurrentIndex(index)
251
252         def set_exchanges(combo_ex):
253             try:
254                 combo_ex.clear()
255             except Exception:
256                 return
257             combo_ex.addItems(self.exchanges)
258             try:
259                 index = self.exchanges.index(self.config.get('use_exchange', "Blockchain"))
260             except Exception:
261                 index = 0
262             combo_ex.setCurrentIndex(index)
263
264         def ok_clicked():
265             d.accept();
266
267         set_exchanges(combo_ex)
268         set_currencies(combo)
269         combo.currentIndexChanged.connect(on_change)
270         combo_ex.currentIndexChanged.connect(on_change_ex)
271         combo.connect(d, SIGNAL('refresh_currencies_combo()'), lambda: set_currencies(combo))
272         combo_ex.connect(d, SIGNAL('refresh_exchanges_combo()'), lambda: set_exchanges(combo_ex))
273         ok_button.clicked.connect(lambda: ok_clicked())
274         layout.addWidget(combo,1,1)
275         layout.addWidget(combo_ex,0,1)
276         layout.addWidget(ok_button,2,1)
277         
278         if d.exec_():
279             return True
280         else:
281             return False
282
283
284