datamaxi 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
datamaxi/__init__.py ADDED
File without changes
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
datamaxi/api.py ADDED
@@ -0,0 +1,144 @@
1
+ import os
2
+ import json
3
+ from json import JSONDecodeError
4
+ import logging
5
+ import requests
6
+ from .__version__ import __version__
7
+ from datamaxi.error import ClientError, ServerError
8
+ from datamaxi.lib.utils import cleanNoneValue
9
+ from datamaxi.lib.utils import encoded_string
10
+
11
+
12
+ class API(object):
13
+ """The base class for all DataMaxi+ Python clients. `api_key` can be set
14
+ as an environment variable `NEVEREST_API_KEY`.
15
+ """
16
+
17
+ def __init__(
18
+ self,
19
+ api_key=None,
20
+ base_url=None,
21
+ timeout=None,
22
+ proxies=None,
23
+ show_limit_usage=False,
24
+ show_header=False,
25
+ ):
26
+ """Client API constructor. `api_key` can be set
27
+ as an environment variable `NEVEREST_API_KEY`.
28
+
29
+ Args:
30
+ api_key (str): The API key for the DataMaxi+ API.
31
+ base_url (str): The base URL for the DataMaxi+ API.
32
+ timeout (int): The timeout for the requests.
33
+ proxies (dict): The proxies for the requests.
34
+ show_limit_usage (bool): Show the limit usage.
35
+ show_header (bool): Show the header.
36
+ """
37
+ self.api_key = api_key or os.environ.get("NEVEREST_API_KEY")
38
+ self.base_url = base_url
39
+ self.timeout = timeout
40
+ self.proxies = None
41
+ self.show_limit_usage = False
42
+ self.show_header = False
43
+
44
+ self.session = requests.Session()
45
+ self.session.headers.update(
46
+ {
47
+ "Content-Type": "application/json;charset=utf-8",
48
+ "User-Agent": "datamaxi/" + __version__,
49
+ "Authorization": "X-NVRST-APIKEY " + str(self.api_key),
50
+ }
51
+ )
52
+
53
+ if show_limit_usage is True:
54
+ self.show_limit_usage = True
55
+
56
+ if show_header is True:
57
+ self.show_header = True
58
+
59
+ if type(proxies) is dict:
60
+ self.proxies = proxies
61
+
62
+ self._logger = logging.getLogger(__name__)
63
+ return
64
+
65
+ def query(self, url_path, payload=None):
66
+ return self.send_request("GET", url_path, payload=payload)
67
+
68
+ def send_request(self, http_method, url_path, payload=None):
69
+ if payload is None:
70
+ payload = {}
71
+ url = self.base_url + url_path
72
+ self._logger.debug("url: " + url)
73
+ params = cleanNoneValue(
74
+ {
75
+ "url": url,
76
+ "params": self._prepare_params(payload),
77
+ "timeout": self.timeout,
78
+ "proxies": self.proxies,
79
+ }
80
+ )
81
+ response = self._dispatch_request(http_method)(**params)
82
+ self._logger.debug("raw response from server:" + response.text)
83
+ self._handle_exception(response)
84
+
85
+ try:
86
+ data = response.json()
87
+ except ValueError:
88
+ data = response.text
89
+ result = {}
90
+
91
+ if self.show_limit_usage:
92
+ limit_usage = {}
93
+ for key in response.headers.keys():
94
+ key = key.lower()
95
+ if (
96
+ key.startswith("x-ratelimit-limit")
97
+ or key.startswith("x-ratelimit-remaining")
98
+ or key.startswith("x-ratelimit-reset")
99
+ ):
100
+ limit_usage[key] = response.headers[key]
101
+ result["limit_usage"] = limit_usage
102
+
103
+ if self.show_header:
104
+ result["header"] = response.headers
105
+
106
+ if len(result) != 0:
107
+ result["data"] = data
108
+ return result
109
+
110
+ return data
111
+
112
+ def _prepare_params(self, params):
113
+ return encoded_string(cleanNoneValue(params))
114
+
115
+ def _dispatch_request(self, http_method):
116
+ return {
117
+ "GET": self.session.get,
118
+ "DELETE": self.session.delete,
119
+ "PUT": self.session.put,
120
+ "POST": self.session.post,
121
+ }.get(http_method, "GET")
122
+
123
+ def _handle_exception(self, response):
124
+ status_code = response.status_code
125
+ if status_code < 400:
126
+ return
127
+ if 400 <= status_code < 500:
128
+ try:
129
+ err = json.loads(response.text)
130
+ except JSONDecodeError:
131
+ raise ClientError(
132
+ status_code, None, response.text, None, response.headers
133
+ )
134
+ error_data = None
135
+ if "data" in err:
136
+ error_data = err["data"]
137
+ raise ClientError(
138
+ status_code,
139
+ err["code"],
140
+ err["msg"],
141
+ response.headers,
142
+ error_data,
143
+ )
144
+ raise ServerError(status_code, response.text)
@@ -0,0 +1,56 @@
1
+ from typing import Any, List, Union
2
+ import pandas as pd
3
+ from datamaxi.api import API
4
+ from datamaxi.lib.utils import check_required_parameters
5
+ from datamaxi.lib.utils import postprocess
6
+ from datamaxi.lib.constants import BASE_URL
7
+
8
+
9
+ class Binance(API):
10
+ """Client to fetch Binance data from DataMaxi+ API."""
11
+
12
+ def __init__(self, api_key=None, **kwargs: Any):
13
+ """Initialize the object.
14
+
15
+ Args:
16
+ api_key (str): The DataMaxi+ API key
17
+ **kwargs: Keyword arguments used by `datamaxi.api.API`.
18
+ """
19
+ if "base_url" not in kwargs:
20
+ kwargs["base_url"] = BASE_URL
21
+ super().__init__(api_key, **kwargs)
22
+
23
+ def symbols(self) -> List[str]:
24
+ """Supported Binance supported symbols
25
+
26
+ `GET /v1/raw/binance/symbols`
27
+
28
+ <https://docs.neverest.finance/cex/binance/symbols>
29
+
30
+ Returns:
31
+ List of supported Binance symbols
32
+ """
33
+ url_path = "/v1/raw/binance/symbols"
34
+ return self.query(url_path)
35
+
36
+ @postprocess()
37
+ def kline(
38
+ self, symbol: str, interval: str = "1d", pandas: bool = True
39
+ ) -> Union[List, pd.DataFrame]:
40
+ """Get Binance k-line data
41
+
42
+ `GET /v1/raw/binance/kline`
43
+
44
+ <https://docs.neverest.finance/cex/binance/kline>
45
+
46
+ Args:
47
+ symbol (str): Binance symbol
48
+ interval (str): Kline interval
49
+ pandas (bool): Return data as pandas DataFrame
50
+
51
+ Returns:
52
+ Binance kline data for a given symbol and interval in pandas DataFrame
53
+ """
54
+ check_required_parameters([[symbol, "symbol"], [interval, "interval"]])
55
+ params = {"symbol": symbol, "interval": interval}
56
+ return self.query("/v1/raw/binance/kline", params)
@@ -0,0 +1,499 @@
1
+ from typing import Any, List, Union
2
+ import pandas as pd
3
+ from datamaxi.api import API
4
+ from datamaxi.lib.utils import check_required_parameter
5
+ from datamaxi.lib.utils import check_required_parameters
6
+ from datamaxi.lib.utils import check_required_parameter_list
7
+ from datamaxi.lib.utils import encode_string_list
8
+ from datamaxi.lib.utils import make_list
9
+ from datamaxi.lib.utils import postprocess
10
+ from datamaxi.lib.constants import BASE_URL
11
+
12
+
13
+ class Defillama(API):
14
+ """Client to fetch Defillama data from DataMaxi+ API."""
15
+
16
+ def __init__(self, api_key=None, **kwargs: Any):
17
+ """Initialize the object.
18
+
19
+ Args:
20
+ api_key (str): The DataMaxi+ API key
21
+ **kwargs: Keyword arguments used by `datamaxi.api.API`.
22
+ """
23
+ if "base_url" not in kwargs:
24
+ kwargs["base_url"] = BASE_URL
25
+ super().__init__(api_key, **kwargs)
26
+
27
+ def protocols(self) -> List[str]:
28
+ """Get supported protocols
29
+
30
+ `GET /v1/defillama/protocol`
31
+
32
+ <https://docs.neverest.finance/defillama/protocol>
33
+
34
+ Returns:
35
+ List of supported protocols
36
+ """
37
+ url_path = "/v1/defillama/protocol"
38
+ return self.query(url_path)
39
+
40
+ def chains(self) -> List[str]:
41
+ """Get supported chains
42
+
43
+ `GET /v1/defillama/chain`
44
+
45
+ <https://docs.neverest.finance/defillama/chain>
46
+
47
+ Returns:
48
+ List of supported chains
49
+ """
50
+ url_path = "/v1/defillama/chain"
51
+ return self.query(url_path)
52
+
53
+ def tokens(self) -> List[str]:
54
+ """Get supported tokens
55
+
56
+ `GET /v1/defillama/token`
57
+
58
+ <https://docs.neverest.finance/defillama/token>
59
+
60
+ Returns:
61
+ List of supported tokens
62
+ """
63
+ url_path = "/v1/defillama/token"
64
+ return self.query(url_path)
65
+
66
+ def pools(self) -> List[str]:
67
+ """Get supported pools
68
+
69
+ `GET /v1/defillama/pool`
70
+
71
+ <https://docs.neverest.finance/defillama/pool>
72
+
73
+ Returns:
74
+ List of supported pools
75
+ """
76
+ url_path = "/v1/defillama/pool"
77
+ return self.query(url_path)
78
+
79
+ def stablecoins(self) -> List[str]:
80
+ """Get supported stablecoins
81
+
82
+ `GET /v1/defillama/stablecoin`
83
+
84
+ <https://docs.neverest.finance/defillama/stablecoin>
85
+
86
+ Returns:
87
+ List of supported stablecoins
88
+ """
89
+ url_path = "/v1/defillama/stablecoin"
90
+ return self.query(url_path)
91
+
92
+ @postprocess()
93
+ def tvl(self, pandas: bool = True) -> Union[List, pd.DataFrame]:
94
+ """Get total TVL across all chains and protocols
95
+
96
+ `GET /v1/defillama/tvl`
97
+
98
+ <https://docs.neverest.finance/defillama/tvl>
99
+
100
+ Args:
101
+ pandas (bool): Return data as pandas DataFrame
102
+
103
+ Returns:
104
+ Timeseries of total TVL
105
+ """
106
+ url_path = "/v1/defillama/tvl"
107
+ return self.query(url_path)
108
+
109
+ @postprocess()
110
+ def protocol_tvl(
111
+ self, protocols: Union[str, List[str]] = None, pandas: bool = True
112
+ ) -> Union[List, pd.DataFrame]:
113
+ """Get TVL for given protocols
114
+
115
+ `GET /v1/defillama/tvl`
116
+
117
+ <https://docs.neverest.finance/defillama/tvl>
118
+
119
+ Args:
120
+ protocols (Union[str, List[str]]): single protocol or multiple protocol names
121
+ pandas (bool): Return data as pandas DataFrame
122
+
123
+ Returns:
124
+ Timeseries of protocol TVLs
125
+ """
126
+ protocols = make_list(protocols)
127
+ check_required_parameter_list(protocols, "protocols")
128
+ params = {"protocols": encode_string_list(protocols)}
129
+ return self.query("/v1/defillama/tvl", params)
130
+
131
+ @postprocess()
132
+ def chain_tvl(
133
+ self, chains: Union[str, List[str]] = None, pandas: bool = True
134
+ ) -> Union[List, pd.DataFrame]:
135
+ """Get TVL for given chains
136
+
137
+ `GET /v1/defillama/tvl`
138
+
139
+ <https://docs.neverest.finance/defillama/tvl>
140
+
141
+ Args:
142
+ chains (Union[str, List[str]]): single chain or multiple chain names
143
+ pandas (bool): Return data as pandas DataFrame
144
+
145
+ Returns:
146
+ Timeseries of chain TVLs
147
+ """
148
+ chains = make_list(chains)
149
+ check_required_parameter_list(chains, "chains")
150
+ params = {"chains": encode_string_list(chains)}
151
+ return self.query("/v1/defillama/tvl", params)
152
+
153
+ @postprocess()
154
+ def protocol_chain_tvl(
155
+ self, protocol: str, chain: str, pandas: bool = True
156
+ ) -> Union[List, pd.DataFrame]:
157
+ """Get TVL for given protocol and chain
158
+
159
+ `GET /v1/defillama/tvl`
160
+
161
+ <https://docs.neverest.finance/defillama/tvl>
162
+
163
+ Args:
164
+ protocol (str): protocol name
165
+ chain (str): chain name
166
+ pandas (bool): Return data as pandas DataFrame
167
+
168
+ Returns:
169
+ Timeseries of protocol TVL on a given chain
170
+ """
171
+ check_required_parameters([[protocol, "protocol"], [chain, "chain"]])
172
+ params = {"chain": chain, "protocol": protocol}
173
+ return self.query("/v1/defillama/tvl", params)
174
+
175
+ @postprocess(num_index=2)
176
+ def protocol_token_tvl(
177
+ self, protocol: str, usd: bool = True, pandas: bool = True
178
+ ) -> Union[List, pd.DataFrame]:
179
+ """Get token TVL on a given protocol
180
+
181
+ `GET /v1/defillama/tvl`
182
+
183
+ <https://docs.neverest.finance/defillama/tvl>
184
+
185
+ Args:
186
+ protocol (str): protocol name
187
+ usd (bool): Convert to USD otherwise return token amount
188
+ pandas (bool): Return data as pandas DataFrame
189
+
190
+ Returns:
191
+ Timeseries of token TVL for a given protocol
192
+ """
193
+ check_required_parameters([[protocol, "protocol"], [usd, "usd"]])
194
+ params = {
195
+ "protocol": protocol,
196
+ "token": "true",
197
+ "usd": str(usd).lower(),
198
+ }
199
+ return self.query("/v1/defillama/tvl", params)
200
+
201
+ @postprocess(num_index=2)
202
+ def protocol_chain_token_tvl(
203
+ self, protocol: str, chain: str, usd: bool = True, pandas: bool = True
204
+ ) -> Union[List, pd.DataFrame]:
205
+ """Get token TVL on a given protocol and chain
206
+
207
+ `GET /v1/defillama/tvl`
208
+
209
+ <https://docs.neverest.finance/defillama/tvl>
210
+
211
+ Args:
212
+ protocol (str): protocol name
213
+ chain (str): chain name
214
+ usd (bool): Convert to USD otherwise return token amount
215
+ pandas (bool): Return data as pandas DataFrame
216
+
217
+ Returns:
218
+ Timeseries of token TVL for a given protocol and chain
219
+ """
220
+ check_required_parameters(
221
+ [[protocol, "protocol"], [chain, "chain"], [usd, "usd"]]
222
+ )
223
+ params = {
224
+ "protocol": protocol,
225
+ "chain": chain,
226
+ "token": "true",
227
+ "usd": str(usd).lower(),
228
+ }
229
+ return self.query("/v1/defillama/tvl", params)
230
+
231
+ @postprocess()
232
+ def protocol_mcap(
233
+ self, protocols: Union[str, List[str]] = None, pandas: bool = True
234
+ ) -> Union[List, pd.DataFrame]:
235
+ """Get market cap for given protocols
236
+
237
+ `GET /v1/defillama/mcap`
238
+
239
+ <https://docs.neverest.finance/defillama/mcap>
240
+
241
+ Args:
242
+ protocols (Union[str, List[str]]): single protocol or multiple protocol names
243
+ pandas (bool): Return data as pandas DataFrame
244
+
245
+ Returns:
246
+ Timeseries of market cap for given protocols
247
+ """
248
+ protocols = make_list(protocols)
249
+ check_required_parameter_list(protocols, "protocols")
250
+ params = {
251
+ "protocols": encode_string_list(protocols),
252
+ }
253
+ return self.query("/v1/defillama/mcap", params)
254
+
255
+ @postprocess()
256
+ def token_price(
257
+ self, addresses: Union[str, List[str]] = None, pandas: bool = True
258
+ ) -> Union[List, pd.DataFrame]:
259
+ """Get token prices
260
+
261
+ `GET /v1/defillama/token`
262
+
263
+ <https://docs.neverest.finance/defillama/token-price>
264
+
265
+ Args:
266
+ addresses (Union[str, List[str]]): single address or multiple addresses
267
+ pandas (bool): Return data as pandas DataFrame
268
+
269
+ Returns:
270
+ Timeseries of token prices
271
+ """
272
+ addresses = make_list(addresses)
273
+ check_required_parameter_list(addresses, "addresses")
274
+ params = {
275
+ "addresses": encode_string_list(addresses),
276
+ }
277
+ return self.query("/v1/defillama/token", params)
278
+
279
+ # def yields(self, pools: Union[str, List[str]]=None) -> pd.DataFrame:
280
+ # pools = make_list(pools)
281
+ # check_required_parameter_list(pools, "pools")
282
+ # params = {
283
+ # "poolIds": encode_string_list(pools),
284
+ # }
285
+ # return self.query("/v1/defillama/yield", params)
286
+
287
+ @postprocess()
288
+ def stablecoin_mcap(
289
+ self, stablecoin: str = None, pandas: bool = True
290
+ ) -> Union[List, pd.DataFrame]:
291
+ """Get market cap for given stablecoin
292
+
293
+ `GET /v1/defillama/stablecoin`
294
+
295
+ <https://docs.neverest.finance/defillama/stablecoin-mcap>
296
+
297
+ Args:
298
+ stablecoin (str): stablecoin name
299
+ pandas (bool): Return data as pandas DataFrame
300
+
301
+ Returns:
302
+ Timeseries of market cap for given stablecoin
303
+ """
304
+ check_required_parameter_list(stablecoin, "stablecoin")
305
+ params = {
306
+ "stablecoin": stablecoin,
307
+ }
308
+ return self.query("/v1/defillama/stablecoin", params)
309
+
310
+ @postprocess()
311
+ def stablecoin_chain_mcap(
312
+ self, stablecoin: str, chain: str, pandas: bool = True
313
+ ) -> Union[List, pd.DataFrame]:
314
+ """Get market cap for a given stablecoin on a specific chain
315
+
316
+ `GET /v1/defillama/stablecoin`
317
+
318
+ <https://docs.neverest.finance/defillama/stablecoin-mcap>
319
+
320
+ Args:
321
+ stablecoin (str): stablecoin name
322
+ chain (str): chain name
323
+ pandas (bool): Return data as pandas DataFrame
324
+
325
+ Returns:
326
+ Timeseries of market cap for given stablecoin on a specific chain
327
+ """
328
+ check_required_parameters([[stablecoin, "stablecoin"], [chain, "chain"]])
329
+ params = {
330
+ "stablecoin": stablecoin,
331
+ "chain": chain,
332
+ }
333
+ return self.query("/v1/defillama/stablecoin", params)
334
+
335
+ @postprocess()
336
+ def stablecoin_price(
337
+ self, stablecoins: Union[str, List[str]] = None, pandas: bool = True
338
+ ) -> Union[List, pd.DataFrame]:
339
+ """Get price for given stablecoins
340
+
341
+ `GET /v1/defillama/stablecoin/price`
342
+
343
+ <https://docs.neverest.finance/defillama/stablecoin-price>
344
+
345
+ Args:
346
+ stablecoins (Union[str, List[str]]): single stablecoin or multiple stablecoin names
347
+ pandas (bool): Return data as pandas DataFrame
348
+
349
+ Returns:
350
+ Timeseries of stablecoin prices
351
+ """
352
+ stablecoins = make_list(stablecoins)
353
+ check_required_parameter_list(stablecoins, "stablecoins")
354
+ params = {
355
+ "stablecoins": encode_string_list(stablecoins),
356
+ }
357
+ return self.query("/v1/defillama/stablecoin/price", params)
358
+
359
+ @postprocess()
360
+ def fee(
361
+ self,
362
+ protocols: Union[str, List[str]] = None,
363
+ chains: Union[str, List[str]] = None,
364
+ daily: bool = True,
365
+ pandas: bool = True,
366
+ ) -> Union[List, pd.DataFrame]:
367
+ """Get fee for given protocols or chains
368
+
369
+ `GET /v1/defillama/fee`
370
+
371
+ <https://docs.neverest.finance/defillama/fee>
372
+
373
+ Args:
374
+ protocols (Union[str, List[str]]): single protocol or multiple protocol names
375
+ chains (Union[str, List[str]]): single chain or multiple chain names
376
+ daily (bool): daily fee or total fee
377
+ pandas (bool): Return data as pandas DataFrame
378
+
379
+ Returns:
380
+ Timeseries of protocol or fees
381
+ """
382
+ check_required_parameter(daily, "daily")
383
+ params = {
384
+ "daily": str(daily).lower(),
385
+ }
386
+
387
+ if not ((protocols is None) ^ (chains is None)):
388
+ raise ValueError("Either protocols or chains should be provided")
389
+
390
+ if protocols is not None:
391
+ protocols = make_list(protocols)
392
+ check_required_parameter_list(protocols, "protocols")
393
+ params["protocols"] = encode_string_list(protocols)
394
+ elif chains is not None:
395
+ chains = make_list(chains)
396
+ check_required_parameter_list(chains, "chains")
397
+ params["chains"] = encode_string_list(chains)
398
+
399
+ return self.query("/v1/defillama/fee", params)
400
+
401
+ @postprocess()
402
+ def revenue(
403
+ self,
404
+ protocols: Union[str, List[str]] = None,
405
+ chains: Union[str, List[str]] = None,
406
+ daily: bool = True,
407
+ pandas: bool = True,
408
+ ) -> Union[List, pd.DataFrame]:
409
+ """Get revenue for given protocols or chains
410
+
411
+ `GET /v1/defillama/revenue`
412
+
413
+ <https://docs.neverest.finance/defillama/revenue>
414
+
415
+ Args:
416
+ protocols (Union[str, List[str]]): single protocol or multiple protocol names
417
+ chains (Union[str, List[str]]): single chain or multiple chain names
418
+ daily (bool): daily revenue or total revenue
419
+ pandas (bool): Return data as pandas DataFrame
420
+
421
+ Returns:
422
+ Timeseries of protocol or chain revenues
423
+ """
424
+ check_required_parameter(daily, "daily")
425
+ params = {
426
+ "daily": str(daily).lower(),
427
+ }
428
+
429
+ if not ((protocols is None) ^ (chains is None)):
430
+ raise ValueError("Either protocols or chains should be provided")
431
+
432
+ if protocols is not None:
433
+ protocols = make_list(protocols)
434
+ check_required_parameter_list(protocols, "protocols")
435
+ params["protocols"] = encode_string_list(protocols)
436
+ elif chains is not None:
437
+ chains = make_list(chains)
438
+ check_required_parameter_list(chains, "chains")
439
+ params["chains"] = encode_string_list(chains)
440
+
441
+ return self.query("/v1/defillama/revenue", params)
442
+
443
+ @postprocess(num_index=4)
444
+ def fee_detail(
445
+ self, protocol: str, chain: str = None, daily: bool = True, pandas: bool = True
446
+ ) -> Union[List, pd.DataFrame]:
447
+ """Get fee detail for given protocol and chain
448
+
449
+ `GET /v1/defillama/fee/detail`
450
+
451
+ <https://docs.neverest.finance/defillama/fee-detail>
452
+
453
+ Args:
454
+ protocol (str): protocol name
455
+ chain (str): chain name (optional)
456
+ daily (bool): daily fee or total fee
457
+ pandas (bool): Return data as pandas DataFrame
458
+
459
+ Returns:
460
+ Timeseries of fee detail for a given protocol and chain
461
+ """
462
+ check_required_parameters([[protocol, "protocol"], [daily, "daily"]])
463
+ params = {
464
+ "protocol": protocol,
465
+ "daily": str(daily).lower(),
466
+ }
467
+ if chain is not None:
468
+ params["chain"] = chain
469
+
470
+ return self.query("/v1/defillama/fee/detail", params)
471
+
472
+ @postprocess(num_index=4)
473
+ def revenue_detail(
474
+ self, protocol: str, chain: str = None, daily: bool = True, pandas: bool = True
475
+ ) -> Union[List, pd.DataFrame]:
476
+ """Get revenue detail for given protocol and chain
477
+
478
+ `GET /v1/defillama/revenue/detail`
479
+
480
+ <https://docs.neverest.finance/defillama/revenue-detail>
481
+
482
+ Args:
483
+ protocol (str): protocol name
484
+ chain (str): chain name (optional)
485
+ daily (bool): daily revenue or total revenue
486
+ pandas (bool): Return data as pandas DataFrame
487
+
488
+ Returns:
489
+ Timeseries of revenue detail for a given protocol and chain
490
+ """
491
+ check_required_parameters([[protocol, "protocol"], [daily, "daily"]])
492
+ params = {
493
+ "protocol": protocol,
494
+ "daily": str(daily).lower(),
495
+ }
496
+ if chain is not None:
497
+ params["chain"] = chain
498
+
499
+ return self.query("/v1/defillama/revenue/detail", params)
datamaxi/error.py ADDED
@@ -0,0 +1,30 @@
1
+ class Error(Exception):
2
+ pass
3
+
4
+
5
+ class ClientError(Error):
6
+ def __init__(self, status_code, error_code, error_message, header, error_data=None):
7
+ # https status code
8
+ self.status_code = status_code
9
+ # error code returned from server
10
+ self.error_code = error_code
11
+ # error message returned from server
12
+ self.error_message = error_message
13
+ # the whole response header returned from server
14
+ self.header = header
15
+ # return data if it's returned from server
16
+ self.error_data = error_data
17
+
18
+
19
+ class ServerError(Error):
20
+ def __init__(self, status_code, message):
21
+ self.status_code = status_code
22
+ self.message = message
23
+
24
+
25
+ class ParameterRequiredError(Error):
26
+ def __init__(self, params):
27
+ self.params = params
28
+
29
+ def __str__(self):
30
+ return "%s is mandatory, but received empty." % (", ".join(self.params))
@@ -0,0 +1,53 @@
1
+ from typing import Any, List, Union
2
+ import pandas as pd
3
+ from datamaxi.api import API
4
+ from datamaxi.lib.utils import check_required_parameter
5
+ from datamaxi.lib.utils import postprocess
6
+ from datamaxi.lib.constants import BASE_URL
7
+
8
+
9
+ class Google(API):
10
+ """Client to fetch Google trend data from DataMaxi+ API."""
11
+
12
+ def __init__(self, api_key=None, **kwargs: Any):
13
+ """Initialize the object.
14
+
15
+ Args:
16
+ api_key (str): The DataMaxi+ API key
17
+ **kwargs: Keyword arguments used by `datamaxi.api.API`.
18
+ """
19
+ if "base_url" not in kwargs:
20
+ kwargs["base_url"] = BASE_URL
21
+ super().__init__(api_key, **kwargs)
22
+
23
+ def keywords(self) -> List[str]:
24
+ """Get Google trend supported keywords
25
+
26
+ `GET /v1/google/keywords`
27
+
28
+ <https://docs.datamaxi.finance/google/keywords>
29
+
30
+ Returns:
31
+ List of supported Google trend keywords
32
+ """
33
+ url_path = "/v1/google/keywords"
34
+ return self.query(url_path)
35
+
36
+ @postprocess()
37
+ def trend(self, keyword: str, pandas: bool = True) -> Union[List, pd.DataFrame]:
38
+ """Get Google trend for given keyword
39
+
40
+ `GET /v1/google/trend`
41
+
42
+ <https://docs.datamaxi.finance/google/trend>
43
+
44
+ Args:
45
+ keyword (str): keyword to search for
46
+ pandas (bool): Return data as pandas DataFrame
47
+
48
+ Returns:
49
+ Google trend data
50
+ """
51
+ check_required_parameter(keyword, "keyword")
52
+ params = {"keyword": keyword}
53
+ return self.query("/v1/google/trend", params)
@@ -0,0 +1 @@
1
+ BASE_URL = "http://api.neverest.finance"
datamaxi/lib/utils.py ADDED
@@ -0,0 +1,109 @@
1
+ from typing import List
2
+ from urllib.parse import urlencode
3
+ import pandas as pd
4
+ from functools import wraps
5
+ from datamaxi.error import ParameterRequiredError
6
+
7
+
8
+ def to_float(x):
9
+ return float(x)
10
+
11
+
12
+ def to_int(x):
13
+ return int(x)
14
+
15
+
16
+ def cleanNoneValue(d) -> dict:
17
+ out = {}
18
+ for k in d.keys():
19
+ if d[k] is not None:
20
+ out[k] = d[k]
21
+ return out
22
+
23
+
24
+ def check_required_parameter(value, name: str):
25
+ if not value and value != 0:
26
+ raise ParameterRequiredError([name])
27
+
28
+
29
+ def check_required_parameters(params):
30
+ """Validate multiple parameters
31
+ params = [
32
+ ['btcusdt', 'symbol'],
33
+ [10, 'price']
34
+ ]
35
+
36
+ """
37
+ for p in params:
38
+ check_required_parameter(p[0], p[1])
39
+
40
+
41
+ def check_required_parameter_list(values: List, name: str):
42
+ if len(values) == 0:
43
+ raise ParameterRequiredError([name])
44
+
45
+ for value in values:
46
+ check_required_parameter(value, name)
47
+
48
+
49
+ def encode_string_list(L: List):
50
+ return "[" + ",".join(f'"{i}"' for i in L) + "]"
51
+
52
+
53
+ def encoded_string(query):
54
+ return urlencode(query, True).replace("%40", "@")
55
+
56
+
57
+ def convert_to_df(data, header: bool, index: str = None, apply_fn={}):
58
+ df = pd.DataFrame(data)
59
+
60
+ if header:
61
+ new_header = df.iloc[0]
62
+ df = df[1:]
63
+ df.columns = new_header
64
+
65
+ if index is not None:
66
+ df = df.set_index(index)
67
+
68
+ for idx, fn in apply_fn.items():
69
+ if isinstance(idx, str):
70
+ df[idx] = df[idx].apply(fn)
71
+ else:
72
+ df[df.columns[idx]] = df[df.columns[idx]].apply(fn)
73
+
74
+ return df
75
+
76
+
77
+ def make_list(value):
78
+ if not isinstance(value, list):
79
+ value = [value]
80
+ return value
81
+
82
+
83
+ def _postprocess(data, index):
84
+ apply_fn = {}
85
+ for idx in range(len(data[0]) - len(index)):
86
+ apply_fn[idx] = to_float
87
+
88
+ return convert_to_df(data, header=True, index=index, apply_fn=apply_fn)
89
+
90
+
91
+ def postprocess(num_index: int = 1):
92
+ def decorator(func):
93
+ @wraps(func)
94
+ def wrapper(*arg, **kwarg):
95
+ res = func(*arg, **kwarg)
96
+
97
+ if not kwarg.get("pandas", True):
98
+ return res
99
+
100
+ if isinstance(res, dict):
101
+ res["data"] = _postprocess(res["data"], res["data"][0][:num_index])
102
+ else:
103
+ res = _postprocess(res, res[0][:num_index])
104
+
105
+ return res
106
+
107
+ return wrapper
108
+
109
+ return decorator
@@ -0,0 +1,53 @@
1
+ from typing import Any, List, Union
2
+ import pandas as pd
3
+ from datamaxi.api import API
4
+ from datamaxi.lib.utils import check_required_parameter
5
+ from datamaxi.lib.constants import BASE_URL
6
+ from datamaxi.lib.utils import postprocess
7
+
8
+
9
+ class Naver(API):
10
+ """Client to fetch Naver trend data from DataMaxi+ API."""
11
+
12
+ def __init__(self, api_key=None, **kwargs: Any):
13
+ """Initialize the object.
14
+
15
+ Args:
16
+ api_key (str): The DataMaxi+ API key
17
+ **kwargs: Keyword arguments used by `datamaxi.api.API`.
18
+ """
19
+ if "base_url" not in kwargs:
20
+ kwargs["base_url"] = BASE_URL
21
+ super().__init__(api_key, **kwargs)
22
+
23
+ def keywords(self) -> List[str]:
24
+ """Get Naver trend supported keywords
25
+
26
+ `GET /v1/naver/keywords`
27
+
28
+ <https://docs.datamaxi.finance/naver/keywords>
29
+
30
+ Returns:
31
+ List of supported Naver trend keywords
32
+ """
33
+ url_path = "/v1/naver/keywords"
34
+ return self.query(url_path)
35
+
36
+ @postprocess()
37
+ def trend(self, keyword: str, pandas: bool = True) -> Union[List, pd.DataFrame]:
38
+ """Get Naver trend for given keyword
39
+
40
+ `GET /v1/naver/trend`
41
+
42
+ <https://docs.neverest.finance/naver/trend>
43
+
44
+ Args:
45
+ keyword (str): keyword to search for
46
+ pandas (bool): Return data as pandas DataFrame
47
+
48
+ Returns:
49
+ Naver trend data
50
+ """
51
+ check_required_parameter(keyword, "keyword")
52
+ params = {"keyword": keyword}
53
+ return self.query("/v1/naver/trend", params)
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Bisonai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.1
2
+ Name: datamaxi
3
+ Version: 0.1.0
4
+ Summary: Official Python client for DataMaxi+ API
5
+ Author-email: Bisonai <business@bisonai.com>
6
+ Project-URL: Homepage, https://github.com/bisonai/datamaxi-python
7
+ Project-URL: Issues, https://github.com/bisonai/datamaxi-python/issues
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Intended Audience :: Financial and Insurance Industry
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: requests >=2.31.0
19
+ Requires-Dist: pandas
20
+
21
+ # DataMaxi+ Python Client
22
+ [![PyPI version](https://img.shields.io/pypi/v/datamaxi)](https://pypi.python.org/pypi/datamaxi)
23
+ [![Python version](https://img.shields.io/pypi/pyversions/datamaxi)](https://www.python.org/downloads/)
24
+ [![Documentation](https://img.shields.io/badge/docs-latest-blue)](https://datamaxi.readthedocs.io/en/stable/)
25
+ [![Code Style](https://img.shields.io/badge/code_style-black-black)](https://black.readthedocs.io/en/stable/)
26
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
27
+
28
+ This is the official implementation of Python client for DataMaxi+ API.
29
+ The package can be used to fetch both historical and latest data using [DataMaxi+ API](https://docs.neverest.finance/).
30
+ This package is compatible with Python v3.8+.
31
+
32
+ * [Installation](#installation)
33
+ * [Configuration](#configuration)
34
+ * [Environment Variables](#environment-variables)
35
+ * [Quickstart](#quickstart)
36
+ * [Local Development](#local-development)
37
+ * [Setup](#setup)
38
+ * [Testing](#testing)
39
+ * [Links](#links)
40
+ * [Contributing](#contributing)
41
+ * [License](#license)
42
+
43
+ ## Installation
44
+
45
+ ```shell
46
+ pip install datamaxi
47
+ ```
48
+
49
+ ## Configuration
50
+
51
+ Access to DataMaxi+ is protected by API Key.
52
+ If you are interested to try DataMaxi+, you can request your API key at [business@bisonai.com](mailto:business@bisonai.com).
53
+
54
+ | Option | Explanation |
55
+ |--------------------|---------------------------------------------------------------------------------------|
56
+ | `api_key` | Your API key |
57
+ | `base_url` | If `base_url` is not provided, it defaults to `api.neverest.finance`. |
58
+ | `timeout` | Number of seconds to wait for a server response. By default requests do not time out. |
59
+ | `proxies` | Proxy through which the request is queried |
60
+ | `show_limit_usage` | Return response as dictionary including including `"limit_usage"` and `"data"` keys |
61
+ | `show_header` | Return response as dictionary including including `"header"` and `"data"` keys |
62
+
63
+ ### Environment Variables
64
+
65
+ You may use environment variables to configure the DataMaxi+ client to avoid any inline boilerplate.
66
+
67
+ | Env | Description |
68
+ |--------------------|----------------------------------------------|
69
+ | `NEVEREST_API_KEY` | Used instead of `api_key` if none is passed. |
70
+
71
+ ## Quickstart
72
+
73
+ DataMaxi+ Python package currently includes the following clients:
74
+
75
+ * `Binance`
76
+ * `Defillama`
77
+ * `Naver`
78
+ * `Google`
79
+
80
+ All clients accept the same parameters that are described at [Configuration](#configuration) section.
81
+ First, import the clients,
82
+
83
+ ```python
84
+ from datamaxi.binance import Binance
85
+ from datamaxi.defillama import Defillama
86
+ from datamaxi.naver import Naver
87
+ from datamaxi.google import Google
88
+ ```
89
+
90
+ and initialize them.
91
+
92
+ ```python
93
+ binance = Binance(api_key=api_key)
94
+ defillama = Defillama(api_key=api_key)
95
+ naver = Naver(api_key=api_key)
96
+ google = Google(api_key=api_key)
97
+ ```
98
+
99
+ ## Local Development
100
+
101
+ ### Setup
102
+
103
+ If you wish to work on local development please clone/fork the git repo and use `pip install -r requirements.txt` to setup the project.
104
+
105
+ ### Testing
106
+
107
+ ```shell
108
+ # In case packages are not installed yet
109
+ pip install -r requirements/requirements-test.txt
110
+
111
+ python -m pytest tests/
112
+ ```
113
+
114
+ ## Links
115
+
116
+ * [DataMaxi+](https://datamaxiplus.com/)
117
+ * [DataMaxi+ API](https://api.neverest.finance/)
118
+ * [DataMaxi+ API Documentation](https://docs.neverest.finance/)
119
+
120
+ ## Contributing
121
+
122
+ We welcome contributions!
123
+ If you discover a bug in this project, please feel free to open an issue to discuss the changes you would like to propose.
124
+
125
+ ## License
126
+
127
+ [MIT License](LICENSE)
@@ -0,0 +1,15 @@
1
+ datamaxi/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ datamaxi/__version__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
3
+ datamaxi/api.py,sha256=GDqbaPpseouOAdC3d94kuRSXIdWiPUnZE4-_hKOKrCk,4549
4
+ datamaxi/error.py,sha256=b6HanzbJGnJdjmVxsj-Tx0S2g6qmrPnwOzQVLJapXz0,907
5
+ datamaxi/binance/__init__.py,sha256=ZLKtIwpMm-4R9edQ2wfatuA1j0vfcDuPenqJQi6Obvo,1749
6
+ datamaxi/defillama/__init__.py,sha256=MckStB_FFRrn4wsCETbmZSMChCHpoLjD0lcj4iTzjsM,15894
7
+ datamaxi/google/__init__.py,sha256=Ub5LzCykc0FkOFqc490Ebr688eoMQG_gmzuJJJqsgJk,1573
8
+ datamaxi/lib/constants.py,sha256=M_rPOUd8vhCyCqU5hEynA9JqwTaRqJTwwEhvn2Dc3Vg,41
9
+ datamaxi/lib/utils.py,sha256=qvN_NjUXUiOzGAkxpvuzgEyhV6IpH9hDqv1AZpWTgHA,2379
10
+ datamaxi/naver/__init__.py,sha256=qoHOjBNZONlC4OLI-WDuGRCmQzrSScL0ILLLYv0nNRM,1561
11
+ datamaxi-0.1.0.dist-info/LICENSE,sha256=1J0wkcNPh72_zZZ3QLCIdpWQ8-qyV3z0GBLhWwRpFsU,1064
12
+ datamaxi-0.1.0.dist-info/METADATA,sha256=ucW1ed_84gOcyurvvVJ3HAib2zGMoLNis7bqjYhHfZ4,4635
13
+ datamaxi-0.1.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
14
+ datamaxi-0.1.0.dist-info/top_level.txt,sha256=tUTuDxSB4RdO2TFQk3HpdrlnS6oi6KCz_LJTgzp-JoE,9
15
+ datamaxi-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: bdist_wheel (0.43.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ datamaxi