Add CoinDesk BPI for exchange rate
[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', "CoinDesk")
24         self.parent.exchanges = ["CoinDesk", "Blockchain"]
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             time.sleep(150)
65
66     def update_cd(self):
67         try:
68             connection = httplib.HTTPSConnection('api.coindesk.com')
69             connection.request("GET", "/v1/bpi/supported-currencies.json")
70         except Exception:
71             return
72         response = connection.getresponse()
73         if response.reason == httplib.responses[httplib.NOT_FOUND]:
74             return
75         try:
76             resp_currencies = json.loads(response.read())
77         except Exception:
78             return
79
80         quote_currencies = {}
81         for cur in resp_currencies:
82             quote_currencies[str(cur["currency"])] = 0.0
83         with self.lock:
84             self.quote_currencies = quote_currencies
85         self.parent.set_currencies(quote_currencies)
86
87
88     def update_bc(self):
89         try:
90             connection = httplib.HTTPSConnection('blockchain.info')
91             connection.request("GET", "/ticker")
92         except Exception:
93             return
94         response = connection.getresponse()
95         if response.reason == httplib.responses[httplib.NOT_FOUND]:
96             return
97         try:
98             response = json.loads(response.read())
99         except Exception:
100             return
101         quote_currencies = {}
102         try:
103             for r in response:
104                 quote_currencies[r] = self._lookup_rate(response, r)
105             with self.lock:
106                 self.quote_currencies = quote_currencies
107         except KeyError:
108             pass
109         self.parent.set_currencies(quote_currencies)
110         # print "updating exchange rate", self.quote_currencies["USD"]
111
112             
113     def get_currencies(self):
114         return [] if self.quote_currencies == None else sorted(self.quote_currencies.keys())
115
116     def _lookup_rate(self, response, quote_id):
117         return decimal.Decimal(str(response[str(quote_id)]["15m"]))
118
119
120 class Plugin(BasePlugin):
121
122     def fullname(self):
123         return "Exchange rates"
124
125     def description(self):
126         return """exchange rates, retrieved from blockchain.info"""
127
128
129     def __init__(self,a,b):
130         BasePlugin.__init__(self,a,b)
131         self.currencies = [self.config.get('currency', "EUR")]
132         self.exchanges = [self.config.get('use_exchange', "CoinDesk")]
133
134     def init(self):
135         self.win = self.gui.main_window
136         self.win.connect(self.win, SIGNAL("refresh_currencies()"), self.win.update_status)
137         # Do price discovery
138         self.exchanger = Exchanger(self)
139         self.exchanger.start()
140         self.gui.exchanger = self.exchanger #
141
142     def set_currencies(self, currency_options):
143         self.currencies = sorted(currency_options)
144         self.win.emit(SIGNAL("refresh_currencies()"))
145         self.win.emit(SIGNAL("refresh_currencies_combo()"))
146
147
148     def set_quote_text(self, btc_balance, r):
149         r[0] = self.create_quote_text(Decimal(btc_balance) / 100000000)
150
151     def create_quote_text(self, btc_balance):
152         quote_currency = self.config.get("currency", "EUR")
153         self.exchanger.use_exchange = self.config.get("use_exchange", "Blockchain")
154         quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
155         if quote_balance is None:
156             quote_text = ""
157         else:
158             quote_text = "%.2f %s" % (quote_balance, quote_currency)
159         return quote_text
160
161
162     def requires_settings(self):
163         return True
164
165
166     def toggle(self):
167         out = BasePlugin.toggle(self)
168         self.win.update_status()
169         return out
170
171
172     def close(self):
173         self.exchanger.stop()
174
175
176     def settings_widget(self, window):
177         return EnterButton(_('Settings'), self.settings_dialog)
178
179     def settings_dialog(self):
180         d = QDialog()
181         layout = QGridLayout(d)
182         layout.addWidget(QLabel("Exchange rate API: "), 0, 0)
183         layout.addWidget(QLabel("Currency: "), 1, 0)
184         combo = QComboBox()
185         combo_ex = QComboBox()
186
187         def on_change(x):
188             cur_request = str(self.currencies[x])
189             if cur_request != self.config.get('currency', "EUR"):
190                 self.config.set_key('currency', cur_request, True)
191                 self.win.update_status()
192
193         def on_change_ex(x):
194             cur_request = str(self.exchanges[x])
195             if cur_request != self.config.get('use_exchange', "CoinDesk"):
196                 self.config.set_key('use_exchange', cur_request, True)
197                 self.win.update_status()
198
199         def set_currencies(combo):
200             try:
201                 combo.clear()
202             except Exception:
203                 return
204             combo.addItems(self.currencies)
205             try:
206                 index = self.currencies.index(self.config.get('currency', "EUR"))
207             except Exception:
208                 index = 0
209             combo.setCurrentIndex(index)
210
211         def set_exchanges(combo_ex):
212             try:
213                 combo_ex.clear()
214             except Exception:
215                 return
216             combo_ex.addItems(self.exchanges)
217             try:
218                 index = self.exchanges.index(self.config.get('use_exchange', "Blockchain"))
219             except Exception:
220                 index = 0
221             combo_ex.setCurrentIndex(index)
222
223         set_exchanges(combo_ex)
224         set_currencies(combo)
225         combo.currentIndexChanged.connect(on_change)
226         combo_ex.currentIndexChanged.connect(on_change_ex)
227         combo.connect(d, SIGNAL('refresh_currencies_combo()'), lambda: set_currencies(combo))
228         combo_ex.connect(d, SIGNAL('refresh_exchanges_combo()'), lambda: set_exchanges(combo_ex))
229         layout.addWidget(combo,1,1)
230         layout.addWidget(combo_ex,0,1)
231         
232         if d.exec_():
233             return True
234         else:
235             return False
236
237
238