├── gmocoiner ├── __init__.py ├── auth.py └── api.py ├── README.md ├── setup.py ├── gmocoiner_usage.py └── .gitignore /gmocoiner/__init__.py: -------------------------------------------------------------------------------- 1 | from .api import GMOCoin 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Deprecated]gmocoiner 2 | GMOコインのPython用自動取引モジュール 3 | https://note.mu/mtkn1/n/n39237b0c3341 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from setuptools import setup 4 | 5 | setup( 6 | name="gmocoiner", 7 | packages=['gmocoiner'], 8 | version="0.1", 9 | description="Python API Client for GMO Coin's API.", 10 | author="MtkN1", 11 | url="https://github.com/MtkN1/gmocoiner", 12 | install_requires=['requests'], 13 | keywords=["bitcoin", "GMO Coin", "API Client"] 14 | ) 15 | -------------------------------------------------------------------------------- /gmocoiner_usage.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from gmocoiner import GMOCoin 4 | 5 | if __name__ == '__main__': 6 | api_key = 'YOUR_API_KEY' 7 | secret = 'YOUR_SECRET_KEY' 8 | 9 | gmo = GMOCoin(api_key, secret, late_limit=True, logger=None) 10 | 11 | # ティッカー情報を表示 12 | resp = gmo.ticker(symbol='BTC_JPY') 13 | ticker = resp.json() 14 | print(json.dumps(ticker, indent=2)) 15 | 16 | # 資産残高を表示 17 | resp = gmo.account_assets() 18 | print(json.dumps(resp.json(), indent=2)) 19 | 20 | # 最寄買い気配地 - 5000の価格に買い指値 21 | buy = int(ticker['data'][0]['bid']) - 5000 22 | resp = gmo.order(symbol='BTC_JPY', side='BUY', 23 | executionType='LIMIT', size='0.01', price=buy) 24 | print(json.dumps(resp.json(), indent=2)) 25 | -------------------------------------------------------------------------------- /gmocoiner/auth.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import hmac 3 | import re 4 | import time 5 | import urllib.parse 6 | 7 | from requests.auth import AuthBase 8 | 9 | 10 | class GMOCoinAuth(AuthBase): 11 | def __init__(self, api_key, secret): 12 | self.api_key = api_key 13 | self.secret = secret 14 | 15 | def __call__(self, r): 16 | timestamp = str(int(time.time() * 1000)) 17 | 18 | o = urllib.parse.urlparse(r.path_url).path 19 | p = re.sub(r'\A/(public|private)', '', o) 20 | text = timestamp + r.method + p + (r.body or '') 21 | sign = hmac.new(self.secret.encode('ascii'), text.encode('ascii'), hashlib.sha256).hexdigest() 22 | 23 | headers = dict(r.headers) 24 | headers['API-KEY'] = self.api_key 25 | headers['API-TIMESTAMP'] = timestamp 26 | headers['API-SIGN'] = sign 27 | r.prepare_headers(headers) 28 | 29 | return r 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /gmocoiner/api.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | from logging import ( 4 | DEBUG, FileHandler, Formatter, NullHandler, StreamHandler, getLogger) 5 | 6 | from requests import Request, Session 7 | from requests.exceptions import HTTPError 8 | 9 | from .auth import GMOCoinAuth 10 | 11 | 12 | class GMOCoin(object): 13 | """ 14 | GMOコインAPIクライアント 15 | 16 | :param api_key: APIキー 17 | :type api_key: str 18 | :param secret: 秘密鍵 19 | :type secret: str 20 | :param late_limit: レート制限をクライアント制御する 21 | :type late_limit: bool 22 | :param logger: ロガー 23 | :type logger: Logger 24 | """ 25 | endpoint = { 26 | 'public': 'https://api.coin.z.com/public', 27 | 'private': 'https://api.coin.z.com/private' 28 | } 29 | 30 | def __init__(self, api_key, secret, late_limit=True, logger=None): 31 | self.logger = logger or getLogger(__name__) 32 | 33 | self.s = Session() 34 | self.s.headers['Content-Type'] = 'application/json' 35 | 36 | self.gmo_auth = GMOCoinAuth(api_key, secret) 37 | 38 | self.late_limit = late_limit 39 | self.last_req_time = 0 40 | 41 | def _request(self, method, path, payload, auth): 42 | for k, v in list(payload.items()): 43 | if v is None: 44 | del payload[k] 45 | 46 | if method == 'GET': 47 | body = None 48 | query = payload 49 | else: 50 | body = json.dumps(payload) 51 | query = None 52 | 53 | if not auth: 54 | endpoint = self.endpoint['public'] 55 | self.s.auth = None 56 | else: 57 | endpoint = self.endpoint['private'] 58 | self.s.auth = self.gmo_auth 59 | 60 | req = Request(method, endpoint + path, data=body, params=query) 61 | prepped = self.s.prepare_request(req) 62 | self.logger.debug(f'sending req to {prepped.url}: {prepped.body}') 63 | 64 | if self.late_limit: 65 | now = time.time() 66 | if self.last_req_time + 1 > now: 67 | wait_time = self.last_req_time + 1 - now 68 | time.sleep(wait_time) 69 | 70 | resp = None 71 | try: 72 | resp = self.s.send(prepped) 73 | resp.raise_for_status() 74 | except HTTPError as e: 75 | self.logger.error(e) 76 | 77 | if self.late_limit: 78 | self.last_req_time = time.time() 79 | 80 | self.logger.debug(f'{resp} {resp.text}') 81 | 82 | return resp 83 | 84 | # 85 | # Public API 86 | # 87 | 88 | def status(self): 89 | """ 90 | 取引所ステータス 91 | 92 | 取引所の稼動状態を取得します。 93 | """ 94 | payload = {} 95 | return self._request('GET', '/v1/status', payload, auth=False) 96 | 97 | def ticker(self, symbol): 98 | """ 99 | 最新レート 100 | 101 | 指定した銘柄の最新レートを取得します。 102 | 103 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 104 | :type symbol: str 105 | """ 106 | payload = { 107 | 'symbol': symbol 108 | } 109 | return self._request('GET', '/v1/ticker', payload, auth=False) 110 | 111 | def orderbooks(self, symbol): 112 | """ 113 | 板情報 114 | 115 | 指定した銘柄の板情報(snapshot)を取得します。 116 | 117 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 118 | :type symbol: str 119 | """ 120 | payload = { 121 | 'symbol': symbol 122 | } 123 | return self._request('GET', '/v1/orderbooks', payload, auth=False) 124 | 125 | def trades(self, symbol, page=None, count=None): 126 | """ 127 | 取引履歴 128 | 129 | 指定した銘柄の取引履歴を取得します。 130 | 131 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 132 | :type symbol: str 133 | :param page: optional 取得対象ページ: 指定しない場合は1を指定したとして動作する。 134 | :type page: number 135 | :param count: optional 1ページ当りの取得件数: 指定しない場合は100(最大値)を指定したとして動作する。 136 | :type count: number 137 | """ 138 | payload = { 139 | 'symbol': symbol, 140 | 'page': page, 141 | 'count': count 142 | } 143 | return self._request('GET', '/v1/trades', payload, auth=False) 144 | 145 | # 146 | # Private API 147 | # 148 | 149 | def account_margin(self): 150 | """ 151 | 余力情報を取得 152 | 153 | 指定した銘柄の取引履歴を取得します。 154 | 対象: 現物取引、レバレッジ取引 155 | """ 156 | payload = {} 157 | return self._request('GET', '/v1/account/margin', payload, auth=True) 158 | 159 | def account_assets(self): 160 | """ 161 | 資産残高を取得 162 | 163 | 指定した銘柄の取引履歴を取得します。 164 | 対象: 現物取引、レバレッジ取引 165 | """ 166 | payload = {} 167 | return self._request('GET', '/v1/account/assets', payload, auth=True) 168 | 169 | def orders(self, orderId=None): 170 | """ 171 | 注文情報取得 172 | 173 | 指定した注文IDの注文情報を取得します。 174 | 対象: 現物取引、レバレッジ取引 175 | 176 | :param orderId: *required 177 | :type orderId: number 178 | """ 179 | payload = { 180 | 'orderId': orderId 181 | } 182 | return self._request('GET', '/v1/orders', payload, auth=True) 183 | 184 | def activeorders(self, symbol, page=None, count=None): 185 | """ 186 | 有効注文一覧 187 | 188 | 有効注文一覧を取得します。 189 | 対象: 現物取引、レバレッジ取引 190 | 191 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 192 | :type symbol: str 193 | :param page: optional 取得対象ページ: 指定しない場合は1を指定したとして動作する。 194 | :type page: number 195 | :param count: optional 1ページ当りの取得件数: 指定しない場合は100(最大値)を指定したとして動作する。 196 | :type count: number 197 | """ 198 | payload = { 199 | 'symbol': symbol, 200 | 'page': page, 201 | 'count': count 202 | } 203 | return self._request('GET', '/v1/activeOrders', payload, auth=True) 204 | 205 | def executions(self, orderId=None, executionId=None): 206 | """ 207 | 約定情報取得 208 | 209 | 約定情報を取得します。 210 | 対象: 現物取引、レバレッジ取引 211 | orderId executionId いずれか一つが必須です。2つ同時には設定できません。 212 | 213 | :param orderId: * orderId executionId いずれか一つが必須。 214 | :type orderId: number 215 | :param executionId: * orderId executionId いずれか一つが必須。 216 | :type executionId: number 217 | """ 218 | payload = { 219 | 'orderId': orderId, 220 | 'executionId': executionId 221 | } 222 | return self._request('GET', '/v1/executions', payload, auth=True) 223 | 224 | def latestexecutions(self, symbol, page=None, count=None): 225 | """ 226 | 最新の約定一覧 227 | 228 | 最新約定一覧を取得します。 229 | 対象: 現物取引、レバレッジ取引 230 | 直近1日分の約定情報を返します。 231 | 232 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 233 | :type symbol: str 234 | :param page: optional 取得対象ページ: 指定しない場合は1を指定したとして動作する。 235 | :type page: number 236 | :param count: optional 1ページ当りの取得件数: 指定しない場合は100(最大値)を指定したとして動作する。 237 | :type count: number 238 | """ 239 | payload = { 240 | 'symbol': symbol, 241 | 'page': page, 242 | 'count': count 243 | } 244 | return self._request('GET', '/v1/latestExecutions', payload, auth=True) 245 | 246 | def openpositions(self, symbol, page=None, count=None): 247 | """ 248 | 建玉一覧を取得 249 | 250 | 有効建玉一覧を取得します。 251 | 対象: レバレッジ取引 252 | 253 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 254 | :type symbol: str 255 | :param page: optional 取得対象ページ: 指定しない場合は1を指定したとして動作する。 256 | :type page: number 257 | :param count: optional 1ページ当りの取得件数: 指定しない場合は100(最大値)を指定したとして動作する。 258 | :type count: number 259 | """ 260 | payload = { 261 | 'symbol': symbol, 262 | 'page': page, 263 | 'count': count 264 | } 265 | return self._request('GET', '/v1/openPositions', payload, auth=True) 266 | 267 | def positionsummary(self, symbol): 268 | """ 269 | 建玉サマリーを取得 270 | 271 | 建玉サマリーを取得します。 272 | 対象: レバレッジ取引 273 | 銘柄ごと、売買区分(買/売)ごとの建玉サマリー取得ができます。 274 | 275 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 276 | :type symbol: str 277 | """ 278 | payload = { 279 | 'symbol': symbol 280 | } 281 | return self._request('GET', '/v1/positionSummary', payload, auth=True) 282 | 283 | def order(self, symbol, side, executionType, size, price=None): 284 | """ 285 | 注文 286 | 287 | 新規注文をします。 288 | 対象: 現物取引、レバレッジ取引 289 | 290 | 現物取引: 買/売注文 291 | レバレッジ取引: 新規の買/売注文 292 | 293 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 294 | :type symbol: str 295 | :param side: *required BUY SELL 296 | :type side: str 297 | :param executionType: *required MARKET LIMIT 298 | :type executionType: str 299 | :param price: *executionTypeによる LIMIT の場合は必須、 MARKET の場合は不要。 300 | :type price: number 301 | :param size: *required 302 | :type size: str 303 | """ 304 | payload = { 305 | 'symbol': symbol, 306 | 'side': side, 307 | 'executionType': executionType, 308 | 'price': price, 309 | 'size': size, 310 | } 311 | return self._request('POST', '/v1/order', payload, auth=True) 312 | 313 | def changeorder(self, orderId, price): 314 | """ 315 | 注文変更 316 | 317 | 注文変更をします。 318 | 対象: 現物取引、レバレッジ取引 319 | 320 | :param orderId: *required 321 | :type orderId: number 322 | :param price: *required 323 | :type price: str 324 | """ 325 | payload = { 326 | 'orderId': orderId, 327 | 'price': price 328 | } 329 | return self._request('POST', '/v1/changeOrder', payload, auth=True) 330 | 331 | def cancelorder(self, orderId): 332 | """ 333 | 注文キャンセル 334 | 335 | 注文取消をします。 336 | 対象: 現物取引、レバレッジ取引 337 | 338 | :param orderId: *required 339 | :type orderId: number 340 | """ 341 | payload = { 342 | 'orderId': orderId 343 | } 344 | return self._request('POST', '/v1/cancelOrder', payload, auth=True) 345 | 346 | def closeorder(self, symbol, side, executionType, 347 | settlePosition_positionId, settlePosition_size, price=None): 348 | """ 349 | 決済注文 350 | 351 | 決済注文をします。 352 | 対象: レバレッジ取引 353 | 354 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 355 | :type symbol: str 356 | :param side: *required BUY SELL 357 | :type side: str 358 | :param executionType: *required MARKET LIMIT 359 | :type executionType: str 360 | :param price: *executionTypeによる LIMIT の場合は必須、 MARKET の場合は不要。 361 | :type price: number 362 | :param settlePosition_positionId: *required 建玉は1つのみ指定可能。 363 | :type settlePosition_positionId: number 364 | :param settlePosition_size: *required 建玉は1つのみ指定可能。 365 | :type settlePosition_size: str 366 | """ 367 | payload = { 368 | 'symbol': symbol, 369 | 'side': side, 370 | 'executionType': executionType, 371 | 'price': price, 372 | 'settlePosition': [ 373 | { 374 | 'positionId': settlePosition_positionId, 375 | 'size': settlePosition_size 376 | } 377 | ] 378 | } 379 | return self._request('POST', '/v1/closeOrder', payload, auth=True) 380 | 381 | def closebulkorder(self, symbol, side, executionType, size, price=None): 382 | """ 383 | 一括決済注文 384 | 385 | 一括決済注文をします。 386 | 対象: レバレッジ取引 387 | 388 | :param symbol: *required BTC ETH BCH LTC XRP BTC_JPY ETH_JPY BCH_JPY LTC_JPY XRP_JPY 389 | :type symbol: str 390 | :param side: *required BUY SELL 391 | :type side: str 392 | :param executionType: *required MARKET LIMIT 393 | :type executionType: str 394 | :param price: *executionTypeによる LIMIT の場合は必須、 MARKET の場合は不要。 395 | :type price: number 396 | :param size: *required 397 | :type size: str 398 | """ 399 | payload = { 400 | 'symbol': symbol, 401 | 'side': side, 402 | 'executionType': executionType, 403 | 'price': price, 404 | 'size': size 405 | } 406 | return self._request('POST', '/v1/closeBulkOrder', payload, auth=True) 407 | 408 | def changelosscutprice(self, positionId=None, losscutPrice=None): 409 | """ 410 | ロスカットレート変更 411 | 412 | 建玉のロスカットレート変更をします。 413 | 対象: レバレッジ取引 414 | 415 | :param positionId: *required 416 | :type positionId: number 417 | :param losscutPrice: *required 418 | :type losscutPrice: str 419 | """ 420 | payload = { 421 | 'positionId': positionId, 422 | 'losscutPrice': losscutPrice 423 | } 424 | return self._request('POST', '/v1/changeLosscutPrice', payload, auth=True) 425 | --------------------------------------------------------------------------------