Skip to content
This repository was archived by the owner on Aug 20, 2025. It is now read-only.

Commit e5dbc78

Browse files
committed
working on unittests
1 parent e54cd1e commit e5dbc78

File tree

6 files changed

+161
-16
lines changed

6 files changed

+161
-16
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,5 @@ ENV/
101101
.mypy_cache/
102102

103103
test.py
104-
.idea/**
104+
.idea/
105+
secrets.yml

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# python-ctapi
22

3-
Python Interface for [CoinTracking.info API](https://cointracking.info/api/api.php)
3+
Python interface for [CoinTracking.info API](https://cointracking.info/api/api.php).
4+
5+
I am not associated -- use at your own risk!
46

57
# Requirements:
68

@@ -41,10 +43,15 @@ print api.getGroupedBalance()
4143
print api.getGains()
4244
```
4345

46+
# Running Tests
47+
```
48+
venv/bin/python -m unittest -v ctapi.test.ctapi_tests
49+
```
50+
4451
# Contribute
4552
Do you have an idea or found a bug in python-ctapi? Please file an issue and make a PR! :)
4653

47-
## Support
54+
## Support Me
4855
If you like the API and wanna support its developer, use the following referral link when registering at cointracking: https://cointracking.info?ref=T161519
4956

5057

ctapi/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .ctapi import *

ctapi/ctapi.py

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,23 @@
3131

3232
URI_API = 'https://cointracking.info/api/v1/'
3333

34-
34+
#
35+
# API object
36+
#
3537
class CTAPI(object):
36-
""" requesting CoinTracking API with API key and API secret """
38+
"""
39+
requesting CoinTracking API with API key and API secret
40+
41+
Documentation: https://cointracking.info/api/api.php
42+
"""
3743

3844
#
3945
# init
4046
#
41-
def __init__(self, api_key='', api_secret='', debug=False):
47+
def __init__(self, api_key=None, api_secret=None, debug=False):
48+
# if not self.api_key or not self.api_secret:
49+
50+
4251
self.api_key = api_key
4352
self.api_secret = api_secret
4453
self.debug = debug
@@ -57,7 +66,11 @@ def __init__(self, api_key='', api_secret='', debug=False):
5766
# encode parameters for url
5867
#
5968
def _encode_params_url(self, params):
60-
""" TODO """
69+
"""
70+
encoding URL parameters
71+
72+
:params: Request parameters to be encoded
73+
"""
6174

6275
encoded_string = ''
6376

@@ -73,7 +86,13 @@ def _encode_params_url(self, params):
7386
# make query to API
7487
#
7588
def _api_query(self, method, params={}):
76-
""" TODO """
89+
"""
90+
Queries CoinTracking.info
91+
92+
:method: Request method
93+
:params: Request parameters
94+
:return: JSON response from Bittrex
95+
"""
7796

7897
global URI_API
7998

@@ -82,14 +101,20 @@ def _api_query(self, method, params={}):
82101
'nonce': '%d' % int(time.time() * 10),
83102
})
84103

104+
if not self.api_secret:
105+
return {
106+
'success': False,
107+
'message': 'no valid secret key',
108+
}
109+
85110
params_string = self._encode_params_url(params)
86111
params_signed = hmac.new(self.api_secret.encode(), msg=params_string.encode(), digestmod=hashlib.sha512).hexdigest()
87112

88113
hdrs = {
89114
'Key': self.api_key,
90115
'Sign': params_signed,
91116
'Connection': 'close',
92-
'User-Agent': 'python-cointracking-api/%s (https://github.com/tbl42/python-ctapi)' % (__version__),
117+
'User-Agent': 'python-ctapi/%s (https://github.com/tbl42/python-ctapi)' % (__version__),
93118
}
94119

95120
logger.debug("="*30)
@@ -108,7 +133,7 @@ def _api_query(self, method, params={}):
108133
ret_json = r.json()
109134

110135
return {
111-
'success': True,
136+
'success': ret_json['success'],
112137
'result': ret_json
113138
}
114139
except:
@@ -125,61 +150,90 @@ def _api_query(self, method, params={}):
125150
# getTrades
126151
#
127152
def getTrades(self, **args):
128-
""" TODO: desc """
153+
"""
154+
Used to get all your CoinTracking trades and transactions.
155+
Similar to the Trade List at https://cointracking.info/trades_full.php
156+
"""
157+
129158
params = {
130159
'limit': 5,
131160
'order': 'DESC',
132161
}
133162
params.update(args)
163+
134164
return self._api_query('getTrades', params)
135165

136166
#
137167
# getBalance
138168
#
139169
def getBalance(self):
140-
""" TODO: desc """
170+
"""
171+
Used to get your current CoinTracking account and coin balance.
172+
Similar to the Current Balance at https://cointracking.info/current_balance.php
173+
"""
174+
141175
return self._api_query('getBalance')
142176

143177
#
144178
# getHistoricalSummary
145179
#
146180
def getHistoricalSummary(self, **args):
147-
""" TODO: desc """
181+
"""
182+
Used to get all historical values for all your coins, currencies, commodities, and the total account value.
183+
Similar to the Daily Balance at https://cointracking.info/overview.php or the Trade Statistics at https://cointracking.info/stats.php
184+
"""
185+
148186
params = {
149187
'btc': 0,
150188
}
151189
params.update(args)
190+
152191
return self._api_query('getHistoricalSummary', params)
153192

154193
#
155194
# getHistoricalCurrency
156195
#
157196
def getHistoricalCurrency(self, **args):
158-
""" TODO: desc """
197+
"""
198+
Used to get all historical amounts and values for a specific currency/coin or for all currencies/coins.
199+
Similar to the Daily Balance at https://cointracking.info/overview.php or the Trade Statistics at https://cointracking.info/stats.php
200+
"""
201+
159202
params = {
160203
'currency': 'ETH',
161204
}
162205
params.update(args)
206+
163207
return self._api_query('getHistoricalCurrency', params)
164208

165209
#
166210
# getGroupedBalance
167211
#
168212
def getGroupedBalance(self, **args):
169-
""" TODO: desc """
213+
"""
214+
Used to get the current balance grouped by exchange, trade-group or transaction type.
215+
Similar to the Balance by Exchange at https://cointracking.info/balance_by_exchange.php
216+
"""
217+
170218
params = {
171219
'group': 'exchange',
172220
}
173221
params.update(args)
222+
174223
return self._api_query('getGroupedBalance', params)
175224

176225
#
177226
# getGains
178227
#
179228
def getGains(self, **args):
180-
""" TODO: desc """
229+
"""
230+
Used to get Returns your current realized and unrealized gains data.
231+
Similar to the Realized and Unrealized Gains at https://cointracking.info/gains.php
232+
"""
233+
181234
params = {
182235
'method': 'FIFO',
183236
}
184237
params.update(args)
238+
185239
return self._api_query('getGains', params)

ctapi/test/__init__.py

Whitespace-only changes.

ctapi/test/ctapi_tests.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import json
2+
import yaml
3+
import unittest
4+
5+
from ctapi import CTAPI
6+
7+
try:
8+
open("secrets.yml").close()
9+
IS_CI_ENV = False
10+
except Exception:
11+
IS_CI_ENV = True
12+
13+
# def test_basic_response(unit_test, result, method_name):
14+
# unit_test.assertTrue(result['success'], "{0:s} failed".format(method_name))
15+
# unit_test.assertTrue(result['message'] is not None, "message not present in response")
16+
# unit_test.assertTrue(result['result'] is not None, "result not present in response")
17+
18+
19+
# {'result': {u'error_msg': u'ERROR: Sign is not correct', u'method': u'getBalance', u'success': 0, u'error': u'SIGN_INCORRECT'}, 'success': True}
20+
# {'result': {u'error_msg': u'ERROR: API Key not set', u'method': u'getBalance', u'success': 0, u'error': u'KEY_MISSING'}, 'success': True}
21+
# {'result': {u'error_msg': u'ERROR: Sign is not correct', u'method': u'getBalance', u'success': 0, u'error': u'SIGN_INCORRECT'}, 'success': True}
22+
23+
def test_auth_basic_failures(unit_test, result, test_type):
24+
pass
25+
26+
# unit_test.assertFalse(result['success'], "{0:s} failed".format(test_type))
27+
# unit_test.assertTrue('invalid' in str(result['message']).lower(), "{0:s} failed response message".format(test_type))
28+
# unit_test.assertIsNone(result['result'], "{0:s} failed response result not None".format(test_type))
29+
30+
31+
@unittest.skipIf(IS_CI_ENV, 'no account secrets uploaded in CI envieonment, TODO')
32+
class TestCoinTrackingAPIBasicTests(unittest.TestCase):
33+
"""
34+
Integration tests for the CoinTracking API
35+
36+
* These will fail in the absence of an internet connection or if CoinTracking API goes down
37+
* They require a valid API key and secret issued by CoinTracking
38+
* They also require the presence of a JSON file called secrets.yml
39+
40+
It is structured as such:
41+
---
42+
key: '123'
43+
secret: '456'
44+
"""
45+
46+
def setUp(self):
47+
with open("secrets.yml") as f:
48+
self.secrets = yaml.load(f)
49+
f.close()
50+
51+
# self.api = CTAPI(secrets['key'], secrets['secret'], debug=True)
52+
self.api = CTAPI(self.secrets['key'], self.secrets['secret'])
53+
54+
def test_handles_invalid_key_or_secret(self):
55+
self.api = CTAPI('invalidkey', self.secrets['secret'])
56+
actual = self.api.getBalance()
57+
self.assertFalse(actual['success'], 'Invalid key, valid secret')
58+
59+
self.api = CTAPI(None, self.secrets['secret'])
60+
actual = self.api.getBalance()
61+
self.assertFalse(actual['success'], 'None key, valid secret')
62+
63+
self.api = CTAPI(self.secrets['key'], 'invalidsecret')
64+
actual = self.api.getBalance()
65+
self.assertFalse(actual['success'], 'valid key, invalid secret')
66+
67+
self.api = CTAPI(self.secrets['key'], None)
68+
actual = self.api.getBalance()
69+
self.assertFalse(actual['success'], 'valid key, None secret')
70+
71+
self.api = CTAPI('invalidkey', 'invalidsecret')
72+
actual = self.api.getBalance()
73+
self.assertFalse(actual['success'], 'invalid key, invalid secret')
74+
75+
# def test_get_balances(self):
76+
# actual = self.bittrex.get_balances()
77+
# test_basic_response(self, actual, "get_balances")
78+
# self.assertTrue(isinstance(actual['result'], list), "result is not a list")
79+
80+
81+
if __name__ == '__main__':
82+
unittest.main()

0 commit comments

Comments
 (0)