Apiクラスにexe_buy_order_market_wait(), exe_buy_order_market(), exe_sell_close_order_limit()メソッドを追加する
ここでは、Coinクラスのtran_typeプロパティに「buy_sell」を設定したときにBOTで使用されるApiのメソッドを追加します。
「買い▶売り」の注文を行う場合、BOTは買いの成行き注文を行って約定したら売りの注文を指値で行います。
exe_buy_order_market_wait()は親メソッドで、exe_buy_order_market()は子メソッドです。
親メソッドでは買いの成行き注文が約定するまで一定時間待ちます。
そして約定したら約定情報をPandasのDataFrameに格納して返します。
DatFrameには約定したときの価格(レート)、数量、注文ID、ポジションIDなどが格納されています。
なお、数量は分割されることがあります。
たとえば、リップル(XRP_JPY)の数量「100」を買いで注文をしたとき、
「20」「30」「50」のように分割されることがあります。
さらに、成行き注文のときは「FAK: Fill and Kill」が適用されて全数量約定しない場合があります。
前出の例で言えば、「100」買い注文したのに「60」しか約定しないといったことがあります。
この場合、BOTは残数の「40」を再度注文します。
つまり、全数約定するように内部的に処理します。
BOTは売りのタイミングを検知したら、
exe_sell_close_order_limit()メソッドを実行して指値の売り注文を出します。
ただし、成行きの買い注文の数量が「20」「30」「50」にように分割されているときは、
売りのポジションIDが3個生成されるのでexe_sell_close_order_limit()メソッドを3回発行します。
指値の売り注文が約定しない状態で午前6を経過するとレバレッジ手数料が発生します。
なので、BOTは午前5~午前6まではレバレッジの取引を保留にします。
レバレッジの手数料を発生させたくないとき、
BOTに午前5時から6時の間に売りポジションを強制的に執行させることも可能です。
この場合、BOTは成行きの売り注文を出します。
指値のときは「FAS: Fill and Store」が適用されるので全数量約定します。
ただし、成行き注文と同様数量が分割されて約定することがあります。
前出の例で言えば、「20」「30」「50」の売り注文したとき、
「50」が「20」と「30」に分割されて約定されることがあります。
これまでの経験だと、リップルで数量を「1000」くらいにすると4~7くらいに数量が分割されて約定されることがあります。
api.py:
###############################################################################################
def exe_buy_order_market_wait(self, qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
if latest_close_price == 0.0: latest_close_price = price
status = 0
execution_id = 'E' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # EMMDD-HHMMSS-999999(milliseconds)
order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # BMMDD-HHMMSS-999999(milliseconds)
position_id = 'P' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # PMMDD-HHMMSS-999999(milliseconds)
position_list = [
{
'executionId': execution_id, # E9999-999999-999999
'fee': '0', # 1 => 0 (leverage trading)
'lossGain': '0',
'orderId': order_id, # B9999-999999-999999
'positionId': position_id, # P9999-999999-999999
'price': latest_close_price, # ★
'settleType': 'OPEN', # 'OPEN' or 'CLOSE'
'side': 'BUY', # 'SELL' or 'BUY'
'size': qty,
'symbol': symbol,
'timestamp': dt.datetime.utcnow()
}
]
df = pd.DataFrame(position_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
msg_code = 'OK'
msg_str = 'Normal Return'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
# end of if not self.coin.real_mode:
### real mode
df = pd.DataFrame()
# exe_buy_order_market(qty: float) -> tuple: return (status, res_order_id, msg_code, msg_str)
status, order_id, msg_code, msg_str = self.exe_buy_order_market(qty) # ★ FAK (Fill and Kill)
# status: 0(ok), 1(Trading margin is insufficient), ?(misc)
# error ?
if status != 0:
return (status, df, msg_code, msg_str) # (1, df, 'msg_code', 'msg_str')
i = 0
while True:
i += 1
if i > self.coin.api_market_timeout_limit: # time out ?
status = 8 # time out
msg_code = 'ERR-888'
msg_str = f'get_price() time out error: loop count={i} '
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return => (8, df, 'ERR-888', 'get_price() time out error:...')
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: return (status, order_status)
status, order_status = self.get_order_status_try(order_id, qty, price)
# status: # 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED, ERROR
if status != 0:
msg_code = 'ERR-999'
msg_str = f"get_order_status({order_id=}) error ▶ {status=}, {order_status=} "
return (status, df, msg_code, msg_str) # error return
### status == 0 : normal return
# exe_buy_order_market() is pending status
if order_status in 'WAITING,MODIFYING,CANCELLING,ORDERED':
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_buy_order_market_wait(): get_order_status({i}) ▶ {status=}, {order_status=} ")
sleep(1) # sleep 1 sec
continue # continue get_order_status_try()
### order_status : EXECUTED or EXPIRED or CANCELED
### get_order_status() ▶ status==0 & order_status in 'EXECUTED(qty==real_qty), EXPIRED(qty > real_qty), CANCELED(real_qty==0)'
sleep(1) # sleep 1 sec
# get_price_try(order_id: str, qty: float, price: float, latest_close_price=0.0, debug1=False, debug2=False) -> tuple: return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
status, df, msg_code, msg_str = self.get_price_try(order_id, qty, price) # qty and price are used for fake mode
# 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_buy_order_market_wait(): get_price({i}) ▶ {status=}, {df.shape=} ")
# error ?
if status != 0:
# empty dataframe ?
if status == 9:
sleep(1) # sleep 1 sec
continue # continue get_price()
# misc error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return (status, df, msg_code, msg_str) # error return
if df.shape[0] == 0:
sleep(1) # sleep 1 sec
continue # continue get_price()
### Normal Return
# -------------------------------------------------------------
# # Column Dtype
# -------------------------------------------------------------
# 0 executionId int64
# 1 fee float64
# 2 lossGain float64
# 3 orderId object(string)
# 4 positionId object(string)
# 5 price float64
# 6 settleType object('OPEN' or 'CLOSE')
# 7 side object('BUY' or 'SELL')
# 8 size float64
# 9 symbol object('BTC')
# 10 timestamp datetime64[ns]
# ------------------------------------------------------------
# real_buy_qty = df.iloc[0]['size']
real_buy_price = df.iloc[0]['price']
# real_buy_fee = df.iloc[0]['fee']
if real_buy_price > 0:
msg_code = 'OK'
msg_str = 'Normal Return'
break # got a real price ? => normal return
sleep(1) # sleep 1 sec
# continue to get_price()
# end of while True:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
# normal return
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
####################################################
def exe_buy_order_market(self, qty: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # Buy MMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test buy order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE MODE{Bcolors.ENDC} ")
return (status, res_order_id, msg_code, msg_str) # (0, 'B9999-999999-999999', 'OK', 'Fake/Test buy order')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/order'
reqBody = {
'symbol': symbol, # Required (BTC, ETH, LTC,...)
'side': 'BUY', # Required (BUY or SELL)
'executionType': 'MARKET', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAK', # FAK (Fill and Kill)
'size': qty_str # Required (1.1234 BTC)
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_buy_order_market() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = ''
msg_code = ''
msg_str = ''
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_buy_order_market({qty=:,.{_q_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-201'
msg_str = msg_list[0].get('message_string') # 'Trading margin is insufficient'
if msg_code == 'ERR-201': # Trading margin is insufficient
pass
else: # misc error
################################################################### debug info
print('-'*100, 'Begin: dump response from exe_buy_order_market()')
print(dict)
print('-'*100, 'End : dump response from exe_buy_order_market()')
################################################################### debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str} ")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '99999', 'OK', 'Normal Return') or (1, '', 'ERR-201', 'Trading margin is insufficient')
##########################################################################################
def exe_sell_close_order_limit(self, position_id: str, qty: float, price: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test sell close order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, res_order_id, msg_code, msg_str) # (0, 'S9999-999999-999999', 'OK', 'Fake/Test sell close order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/closeOrder'
reqBody = {
'symbol': symbol, # Required (BTC_JPY, ETH_JPY, LTC_JPY,...)
'side': 'SELL', # Required (BUY or SELL)
'executionType': 'LIMIT', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAS', # FAS (Fill and Store)
'price': price_str, # Required (9999999 YEN)
'settlePosition': [
{
'positionId': position_id, # 162401271
'size': qty_str # 0.01
}
]
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_sell_close_order_limit() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_sell_close_order_limit({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-???'
msg_str = msg_list[0].get('message_string') # 'Error Message...'
########################################################################## debug info
print('-'*100, 'Begin: dump response from exe_sell_close_order_limit()')
print(dict)
print('-'*100, 'End : dump response from exe_sell_close_order_limit()')
########################################################################## debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-???', 'Error Message')
図6-1はデモプログラムの実行結果です。
ここではApiクラスのexe_buy_order_market_wait()メソッドで成行きの買い注文(XRP_JPY, 注文数=10)を出しています。
成行き注文が約定すると価格(レート)、数量、注文ID、ポジションID等が返されます。
ここでは数量を最小値の「10」にしているので分割されることはありません。
約定した価格に「1.0035」を掛けて売値を計算したら、
exe_sell_close_order_limit()メソッドを発行して指値で売りポジションを決済します。
次にget_order_status_try()メソッドで指値の売り注文のステータスをチェックします。
売り注文が約定したときは「EXECUTED」が返されます。
売り注文が約定したことを確認したらget_price_try()メソッドを発行して約定した価格(レート)、数量、注文ID等を取得します。
ここではまだ約定していないので1分間待って再試行しています。
図6-2は指値の売り注文が約定したときの画面です。
このときは10分くらいで約定しました。
図6-3はGMOコインの画面です。
2円の利益になっています。
数量が「10」なので金額は無視してください。
こちらがデモプログラムのソースコードです。
このデモプログラムはBOTの「買い▶売り」の処理を単純化したものです。
BOTを使用すると、このデモプログラムのような処理を自動的に行います。
api_class_buy_sell_demo.py:
# buy_sell api demo
from time import sleep # import time
import numpy as np
import pandas as pd
from lib.base import Bcolors, Gvar, Coin, Gmo_dataframe
from lib.debug import Debug # dependencies Gvar
from lib.api import Api # dependencies Gvar, Coin
### [1] instantiate Gvar, Coin, Api classes
gvar = Gvar()
gvar.real_mode = True # ★
gvar.callers_method_name = ''
print('-'*210)
print(gvar)
print('-'*210)
coin = Coin()
coin.symbol = 'XRP_JPY' # ★
coin.real_mode = True # ★
coin.trade_type = 'buy_sell' # ★
coin.qty = 10 # ★
coin.qty_decimal = 0
coin.price_decimal = 3
print(coin)
print('-'*210)
api = Api(gvar, coin)
print(api)
print('-'*210)
### [2] get latest market data from GMO Coin
status, mas_df = api.get_crypto_try(page=1, count=100)
mas_df = pd.DataFrame(mas_df) # cast(mas_df)
if status != 0:
print(f"api.get_crypto_try(page=1, count=100) ▶ {status=}, quit... ")
quit()
if mas_df.shape[0] == 0:
print(f"api.get_crypto_try(page=1, count=100) ▶ {mas_df.shape[0]=}, quit... ")
quit()
filter = mas_df['side'] == 'BUY'
buy_mas_df = mas_df[filter]
print(f'{Bcolors.OKGREEN}gmo buy master dataframe: buy_df.tail(1)')
print(buy_mas_df.tail(1))
print(f'{Bcolors.ENDC}', end='')
print('-'*210)
# buy_mas_df:
# ------------------------------------------------------------------------------------
# # Column Dtype
# -----------------------------------------------------------------------------------
# 0 price float64 buy/sell price
# 1 side object 'BUY' or 'SELL'
# 2 size float64
# 3 timestamp datetime64[ns, UTC] ★ UTC descending order
# -----------------------------------------------------------------------------------
### [3] execute buy order market
buy_qty = coin.qty
last_buy_price = buy_mas_df.iloc[-1]['price']
latest_close_price = last_buy_price
# exe_buy_order_market_wait(qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
(status, res_df, msg_code, msg_str) = api.exe_buy_order_market_wait(buy_qty, last_buy_price, latest_close_price) # last_buy_price, latest_close_price are used for fake mode only
res_df = pd.DataFrame(res_df) # cast(df)
if status != 0:
print(f"exe_buy_order_market_wait({buy_qty=:,.0f}, {last_buy_price=:,.3f}, {latest_close_price=:,.3f}) ▶ {status=}, quit... ")
quit()
### print buy order [marker] response dataframe
print('-'*210)
print(f'{Bcolors.OKGREEN}buy order response dataframe: res_df')
print(res_df)
print(f'{Bcolors.ENDC}', end='')
print('-'*210)
### print buy order (market) info
buy_order_id = res_df.iloc[0]['orderId']
position_id = res_df.iloc[0]['positionId']
buy_real_price = res_df.iloc[0]['price']
buy_real_qty = res_df.iloc[0]['size']
print(f".... {Bcolors.HEADER}{buy_order_id=}, {position_id=}, {buy_real_qty=:,.0f}, {buy_real_price=:,.3f}{Bcolors.ENDC} ")
print('-'*210)
### [4] execute sell close order (limit)
sell_position_id = position_id
sell_qty = buy_real_qty
sell_price = buy_real_price * 1.0035 # calculate a sell price
# exe_sell_close_order_limit(position_id: str, qty: float, price: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
(status, sell_order_id, msg_code, msg_str) = api.exe_sell_close_order_limit(sell_position_id, sell_qty, sell_price)
if status != 0:
print(f"exe_sell_close_order_limit({position_id=}, {sell_qty=:,.0f}, {sell_price=:,.3f}) ▶ {status=}, quit... ")
quit()
### [5] get sell close order status
i = 0
while True:
i += 1
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
(status, order_status) = api.get_order_status_try(sell_order_id, sell_qty, sell_price) # sell_qty, sell_price are used for fake mode only
# status: 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED
if status != 0:
print(f"get_order_status_try({sell_order_id=}, {sell_qty=:,.0f}, {sell_price=:,.3f}) ▶ {status=}, quit... ")
quit()
if order_status in 'EXECUTED,EXPIRED,CANCELED': break
# if i > 5: quit()
print(f".... get_order_status_try({sell_order_id=}) ▶ {order_status=}, sleep 1 minute and retry({i=})... ")
sleep(60) # sleep 1 minute
# continue to get order status
# end of while True:
if order_status in 'EXPIRED,CANCELED':
print(f".... get_order_status_try({sell_order_id=}) ▶ {order_status=}, quit... ")
quit()
### [6] get a sell price : sell close order has been executed
i = 0
while True:
i += 1
### get_orderbooks_try() -> tuple: # return (sell_df, buy_df)
(sell_bk_df, buy_bk_df) = api.get_orderbooks_try()
sell_bk_df = pd.DataFrame(sell_bk_df) # 100, 110, 120, 130, 140, 150 (low to high) ascending order of sell price
buy_bk_df = pd.DataFrame(buy_bk_df) # 150, 140, 130, 120, 110, 100 (high to low) descending order of buy price
if sell_bk_df.shape[0] == 0 or buy_bk_df.shape[0] == 0:
print(f"get_orderbooks_try() ▶ {sell_bk_df.shape[0]=}, {buy_bk_df.shape[0]=}, quit... ")
quit()
### get the latest buy price from orderbooks
latest_buy_bk_price = buy_bk_df.iloc[0]['price'] # 150
### get a sell position number
filter_mask = sell_bk_df['price'] < sell_price # < 120
dfx = sell_bk_df[filter_mask] # 100, 110
sell_position = dfx.shape[0] # 2
coin.sell_position_list.append(sell_position) # append sell position [2]
reverse_sell_position_list = coin.sell_position_list[::-1]
print(f".... {reverse_sell_position_list=} ")
### get a sell price
# get_price_try(order_id: str, qty: float, price: float, latest_book_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str)
(status, res_df, msg_code, msg_str) = api.get_price_try(sell_order_id, sell_qty, sell_price, latest_buy_bk_price) # sell_qty, sell_price, latest_buy_bk_price are used for fake mode only
# status: 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
# msg_code: OK, ERR-888, ERR-999
# empty dataframe ?
if status == 9:
print(f".... get_price_try({sell_order_id=}, {sell_qty=:,.0f}, {sell_price=:,.3f}, {latest_buy_bk_price=:,.3f}) ▶ {status=}, sleep 1 minute & retry({i=}) ... ")
print(f".... exe_sell_close_order_limit() close condition [{Bcolors.WARNING}{sell_price=:,.3f} <= {latest_buy_bk_price=:,.3f}{Bcolors.ENDC}] ")
sleep(60) # sleep 1 minute
continue
if status != 0: # 8 or 9 or ?
print(f".... get_price_try({sell_order_id=}, {sell_qty=:,.0f}, {sell_price=:,.3f}, {latest_buy_bk_price=:,.3f}) ▶ {status=}, quit... ")
quit()
break # if normal return then exit
# end of while True:
### [7] get a sell price and print it
res_df = pd.DataFrame(res_df) # cast res_df
print('-'*210)
print(f'{Bcolors.OKGREEN}get price response dataframe: res_df')
print(res_df)
print(f'{Bcolors.ENDC}', end='')
print('-'*210)
sell_order_id = res_df.iloc[0]['orderId']
sell_real_qty = res_df.iloc[0]['size']
sell_real_price = res_df.iloc[0]['price']
print(f".... {Bcolors.HEADER}{sell_order_id=}, {sell_real_qty=:,.0f}, {sell_real_price=:,.3f}{Bcolors.ENDC} ")
print('-'*210)
Apiクラスにexe_sell_order_market_wait(), exe_sell_order_market(), exe_buy_close_order_limit()メソッドを追加する
ここでは、Coinクラスのtran_typeプロパティに「sell_buy」を設定したときに使用されるApiのメソッドを追加します。
「空売り▶買戻し」の注文を行う場合、BOTは空売りの成行き注文を行って約定したら買い戻しの注文を指値で行います。
exe_sell_order_market_wait()は親メソッドで、exe_sell_order_market()は子メソッドです。
親メソッドでは空売り成行き注文が約定するまで一定時間待ちます。
そして約定したら約定情報をPandasのDataFrameに格納して返します。
BOTは買い戻しのタイミングを検知したら、 exe_buy_close_order_limit()メソッドを実行して指値の買い注文を出します。
api.py:
################################################################################################
def exe_sell_order_market_wait(self, qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
if latest_close_price == 0.0: latest_close_price = price
status = 0
execution_id = 'E' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # EMMDD-HHMMSS-999999(milliseconds)
order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
position_id = 'P' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # PMMDD-HHMMSS-999999(milliseconds) ★
position_list = [
{
'executionId': execution_id, # E9999-999999-999999
'fee': '0', # 1 => 0 (leverage traing)
'lossGain': '0',
'orderId': order_id, # S9999-999999-999999
'positionId': position_id, # P9999-999999-999999 ★
'price': latest_close_price, # ★
'settleType': 'OPEN', # 'OPEN' or 'CLOSE'
'side': 'SELL', # 'SELL' or 'BUY'
'size': qty,
'symbol': symbol,
'timestamp': dt.datetime.utcnow()
}
]
df = pd.DataFrame(position_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string ★
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
msg_code = 'OK'
msg_str = 'Normal Return'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC} ")
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
# end of if not self.coin.real_mode:
### real mode
df = pd.DataFrame()
status, order_id, msg_code, msg_str = self.exe_sell_order_market(qty) # ★ FAK (Fill and Kill)
# error ?
if status != 0:
return status, df, msg_code, msg_str # (1, df, 'msg_code', 'msg_str')
i = 0
while True:
i += 1
if i > self.coin.api_market_timeout_limit: # time out ?
status = 8 # time out
msg_code = 'ERR-888'
msg_str = f'get_price() time out error: loop count={i} '
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return => (8, df, 'ERR-888', 'get_price() time out error:...')
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
status, order_status = self.get_order_status_try(order_id, qty, price)
# status: # 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED, ERROR
if status != 0:
msg_code = 'ERR-999'
msg_str = f"get_order_status({order_id=}) error ▶ {status=}, {order_status=} "
return status, df, msg_code, msg_str # error return
### status == 0
# exe_sell_order_market() is pending status
if order_status in 'WAITING,MODIFYING,CANCELLING,ORDERED':
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_sell_order_market_wait(): get_order_status({i}) ▶ {status=}, {order_status=} ")
sleep(1) # sleep 1 sec
continue # continue get_order_status()
### order_staus : EXECUTED or EXPIRED or CANCELED
### get_order_status() ▶ status==0 & order_status in 'EXECUTED,EXPIRED,CANCELED'
sleep(1) # sleep 1 sec
# get_price_try(order_id: str, qty: float, price: float, latest_close_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
status, df, msg_code, msg_str = self.get_price_try(order_id, qty, price) # qty and price are used for fake mode
# 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_sell_order_market_wait(): get_price({i}) ▶ {status=}, {df.shape=} ")
# error ?
if status != 0:
# DataFrame is empty ?
if status == 9:
sleep(1) # sleep 1 sec
continue # continue get_price()
# misc error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return
if df.shape[0] == 0:
sleep(1) # sleep 1 sec
continue # continue get_price()
### Normal Return
# -----------------------------------------------------
# # Column Dtype
# -----------------------------------------------------
# 0 executionId int64
# 1 fee float64
# 2 lossGain float64
# 3 orderId object(string)
# 4 positionId object(string)
# 5 price float64
# 6 settleType object('OPEN' or 'CLOSE')
# 7 side object('BUY' or 'SELL')
# 8 size float64
# 9 symbol object('BTC_JPY')
# 10 timestamp datetime64[ns]
# ----------------------------------------------------
# real_sell_qty = df.iloc[0]['size']
real_sell_price = df.iloc[0]['price']
# real_sell_fee = df.iloc[0]['fee']
if real_sell_price > 0:
msg_code = 'OK'
msg_str = 'Normal Return'
break # got a real price ?
sleep(1) # sleep 1 sec
# continue to get_order_status_try()
# end of while True:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str}")
# normal or time out error return
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
#####################################################
def exe_sell_order_market(self, qty: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test sell order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE MODE{Bcolors.ENDC} ")
return (status, res_order_id, msg_code, msg_str) # (0, 'S9999-999999-999999', 'OK', 'Fake\Test sell order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/order'
reqBody = {
'symbol': symbol, # Required (BTC, ETH, LTC,...)
'side': 'SELL', # Required (BUY or SELL)
'executionType': 'MARKET', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAK', # FAK (Fill and Kill)
'size': qty_str # Required (1 BTC)
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_sell_order_market() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status') # int type
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_sell_order_market({qty=:,.{_q_}f}): ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # ERR-208
msg_str = msg_list[0].get('message_string') # Exceeds the available balance
if msg_code == 'ERR-208': # Exceeds the available balance
pass
else:
##################################################################### debug info
print('-'*100, 'Begin: dump response from exe_sell_order_market()')
print(dict) # temp temp
print('-'*100, 'End : dump response from exe_sell_order_market()')
##################################################################### debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-201', 'Exceeds the available balance')
########################################################################################
def exe_buy_close_order_limit(self, position_id: str, qty: float, price: float) ->tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # BMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test buy close order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, res_order_id, msg_code, msg_str) # (0, 'B9999-999999-999999', 'OK', 'Fake/Test close order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/closeOrder'
reqBody = {
'symbol': symbol, # Required (BTC_JPY, ETH_JPY, LTC_JPY,...)
'side': 'BUY', # Required (BUY or SELL)
'executionType': 'LIMIT', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAS', # FAK (Fill and Store)
'price': price_str, # Required (9999999 YEN)
'settlePosition': [
{
'positionId': position_id, # 162401271
'size': qty_str # 0.01
}
]
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_buy_close_order_limit() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_buy_close_order_limit({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-???'
msg_str = msg_list[0].get('message_string') # 'Error Message...'
######################################################################### debug info
print('-'*100, 'Begin: dump response from exe_buy_close_order_limit()')
print(dict)
print('-'*100, 'End : dump response from exe_buy_close_order_limit()')
######################################################################### debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-???', 'Error Message')
図7-1はデモプログラムの実行結果です。
ここではApiクラスのexe_sell_order_market_wait()メソッドで成行きの空売り注文(XRP_JPY, 注文数=10)を出しています。
成行き注文が約定すると価格(レート)、数量、注文ID、ポジションID等が返されます。
ここでは数量を最小値の「10」にしているので分割されることはありません。
約定した価格に「0.0065」を掛けて買値を計算したら、
exe_buy_close_order_limit()メソッドを発行して指値で買いポジションを決済します。
次にget_order_status_try()メソッドで指値の買い注文のステータスをチェックします。
買い注文が約定したときは「EXECUTED」が返されます。
買い注文が約定したことを確認したらget_price_try()メソッドを発行して約定した価格(レート)、数量、注文ID等を取得します。
ここではまだ約定していないので1分間待って再試行しています。
図7-2は指値の売り注文が約定したときの画面です。 このときは15分くらいで約定しました。
図7-3はGMOコインの画面です。 2円の利益になっています。
注文数量が「10」なので金額は無視してください。
こちらがデモプログラムのソースコードです。
このデモプログラムはBOTの「空売り▶買戻し」の処理を単純化したものです。
BOTを使用すると、このデモプログラムのような処理を自動的に行います。
api_class_sell_buy_demo.py:
# sell_buy api demo
from time import sleep # import time
import numpy as np
import pandas as pd
from lib.base import Bcolors, Gvar, Coin, Gmo_dataframe
from lib.debug import Debug # dependencies Gvar
from lib.api import Api # dependencies Gvar, Coin
### [1] instantiate Gvar, Coin, Api classes
gvar = Gvar()
gvar.real_mode = True # ★
gvar.callers_method_name = ''
print('-'*210)
print(gvar)
print('-'*210)
coin = Coin()
coin.symbol = 'XRP_JPY' # ★
coin.real_mode = True # ★
coin.trade_type = 'sell_buy' # ★
coin.qty = 10 # ★
coin.qty_decimal = 0
coin.price_decimal = 3
print(coin)
print('-'*210)
api = Api(gvar, coin)
print(api)
print('-'*210)
### [2] get latest market data from GMO Coin
status, mas_df = api.get_crypto_try(page=1, count=100)
mas_df = pd.DataFrame(mas_df) # cast(mas_df)
if status != 0:
print(f"api.get_crypto_try(page=1, count=100) ▶ {status=}, quit... ")
quit()
if mas_df.shape[0] == 0:
print(f"api.get_crypto_try(page=1, count=100) ▶ {mas_df.shape[0]=}, quit... ")
quit()
filter = mas_df['side'] == 'SELL'
sell_mas_df = mas_df[filter]
print(f'{Bcolors.OKGREEN}gmo sell master dataframe: sell_df.tail(1)')
print(sell_mas_df.tail(1))
print(f'{Bcolors.ENDC}', end='')
print('-'*210)
# sell_mas_df:
# ------------------------------------------------------------------------------------
# # Column Dtype
# -----------------------------------------------------------------------------------
# 0 price float64 buy/sell price
# 1 side object 'BUY' or 'SELL'
# 2 size float64
# 3 timestamp datetime64[ns, UTC] ★ UTC descending order
# -----------------------------------------------------------------------------------
### [3] execute sell order (market)
sell_qty = coin.qty
last_sell_price = sell_mas_df.iloc[-1]['price']
latest_close_price = last_sell_price
# exe_sell_order_market_wait(qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
(status, res_df, msg_code, msg_str) = api.exe_sell_order_market_wait(sell_qty, last_sell_price, latest_close_price) # last_sell_price, latest_close_price are used for fake mode only
res_df = pd.DataFrame(res_df) # cast(df)
if status != 0:
print(f"exe_sell_order_market_wait({sell_qty=:,.0f}, {last_sell_price=:,.3f}, {latest_close_price=:,.3f}) ▶ {status=}, quit... ")
quit()
### print executed sell order response dataframe
print('-'*210)
print(f'{Bcolors.OKGREEN}sell order response dataframe: res_df')
print(res_df)
print(f'{Bcolors.ENDC}', end='')
print('-'*210)
### print sell order (market) info
sell_order_id = res_df.iloc[0]['orderId']
position_id = res_df.iloc[0]['positionId']
sell_real_price = res_df.iloc[0]['price']
sell_real_qty = res_df.iloc[0]['size']
print(f".... {Bcolors.HEADER}{sell_order_id=}, {position_id=}, {sell_real_qty=:,.0f}, {sell_real_price=:,.3f}{Bcolors.ENDC} ")
print('-'*210)
### [4] execute buy close order (limit)
buy_position_id = position_id
buy_qty = sell_real_qty
buy_price = sell_real_price - (sell_real_price * 0.0035) # calculate buy price
# exe_buy_close_order_limit(position_id: str, qty: float, price: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
(status, buy_order_id, msg_code, msg_str) = api.exe_buy_close_order_limit(buy_position_id, buy_qty, buy_price)
if status != 0:
print(f"exe_buy_close_order_limit({position_id=}, {buy_qty=:,.0f}, {buy_price=:,.3f}) ▶ {status=}, quit... ")
quit()
### [5] get buy close order status
i = 0
while True:
i += 1
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
(status, order_status) = api.get_order_status_try(buy_order_id, buy_qty, buy_price)
# status: 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED
if status != 0:
print(f"get_order_status_try({buy_order_id=}, {buy_qty=:,.0f}, {buy_price=:,.3f}) ▶ {status=}, quit... ")
quit()
if order_status in 'EXECUTED,EXPIRED,CANCELED': break
print(f".... get_order_status_try({buy_order_id=}) ▶ {order_status=}, sleep 1 minute and retry({i=}) ... ")
sleep(60) # sleep 1 minute
# if i > 5: quit()
# continue to get order status
# end of while True:
if order_status in 'EXPIRED,CANCELED':
print(f".... get_order_status_try({buy_order_id=}) ▶ {order_status=}, quit... ")
quit()
### [6] get a buy price : buy order has been executed
i = 0
while True:
### get_orderbooks_try() -> tuple: # return (sell_df, buy_df)
(sell_bk_df, buy_bk_df) = api.get_orderbooks_try()
sell_bk_df = pd.DataFrame(sell_bk_df) # 100, 110, 120, 130, 140, 150 (low to high) ascending order of sell price
buy_bk_df = pd.DataFrame(buy_bk_df) # 150, 140, 130, 120, 110, 100 (high to low) descending order of buy price
if sell_bk_df.shape[0] == 0 or buy_bk_df.shape[0] == 0:
print(f"get_orderbooks_try() ▶ {sell_bk_df.shape[0]=}, {buy_bk_df.shape[0]=}, quit... ")
quit()
### get the latest sell price from orderbooks
latest_sell_bk_price = sell_bk_df.iloc[-1]['price'] # 150
### get a buy position number
buy_bk_df.sort_values('price', ascending=True, inplace=True) # sort in ascending order of price
filter_mask = buy_bk_df['price'] > buy_price # > 120
dfx = buy_bk_df[filter_mask] # 130, 140, 150
buy_position = dfx.shape[0] # 3
coin.buy_position_list.append(buy_position) # append buy position [3]
reverse_buy_position_list = coin.buy_position_list[::-1]
print(f".... {reverse_buy_position_list=} ")
# get_price_try(order_id: str, qty: float, price: float, latest_book_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str)
(status, res_df, msg_code, msg_str) = api.get_price_try(buy_order_id, buy_qty, buy_price, latest_sell_bk_price)
# status: 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
# msg_code: OK, ERR-888, ERR-999
# empty dataframe ?
if status == 9:
print(f".... get_price_try({buy_order_id=}, {buy_qty=:,.0f}, {buy_price=:,.3f}, {latest_sell_bk_price=:,.3f}) ▶ {status=}, sleep 1 minute & retry({i=}) ... ")
print(f".... exe_buy_close_order_limit() close condition [{Bcolors.WARNING}{buy_price=:,.3f} >= {latest_sell_bk_price=:,.3f}{Bcolors.ENDC}] ")
sleep(60) # sleep 1 minute
continue
if status != 0: # 8 or 9 or ?
print(f".... get_price_try({buy_order_id=}, {buy_qty=:,.0f}, {buy_price=:,.3f}, {latest_sell_bk_price=:,.3f}) ▶ {status=}, quit... ")
quit()
break # if normal return then exit
# end of while True:
### [7] get a buy price and print it
res_df = pd.DataFrame(res_df) # cast res_df
print('-'*210)
print(f'{Bcolors.OKGREEN}get price response dataframe: res_df')
print(res_df)
print(f'{Bcolors.ENDC}', end='')
print('-'*210)
buy_order_id = res_df.iloc[0]['orderId']
buy_real_qty = res_df.iloc[0]['size']
buy_real_price = res_df.iloc[0]['price']
print(f".... {Bcolors.HEADER}{buy_order_id=}, {buy_real_qty=:,.0f}, {buy_real_price=:,.3f}{Bcolors.ENDC} ")
print('-'*210)
Apiクラスの全てを掲載
ここではApiクラスの全てのソースコードを掲載しています。
api.py:
# api.py
"""
Api class
status = 0, 4, 5, 8, 9, 999
0: normal return
4: invalid request (requests are too many)
5: maintenance return
8: retry return
9: empty dataframe return
999: misc error return
"""
# import the libraries
import os
import os.path
import time
from time import sleep
import datetime as dt
import numpy as np
import pandas as pd
# api
import hmac
import hashlib
import requests
import json
from lib.base import Bcolors, Gvar, Coin, Gmo_dataframe
from lib.debug import Debug
from lib.trade import Trade
import warnings
warnings.simplefilter('ignore')
########## Api class
class Api:
"""
__init__(self), __init__(self, gvar, coin)
load_master_driver() => load_master_data() => call get_crypto_try() => get_crypto()
get_assets() => exe_assets()
get_crypto_try() => get_crypto()
get_orderbooks_try() => get_orderbooks()
get_order_status_try() => get_order_status(), get_price_try() => get_price()
exe_buy_order_market_wait() => exe_buy_order_market(), exe_buy_order_limit()
exe_sell_order_market_wait() => exe_sell_order_market(), exe_sell_order_limit()
exe_buy_close_order_market_wait() => exe_buy_close_order_market(), exe_buy_close_order_limit()
exe_sell_close_order_market_wait() => exe_sell_close_order_market(), exe_sell_close_order_limit()
exe_cancel_order()
"""
###########################
def __init__(self) -> None:
self.gvar = Gvar() # Gvar
self.coin = Coin() # Coin
self.debug = Debug(self.gvar) # dependent class
self.trade = Trade(self.gvar, self.coin) # dependent class
self.folder_gmo = f'{self.gvar.domain}/{self.gvar.exchange}/' # desktop-pc/bot/
self.folder_trading_type = f'{self.gvar.domain}/{self.gvar.exchange}/{self.coin.trade_type}/'
# desktop-pc/bot/buy_sell/
# desktop-pc/bot/sell_buy/
### GET GMO API-key / Secret Key
self.api_key = os.environ.get('GMO_API_KEY')
self.api_secret = os.environ.get('GMO_SECRET_KEY')
#######################################################
def __init__(self, gvar: object, coin: object) -> None:
self.gvar = gvar # Gvar
self.coin = coin # Coin
self.debug = Debug(gvar) # dependent class
self.trade = Trade(gvar, coin) # dependent class
self.folder_gmo = f'{self.gvar.domain}/{self.gvar.exchange}/' # desktop-pc/bot/
self.folder_trading_type = f'{self.gvar.domain}/{self.gvar.exchange}/{self.coin.trade_type}/'
# desktop-pc/bot/buy_sell/
# desktop-pc/bot/sell_buy/
### GET GMO API-key / Secret Key
self.api_key = os.environ.get('GMO_API_KEY')
self.api_secret = os.environ.get('GMO_SECRET_KEY')
#########################
def __str__(self) -> str:
return f"Api({self.folder_gmo=}, {self.folder_trading_type=}, self.api_key=●●●, self.api_secret=●●●)"
########################
def x_get_api_key(self):
return self.api_key
###########################
def x_get_api_secret(self):
return self.api_secret
#########################################################
def get_crypto_try(self, page: int, count: int) -> tuple: # return (status, df)
retry_count = 0
# retry N times if invalid or connection error or time out errors
while True:
status, df = self.get_crypto(page, count) # => return (status, df)
# invalid request (requests are too many), connection error, time out error ?
if status == 4 or status == 8:
if retry_count < self.coin.api_retry_limit_count:
retry_count += 1
sec = np.random.randint(low=3, high=61, size=(1))[0] # get random number between 3~61 seconds
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}due to get_crypto({status=}) error sleeping {sec} seconds :{Bcolors.ENDC} ▶ {retry_count=} ")
sleep(sec) # sleep 3~61 seconds
continue
break # exit while loop
# end of while True:
return (status, df)
#####################################################
def get_crypto(self, page: int, count: int) -> tuple: # return (status, df)
symbol = self.coin.symbol
page_str = str(page)
count_str = str(count)
endPoint = 'https://api.coin.z.com/public'
path = f'/v1/trades?symbol={symbol}&page={page_str}&count={count_str}'
url = endPoint + path
status = 999 # misc error return
while True:
try:
res_dict = requests.get(url, timeout=3) # timeout=3 sec
except requests.exceptions.HTTPError as errh:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}HTTP ERROR:{Bcolors.ENDC} get_crypto() ▶ request.get(): {errh} ")
self.debug.sound_alert(3)
df = pd.DataFrame() # return empty dataframe
status = 999 # misc error
break # error return
except requests.exceptions.ConnectionError as errc:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}CONNECTION ERROR:{Bcolors.ENDC} get_crypto() ▶ request.get(): {errc} ")
self.debug.sound_alert(3)
df = pd.DataFrame() # return empty dataframe
status = 8 # retry error
break # error return
except requests.exceptions.Timeout as errt:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}TIMEOUT ERROR:{Bcolors.ENDC} get_crypto() ▶ request.get(): {errt} ")
self.debug.sound_alert(3)
df = pd.DataFrame() # return empty dataframe
status = 8 # retry error
break # error return
except requests.exceptions.RequestException as err:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}EXCEPTION ERROR:{Bcolors.ENDC} get_crypto() ▶ request.get(): {err} " )
self.debug.sound_alert(3)
df = pd.DataFrame() # return empty dataframe
status = 999 # misc error
break # error return
except: # block lets you handle the error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC EXCEPTION ERROR:{Bcolors.ENDC} get_crypto() ▶ return ...")
self.debug.sound_alert(3)
df = pd.DataFrame() # return empty dataframe
status = 999 # misc error
break # error return
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try and except block
pass
# end of try except else finally
dict = res_dict.json()
# invalid url
# {'message': 'Not Found', 'statusCode': 404}
statusCode = dict.get('statusCode')
if statusCode == 404:
message = dict.get('message')
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}URL NOT FOUND ERROR:{Bcolors.ENDC} get_crypto() ▶ requests.get({url=}) : {statusCode=}, {message=} ")
self.debug.sound_alert(3)
quit()
### Normal Case
# {'status': 0, 'data': {'list': [{'price': '4480940', 'side': 'BUY', 'size': '0.0109', 'timestamp': '2022-02-27T08:15:09.241Z'},...
################################################### debug info
# print('-'*100, 'dump response from get_crypto()')
# print(dict)
# print('-'*100, 'dump response from get_crypto()')
################################################### debug info
status = dict.get('status')
# no error ?
if status == 0:
data_dict = dict.get('data')
data_list = data_dict.get('list')
data_pagination = data_dict.get('pagination')
df = pd.DataFrame(data_list)
if df.shape[0] > 0:
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (ware) ★ errors='coerce' => NaT or errors='ignore'
df = df.astype({'price': 'float', 'size': 'float'})
break # normal return
# # Column Dtype
# ----------------------------------------------------------------
# 0 price float64
# 1 side object 'BUY' or 'SELL'
# 2 size float64
# 3 timestamp datetime64[ns, UTC] ★ UTC descending order
# ----------------------------------------------------------------
else: # error return : status == 5 or 4 or 1 or ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code')
msg_str = msg_list[0].get('message_string')
if status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} get_crypto({symbol}, {page}, {count}) ▶ {status=}, {msg_code} - {msg_str} quit python...")
self.debug.sound_alert(3)
quit()
elif status == 4 and msg_code == 'ERR-5003': # Invalid Request: ERR-5003 Requests are too many
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}Invalid Request():{Bcolors.ENDC} get_crypto({symbol}, {page}, {count}) ▶ {status=}, {msg_code} - {msg_str} wait 1 minute and retry...")
df = pd.DataFrame() # return empty dataframe
break # error return
else: # status == ?
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}Invalid Request:{Bcolors.ENDC} get_crypto({symbol}, {page}, {count}): {status=}, {msg_code} - {msg_str} error return with empty dataframe... ")
################################################# debug info
print('-'*100, 'dump response from get_crypto()')
print(dict)
print('-'*100, 'dump response from get_crypto()')
################################################# debug info
df = pd.DataFrame() # return empty dataframe
break # error return
# end of if status == 0:
# end of while True:
#trace_write(f".... get_crpto('{symbol}', page='{page}', count='{count}'): ▶ {df.shape[0]} ")
return (status, df)
######################################
def get_orderbooks_try(self) -> tuple: # return (sell_df, buy_df)
retry_count = 0
# retry N times if invalid or connection or time out errors
while True:
status, sell_df, buy_df = self.get_orderbooks() # => return (status, sell_df, buy_df)
# invalid request (requests are too many), connection error, time out error ?
if status == 4 or status == 8:
if retry_count < self.coin.api_retry_limit_count:
retry_count += 1
sec = np.random.randint(low=3, high=61, size=(1))[0] # get random number between 3~61 seconds
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}due to get_orderbooks({status=}) error sleeping {sec} seconds :{Bcolors.ENDC} ▶ {retry_count=} ")
sleep(sec) # sleep 3~61 seconds
continue
break # exit while loop
# end of while True:
return (sell_df, buy_df)
##################################
def get_orderbooks(self) -> tuple: # return (status, sell_df, buy_df)
symbol = self.coin.symbol
endPoint = 'https://api.coin.z.com/public'
path = f'/v1/orderbooks?symbol={symbol}'
url = endPoint + path
status = 999 # misc error
while True:
try:
res_dict = requests.get(url, timeout=3) # 3 seconds # timeout=0.001 sec
except requests.exceptions.HTTPError as errh:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}HTTP ERROR:{Bcolors.ENDC} get_orderbooks() ▶ request.get(): {errh} ")
self.debug.sound_alert(3)
status = 999 # misc error
sell_df = pd.DataFrame() # return empty dataframe
buy_df = pd.DataFrame() # return empty dataframe
break # return with empty dataframe
except requests.exceptions.ConnectionError as errc:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}CONNECTION ERROR:{Bcolors.ENDC} get_orderbooks() ▶ request.get(): {errc} ")
self.debug.sound_alert(3)
status = 8 # retry error
sell_df = pd.DataFrame() # return empty dataframe
buy_df = pd.DataFrame() # return empty dataframe
break # return with empty dataframe
except requests.exceptions.Timeout as errt:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}TIMEOUT ERROR:{Bcolors.ENDC} get_orderbooks() ▶ request.get(): {errt} ")
self.debug.sound_alert(3)
status = 8 # retry error
sell_df = pd.DataFrame() # return empty dataframe
buy_df = pd.DataFrame() # return empty dataframe
break # return with empty dataframe
except requests.exceptions.RequestException as err:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}EXCEPTION ERROR:{Bcolors.ENDC} get_orderbooks() ▶ request.get(): {err} " )
self.debug.sound_alert(3)
status = 999 # misc error
sell_df = pd.DataFrame() # return empty dataframe
buy_df = pd.DataFrame() # return empty dataframe
break # return with empty dataframe
except: # block lets you handle the error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC EXCEPTION ERROR:{Bcolors.ENDC} get_orderbooks() ▶ return...")
self.debug.sound_alert(3)
status = 999 # misc error
sell_df = pd.DataFrame() # return empty dataframe
buy_df = pd.DataFrame() # return empty dataframe
break # return with empty dataframe
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
# end of try except else finally
dict = res_dict.json()
# {'status': 0,
# 'data': {
# 'asks': [{'price': '4042000', 'size': '0.0005'},
# {'price': '4045000', 'size': '0.0028'},
# {'price': '4045018', 'size': '0.0001'},
# {'price': '4049000', 'size': '0.001'},
# {'price': '4049310', 'size': '0.0329'},
# {'price': '4049520', 'size': '0.0125'},
# {'price': 5381822.0, 'size': '0.001'}],
# 'bids': [{'price': 5013050.0, 'size': '0.0749'},
# {'price': 5013000.0, 'size': '0.0015'},
# {'price': 5012425.0, 'size': '0.05'},
# {'price': 5012420.0, 'size': '0.05'},
# {'price': 4740000.0, 'size': '0.2135'}],
# 'symbol': 'BTC'},
### Normal Case
####################################################### debug info
# print('-'*100, 'dump response from get_orderbooks()')
# print(dict)
# print('-'*100, 'dump response from get_orderbooks()')
####################################################### debug info
status = dict.get('status')
if status == 0:
data_dict = dict.get('data')
asks = data_dict.get('asks') # sell order info (ascending order)
bids = data_dict.get('bids') # buy order info (descending order)
sell_df = pd.DataFrame(asks)
buy_df = pd.DataFrame(bids)
# {'asks': [{'price': '4064331', 'size': '0.0001'},
# {'price': '4069888', 'size': '0.0309'},
# {'price': '4070000', 'size': '0.0073'},
# {'price': '4070620', 'size': '0.001'},
# {'price': 5381822.0, 'size': '0.001'}],
# 'bids': [{'price': 5013050.0, 'size': '0.0749'},
# {'price': 5013000.0, 'size': '0.0015'},
# {'price': 5012425.0, 'size': '0.05'},
# {'price': 5012420.0, 'size': '0.05'},
# {'price': 4740000.0, 'size': '0.2135'}],
if sell_df.shape[0] > 0:
# convert data types
sell_df = sell_df.astype({'price': 'float', 'size': 'float'})
if buy_df.shape[0] > 0:
# convert data types
buy_df = buy_df.astype({'price': 'float', 'size': 'float'})
else: # error return
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code')
msg_str = msg_list[0].get('message_string')
if status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} get_orderbooks({symbol}) ▶ {status=}, {msg_code} - {msg_str} quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}Invalid Request:{Bcolors.ENDC} get_orderbooks({symbol}) ▶ {status=}, {msg_code} - {msg_str} error return with empty dataframe... ")
###################################################### debug info
print('-'*100, 'dump response from get_orderbooks()')
print(dict)
print('-'*100, 'dump response from get_orderbooks()')
##################################################### debug info
sell_df = pd.DataFrame() # return empty dataframe
buy_df = pd.DataFrame() # return empty dataframe
# end if if status == 0:
break
# end of while true:
#trace_write(f".... get_orderbooks('{symbol}'): ▶ {sell_df.shape[0]}, {buy_df.shape[0]} ")
return (status, sell_df, buy_df) # status, sell_df(ascending order), buy_df(descending order)
################################################################################
def get_order_status_try(self, order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
retry_count = 0
# retry N times if invalid or connection or time out errors
while True:
status, order_status = self.get_order_status(order_id, qty, price) # return (status, order_status)
# status: 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED
# invalid request (requests are too many), connection error, time out error ?
if status == 4 or status == 8:
if retry_count < self.coin.api_retry_limit_count:
retry_count += 1
sec = np.random.randint(low=3, high=61, size=(1))[0] # get random number between 3~61 seconds
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}due to get_order_status({status=}) error sleeping {sec} seconds :{Bcolors.ENDC} ▶ {retry_count=} ")
sleep(sec) # sleep 3~61 seconds
continue
break # exit while loop
# end of while True:
return (status, order_status) # 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
############################################################################
def get_order_status(self, order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
if not self.coin.real_mode:
status = 0
order_status = 'EXECUTED'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_order_status{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_status=} {Bcolors.FAIL}FAKE{Bcolors.ENDC} ")
return (status, order_status)
# end of if not self.coin.real_mode:
### real mode
status = 9 # misc rrror
while True:
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'GET'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/orders'
url = timestamp + method + path
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
parameters = {
'orderId': order_id # '2478774888' : DO NOT USE multiple orderId '2478774888','2478774999' max 10 orderId
}
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res_dict = requests.get(endPoint + path, headers=headers, params=parameters, timeout=3)
except requests.exceptions.HTTPError as errh:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}HTTP ERROR:{Bcolors.ENDC} get_order_status({symbol},{order_id}) ▶ request.get(): {errh} ")
self.debug.sound_alert(3)
status = 9 # misc error
order_status = 'ERROR' # http error
break # error return
except requests.exceptions.ConnectionError as errc:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}CONNECTION ERROR:{Bcolors.ENDC} get_order_status({symbol},{order_id}) ▶ request.get(): {errc} ")
status = 8 # retry error
order_status = 'ERROR' # connection error
break # error return
except requests.exceptions.Timeout as errt:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}TIMEOUT ERROR:{Bcolors.ENDC} get_order_status({symbol},{order_id}) ▶ request.get(): {errt} ")
self.debug.sound_alert(3)
status = 8 # retry error
order_status = 'ERROR' # timeout error
break # error return
except requests.exceptions.RequestException as err:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}EXCEPTION ERROR:{Bcolors.ENDC} get_order_status({symbol},{order_id}) ▶ request.get(): {err} " )
self.debug.sound_alert(3)
status = 9 # misc error
order_status = 'ERROR' # exception error
break # error return
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC EXCEPTION ERROR:{Bcolors.ENDC} get_order_status({symbol},{order_id}) ▶ return...")
self.debug.sound_alert(3)
status = 9 # misc error
order_status = 'ERROR' # misc exception error
break # error return
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res_dict.json()
status = dict.get('status') # 0 or 1
# {
# "status": 0,
# "data": {
# "list": [
# {
# "executedSize": "1000",
# "executionType": "MARKET",
# "losscutPrice": "0",
# "orderId": 2478774888,
# "orderType": "NORMAL",
# "price": "0",
# "rootOrderId": 2478774888,
# "settleType": "OPEN",
# "side": "BUY",
# "size": "1000",
# "status": "EXECUTED", # WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED
# "symbol": "XRP_JPY",
# "timeInForce": "FAK",
# "timestamp": "2022-05-13T05:27:46.042Z"
# }
# ]
# },
# "responsetime": "2022-05-26T00:23:48.767Z"
# }
# normal return
if status == 0:
data = dict.get('data')
if len(data) == 0:
df = pd.DataFrame()
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_order_status{Bcolors.ENDC}({order_id=}): ▶ {status=}, {df.shape[0]=} ")
order_status = 'EXPIRED'
break # return (status, order_status)
data_list = data.get('list')
df = pd.DataFrame(data_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True) # UTC (aware)
# 'executedSize': "1000" => float
# 'losscutPrice': "0" => float
# 'orderId": 2478774888 => object
# 'price': "0" => float
# 'rootOrderId': 2478774888 => object
# 'size': "1000" => float
df = df.astype(
{
'executedSize': 'float', 'losscutPrice': 'float',
'orderId': 'object', 'price': 'float',
'rootOrderId': 'object', 'size': 'float'
}
)
# Data columns (total 14 columns):
# --------------------------------------------------------------
# # Column Dtype
# ---------------------------------------------------------------
# 0 executedSize float64
# 1 executionType object
# 2 losscutPrice float64
# 3 orderId object
# 4 orderType object
# 5 price float64
# 6 rootOrderId object
# 7 settleType object
# 8 side object
# 9 size float64
# 10 status object
# 11 symbol object
# 12 timeInForce object
# 13 timestamp datetime64[ns, UTC]
# ------------------------------------------------------------------
if df.shape[0] == 0:
order_status = 'EXPIRED'
else:
order_status = df.loc[0, 'status'] # WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_order_status{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_status=} ")
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} get_order_status({order_id=}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code')
msg_str = msg_list[0].get('message_string')
################################################################## debug info
print('-'*100, 'Begin: dump response from get_order_status()')
print(dict) # temp temp
print('-'*100, 'End : dump response from get_order_status()')
################################################################## debug info
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_order_status{Bcolors.ENDC}({order_id=}): ▶ {status=}, {msg_code} - {msg_str} ")
order_status = 'ERROR'
# end of if status == 0:
break
# end of while true:
return (status, order_status) # 0(ok), 4(invalid request), 8(retry error), 9(misc error)
#############################################################################################################################
def get_price_try(self, order_id: str, qty: float, price: float, latest_book_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
retry_count = 0
# retry N times if invalid or connection or time out errors
while True:
status, df, msg_code, msg_str = self.get_price(order_id, qty, price, latest_book_price, debug1, debug2) # return (status, order_status)
# status: 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
# msg_code: OK, ERR-888, ERR-999
# connection error, time out error ?
if status == 8:
if retry_count < self.coin.api_retry_limit_count:
retry_count += 1
sec = np.random.randint(low=3, high=61, size=(1))[0] # get random number between 3~61 seconds
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}due to get_price({status=}) error sleeping {sec} seconds :{Bcolors.ENDC} ▶ {retry_count=} ")
sleep(sec) # sleep 3~61 seconds
continue
break # exit while loop
# end of while True:
return (status, df, msg_code, msg_str) # 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
#########################################################################################################################
def get_price(self, order_id: str, qty: float, price: float, latest_book_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
if not self.coin.real_mode:
if latest_book_price == 0.0: latest_book_price = price
buy_sell = True if self.coin.trade_type == 'buy_sell' else False
return_empty_df = True
if buy_sell:
if debug1 or debug2:
return_empty_df = False
else: # neither debug1 nor debug2
# skip empty position list
if len(self.coin.sell_position_list) > 0:
# first sell position ?
if self.coin.sell_position_list[-1] == 0:
# sell_price is less than equal to latest buy orderbooks price ?
if price <= latest_book_price:
return_empty_df = False
else: # sell_buy
if debug1 or debug2:
return_empty_df = False
else: # neither debug1 nor debug2
# skip empty position list
if len(self.coin.buy_position_list) > 0:
# first buy position ?
if self.coin.buy_position_list[-1] == 0:
# buy_price is greater than or equal to latest sell orderbooks price ?
if price >= latest_book_price:
return_empty_df = False
# end of if buy_sell:
if not return_empty_df:
status = 0
execution_id = 'E' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # EMMDD-HHMMSS-999999(milliseconds)
position_id = 'P' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # PMMDD-HHMMSS-999999(milliseconds) ★
position_list = [
{
'executionId': execution_id,
'fee': '0', # 1 => 0 (leverage trading) ★
'lossGain': '0',
'orderId': order_id,
'positionId': position_id, # ★
'price': latest_book_price, # ★
'settleType': 'OPEN', # 'OPEN' or 'CLOSE'
'side': 'SELL', # 'SELL' or 'BUY'
'size': qty,
'symbol': symbol,
'timestamp': dt.datetime.utcnow()
}
]
df = pd.DataFrame(position_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string ★
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
msg_code = 'OK'
msg_str = 'Normal Return'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_price{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
else: # return empty dataframe
df = pd.DataFrame()
status = 9 # empty dataframe
msg_code = 'ERR-999' # null => ERR-999
msg_str = 'DataFrame is empty' # null => DataFrame is empty
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_price{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, df, msg_code, msg_str) # (9, df, 'ERR-999', 'DataFrame is empty')
# end of if not return_empty_df:
# end of if not self.coin.real_mode:
### real mode
status = 9 # internal error or misc error
while True:
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'GET'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/executions'
url = timestamp + method + path
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
parameters = {'orderId': order_id} # buy or sell orderId
headers = {
"API-KEY": self.api_key,
"API-TIMESTAMP": timestamp,
"API-SIGN": sign
}
try:
res = requests.get(endPoint + path, headers=headers, params=parameters)
# res = requests.get(endPoint + path, headers=headers, params=parameters, timeout=3)
except requests.exceptions.HTTPError as errh:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}HTTP ERROR:{Bcolors.ENDC} get_price() ▶ request.get(): {errh} ")
self.debug.sound_alert(3)
df = pd.DataFrame()
status = 9 # misc error
msg_code = 'ERR-999'
msg_str = 'HTTP ERROR'
break
except requests.exceptions.ConnectionError as errc:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}CONNECTION ERROR:{Bcolors.ENDC} get_price() ▶ request.get(): {errc} ")
self.debug.sound_alert(3)
df = pd.DataFrame()
status = 8 # retry error
msg_code = 'ERR-888'
msg_str = 'HTTP ERROR'
break
except requests.exceptions.Timeout as errt:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}TIMEOUT ERROR:{Bcolors.ENDC} get_price() ▶ request.get(): {errt} ")
self.debug.sound_alert(3)
df = pd.DataFrame()
status = 8 # retry error
msg_code = 'ERR-888'
msg_str = 'TIMEOUT ERROR'
break
except requests.exceptions.RequestException as err:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}EXCEPTION ERROR:{Bcolors.ENDC} get_price() ▶ request.get(): {err} " )
self.debug.sound_alert(3)
df = pd.DataFrame()
status = 9 # misc error
msg_code = 'ERR-999'
msg_str = 'EXCEPTION ERROR'
break
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC EXCEPTION ERROR:{Bcolors.ENDC} get_price() ▶ quit python...")
self.debug.sound_alert(3)
df = pd.DataFrame()
status = 9 # misc error
msg_code = 'ERR-999'
msg_str = 'MISC EXCEPTION ERROR'
break
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
# BTC_JPY
# -----------------------------------------------------
# {
# 'status': 0,
# 'data': {
# 'list': [
# {'executionId': 492454252,
# 'fee': '0',
# 'lossGain': '0',
# 'orderId': 2350188580,
# 'positionId': 162375068, ★
# 'price': '92.967',
# 'settleType': 'OPEN',
# 'side': 'SELL',
# 'size': '10',
# 'symbol': 'XRP_JPY',
# 'timestamp': '2022-03-17T10:12:33.135Z'
# }
# ]
# },
# 'responsetime': '2022-03-19T00:06:00.681Z'
# }
# -----------------------------------------------------
status = dict.get('status')
# nornal return ?
if status == 0:
data_dict = dict.get('data')
if len(data_dict) == 0:
df = pd.DataFrame()
status = 9 # empy dataframe
msg_code = 'ERR-999' # null => ERR-999
msg_str = 'DataFrame is empty' # null => DataFrame is empty
else:
data_list = data_dict.get('list')
df = pd.DataFrame(data_list)
# BTC_JPY
# Data columns (total 11 columns):
# -----------------------------------------------------------
# # Column Dtype
# -----------------------------------------------------------
# 0 executionId int64 => string
# 1 fee object => float
# 2 lossGain object => float
# 3 orderId int64 => string
# 4 positionId int64 => string
# 5 price object => float
# 6 settleType object 'OPEN' or 'CLOSE'
# 7 side object 'SELL' or 'BUY'
# 8 size object => float
# 9 symbol object => 'BTC_JPY'
# 10 timestamp object => time(utc)
# -----------------------------------------------------------
if df.shape[0] > 0:
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string ★
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
# Data columns (total 11 columns):
# ---------------------------------------------------------
# # Column Dtype
# ---------------------------------------------------------
# 0 executionId object
# 1 fee float64
# 2 lossGain float64
# 3 orderId object
# 4 positionId object ★
# 5 price float64
# 6 settleType object (OPEN or CLOSE)
# 7 side object (BUY or SELL)
# 8 size float64
# 9 symbol object (BTC_JPY)
# 10 timestamp datetime64[ns, UTC]
# ---------------------------------------------------------
msg_code = 'OK'
msg_str = 'Normal Return' # 'Normal Return'
# end of if len(data_dict) == 0:
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} get_price({order_id=}) ▶ {status=} quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # ERR-???
msg_str = msg_list[0].get('message_string') # error message
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_price{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {msg_code} - {msg_str} ")
self.debug.sound_alert(3)
######################################################## debug info
print('-'*100, 'Begin: dump response from get_price()')
print(dict)
print('-'*100, 'End : dump response from get_price()')
####################################################### debug info
### change original error status(?) to empty status(9) ★
df = pd.DataFrame()
status = 9 # empty dataframe
msg_code = 'ERR-999' # null => ERR-999
msg_str = 'DataFrame is empty' # null => DataFrame is empty
# end of if status == 0:
if status == 0:
side_pos = df.columns.get_loc('side')
side = df.iloc[0, side_pos] # get side ('BUY' or 'SELL')
if side == 'BUY':
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.OKCYAN}get_price_buy{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
else: # 'SELL':
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.OKCYAN}get_price_sell{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
elif status == 9: # dataframe is empty
pass # do not print message
else: # error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_price{Bcolors.ENDC}({order_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
# end of if status == 0:
break
# end of while loop
# normal or error return
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return') or (8, df, 'ERR-888', 'Error Message') or (9, df, 'ERR-999', 'DataFrame is mepty')
##############################
def get_assets(self) -> tuple: # ▶ (status, asset)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
status, df = self.exe_assets()
# --------------------------------------------------
# # Column Dtype
# --------------------------------------------------
# 0 amount object
# 1 available object
# 2 conversionRate object
# 3 symbol object
# --------------------------------------------------
asset = 0.0
if status != 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_assets{Bcolors.ENDC}(): ▶ {status=} ")
# error return
return status, asset
############################################ debug info
# print('-'*100, 'Begin: get_assets()')
# print('\n', df.head())
# print('-'*80)
# print('\n', df.info())
# print('-'*100, 'End : getassets()')
############################################ debug info
# -----------------------------------------------------
# amount available conversionRate symbol
# -----------------------------------------------------
# 0 185822.0000 185822.0000 1.0 JPY
# 1 0.0001 0.0001 4564624.0 BTC
# 2 0.0000 0.0000 302500.0 ETH
# 3 0.0100 0.0100 33100.0 BCH
# 4 0.1000 0.1000 11992.0 LTC
# ----------------------------------------------------
filter_mask = df['symbol'] == symbol
dfx = df[filter_mask]
if dfx.shape[0] > 0:
asset = dfx.amount.values[0]
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}get_assets{Bcolors.ENDC}(): ▶ {status=}, {symbol=}, {asset=:,.{_q_}f} ")
# normal return
return status, asset # 0(int), 1.123(float)
############################## execute account/assets ▶ status, df(dataframe)
def exe_assets(self) -> tuple: # ▶ status, df(dataframe)
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'GET'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/account/assets'
url = timestamp + method + path
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.get(endPoint + path, headers=headers, timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}JCOM Maintenance:{Bcolors.ENDC} get_assets() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
msg_code = ''
msg_str = ''
if status == 0:
data_list = dict.get('data')
df = pd.DataFrame(data_list)
# -----------------------------------------------------------
# # Column Dtype
# -----------------------------------------------------------
# 0 amount object => float
# 1 available object => float
# 2 conversionRate object => float
# 3 symbol object
# -----------------------------------------------------------
# convert data types
df = df.astype({'amount': 'float', 'available': 'float', 'conversionRate': 'float'})
# normal return
return (status, df)
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_assets(): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status=1 or ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # ERR-201
msg_str = msg_list[0].get('message_string') # Trading margin is insufficient
# debug info
print('-'*100, 'Begin: dump response from exe_assets()')
print(dict)
print('-'*100, 'End : dump response from exe_assets()')
# debug info
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_assets{Bcolors.ENDC}(): ▶ {status=}, {msg_code} - {msg_str} ")
# error return
return (status, pd.DataFrame())
###############################################################################################
def exe_buy_order_market_wait(self, qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
if latest_close_price == 0.0: latest_close_price = price
status = 0
execution_id = 'E' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # EMMDD-HHMMSS-999999(milliseconds)
order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # BMMDD-HHMMSS-999999(milliseconds)
position_id = 'P' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # PMMDD-HHMMSS-999999(milliseconds) ★
position_list = [
{
'executionId': execution_id, # E9999-999999-999999
'fee': '0', # 1 => 0 (leverage trading)
'lossGain': '0',
'orderId': order_id, # B9999-999999-999999 ★
'positionId': position_id, # P9999-999999-999999 ★
'price': latest_close_price, # ★
'settleType': 'OPEN', # 'OPEN' or 'CLOSE'
'side': 'BUY', # 'SELL' or 'BUY'
'size': qty,
'symbol': symbol,
'timestamp': dt.datetime.utcnow()
}
]
df = pd.DataFrame(position_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string ★
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
msg_code = 'OK'
msg_str = 'Normal Return'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
# end of if not self.coin.real_mode:
### real mode
df = pd.DataFrame()
# exe_buy_order_market(qty: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
status, order_id, msg_code, msg_str = self.exe_buy_order_market(qty) # ★ FAK (Fill and Kill)
# status: 0(ok), 1(Trading margin is insufficient), ?(misc)
# error ?
if status != 0:
return (status, df, msg_code, msg_str) # (1, df, 'msg_code', 'msg_str')
i = 0
while True:
i += 1
if i > self.coin.api_market_timeout_limit: # time out ?
status = 8 # time out
msg_code = 'ERR-888'
msg_str = f'get_price() time out error: loop count={i} '
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return => (8, df, 'ERR-888', 'get_price() time out error:...')
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
status, order_status = self.get_order_status_try(order_id, qty, price)
# status: # 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED, ERROR
if status != 0:
msg_code = 'ERR-999'
msg_str = f"get_order_status({order_id=}) error ▶ {status=}, {order_status=} "
return (status, df, msg_code, msg_str) # error return
### status == 0 : normal return
# exe_buy_order_market() is pending status
if order_status in 'WAITING,MODIFYING,CANCELLING,ORDERED':
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_buy_order_market_wait(): get_order_status({i}) ▶ {status=}, {order_status=} ")
sleep(1) # sleep 1 sec
continue # continue get_order_status_try()
### order_status : EXECUTED or EXPIRED or CANCELED
### get_order_status() ▶ status==0 & order_status in 'EXECUTED(qty==real_qty), EXPIRED(qty > real_qty), CANCELED(real_qty==0)'
sleep(1) # sleep 1 sec
# get_price_try(order_id: str, qty: float, price: float, latest_close_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
status, df, msg_code, msg_str = self.get_price_try(order_id, qty, price) # ★ qty and price are used for fake mode
# 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_buy_order_market_wait(): get_price({i}) ▶ {status=}, {df.shape=} ")
# error ?
if status != 0:
# empty dataframe ?
if status == 9:
sleep(1) # sleep 1 sec
continue # continue get_price()
# misc error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return (status, df, msg_code, msg_str) # error return
if df.shape[0] == 0:
sleep(1) # sleep 1 sec
continue # continue get_price()
### Normal Return
# -------------------------------------------------------------
# # Column Dtype
# -------------------------------------------------------------
# 0 executionId int64
# 1 fee float64
# 2 lossGain float64
# 3 orderId object(string)
# 4 positionId object(string)
# 5 price float64
# 6 settleType object('OPEN' or 'CLOSE')
# 7 side object('BUY' or 'SELL')
# 8 size float64
# 9 symbol object('BTC')
# 10 timestamp datetime64[ns]
# ------------------------------------------------------------
# real_buy_qty = df.iloc[0]['size']
real_buy_price = df.iloc[0]['price']
# real_buy_fee = df.iloc[0]['fee']
if real_buy_price > 0:
msg_code = 'OK'
msg_str = 'Normal Return'
break # got a real price ? => normal return
sleep(1) # sleep 1 sec
# continue to get_price()
# end of while True:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
# normal return
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
####################################################
def exe_buy_order_market(self, qty: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # Buy MMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test buy order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE MODE{Bcolors.ENDC} ")
return (status, res_order_id, msg_code, msg_str) # (0, 'B9999-999999-999999', 'OK', 'Fake/Test buy order')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/order'
reqBody = {
'symbol': symbol, # Required (BTC, ETH, LTC,...)
'side': 'BUY', # Required (BUY or SELL)
'executionType': 'MARKET', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAK', # FAK (Fill and Kill)
'size': qty_str # Required (1.1234 BTC)
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_buy_order_market() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = '' # string
msg_code = ''
msg_str = ''
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_buy_order_market({qty=:,.{_q_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-201'
msg_str = msg_list[0].get('message_string') # 'Trading margin is insufficient'
if msg_code == 'ERR-201': # Trading margin is insufficient
pass
else: # misc error
################################################################### debug info
print('-'*100, 'Begin: dump response from exe_buy_order_market()')
print(dict) # temp temp
print('-'*100, 'End : dump response from exe_buy_order_market()')
################################################################### debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str} ")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '99999', 'OK', 'Normal Return') or (1, '', 'ERR-201', 'Trading margin is insufficient')
##################################################################
def exe_buy_order_limit(self, qty: float, price: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # BMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test buy order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_limit{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC} ")
return (status, res_order_id, msg_code, msg_str) # (0, 'B9999-999999-999999', 'OK', 'Fake/Test Buy order')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/order'
reqBody = {
'symbol': symbol, # Required (BTC, ETH, LTC,...)
'side': 'BUY', # Required (BUY or SELL)
'executionType': 'LIMIT', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAS', # FAK (Fill and Store)
'price': price_str, # Required (9999999)
'size': qty_str # Required (1.1234 BTC)
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_buy_order_limit() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_buy_order_limit({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
### {'status': 1, 'messages': [{'message_code': 'ERR-5120', 'message_string': 'Too high price.'}], 'responsetime': '2022-09-26T05:00:58.198Z'}
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code')
msg_str = msg_list[0].get('message_string')
if msg_code == 'ERR-201': # Trading margin is insufficient
pass
else:
################################################################# debug info
print('-'*100, 'Begin: dump response from exe_buy_order_limit()')
print(dict) # temp temp
print('-'*100, 'End : dump response from exe_buy_order_limit()')
################################################################# debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_order_limit{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str} ")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '99999', 'OK', 'Normal Return') or (1, '', 'ERR-201', 'Trading margin is insufficient')
################################################################################################
def exe_sell_order_market_wait(self, qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
if latest_close_price == 0.0: latest_close_price = price
status = 0
execution_id = 'E' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # EMMDD-HHMMSS-999999(milliseconds)
order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
position_id = 'P' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # PMMDD-HHMMSS-999999(milliseconds) ★
position_list = [
{
'executionId': execution_id, # E9999-999999-999999
'fee': '0', # 1 => 0 (leverage traing)
'lossGain': '0',
'orderId': order_id, # S9999-999999-999999
'positionId': position_id, # P9999-999999-999999 ★
'price': latest_close_price, # ★
'settleType': 'OPEN', # 'OPEN' or 'CLOSE'
'side': 'SELL', # 'SELL' or 'BUY'
'size': qty,
'symbol': symbol,
'timestamp': dt.datetime.utcnow()
}
]
df = pd.DataFrame(position_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string ★
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
msg_code = 'OK'
msg_str = 'Normal Return'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC} ")
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
# end of if not self.coin.real_mode:
### real mode
df = pd.DataFrame()
status, order_id, msg_code, msg_str = self.exe_sell_order_market(qty) # ★ FAK (Fill and Kill)
# error ?
if status != 0:
return status, df, msg_code, msg_str # (1, df, 'msg_code', 'msg_str')
i = 0
while True:
i += 1
if i > self.coin.api_market_timeout_limit: # time out ?
status = 8 # time out
msg_code = 'ERR-888'
msg_str = f'get_price() time out error: loop count={i} '
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return => (8, df, 'ERR-888', 'get_price() time out error:...')
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
status, order_status = self.get_order_status_try(order_id, qty, price)
# status: # 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED, ERROR
if status != 0:
msg_code = 'ERR-999'
msg_str = f"get_order_status({order_id=}) error ▶ {status=}, {order_status=} "
return status, df, msg_code, msg_str # error return
### status == 0
# exe_sell_order_market() is pending status
if order_status in 'WAITING,MODIFYING,CANCELLING,ORDERED':
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_sell_order_market_wait(): get_order_status({i}) ▶ {status=}, {order_status=} ")
sleep(1) # sleep 1 sec
continue # continue get_order_status()
### order_staus : EXECUTED or EXPIRED or CANCELED
### get_order_status() ▶ status==0 & order_status in 'EXECUTED,EXPIRED,CANCELED'
sleep(1) # sleep 1 sec
# get_price_try(order_id: str, qty: float, price: float, latest_close_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
status, df, msg_code, msg_str = self.get_price_try(order_id, qty, price) # qty and price are used for fake mode
# 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_sell_order_market_wait(): get_price({i}) ▶ {status=}, {df.shape=} ")
# error ?
if status != 0:
# DataFrame is empty ?
if status == 9:
sleep(1) # sleep 1 sec
continue # continue get_price()
# misc error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return
if df.shape[0] == 0:
sleep(1) # sleep 1 sec
continue # continue get_price()
### Normal Return
# -----------------------------------------------------
# # Column Dtype
# -----------------------------------------------------
# 0 executionId int64
# 1 fee float64
# 2 lossGain float64
# 3 orderId object(string)
# 4 positionId object(string)
# 5 price float64
# 6 settleType object('OPEN' or 'CLOSE')
# 7 side object('BUY' or 'SELL')
# 8 size float64
# 9 symbol object('BTC_JPY')
# 10 timestamp datetime64[ns]
# ----------------------------------------------------
# real_sell_qty = df.iloc[0]['size']
real_sell_price = df.iloc[0]['price']
# real_sell_fee = df.iloc[0]['fee']
if real_sell_price > 0:
msg_code = 'OK'
msg_str = 'Normal Return'
break # got a real price ?
sleep(1) # sleep 1 sec
# continue to get_order_status_try()
# end of while True:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market_wait{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str}")
# normal or time out error return
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
#####################################################
def exe_sell_order_market(self, qty: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test sell order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE MODE{Bcolors.ENDC} ")
return (status, res_order_id, msg_code, msg_str) # (0, 'S9999-999999-999999', 'OK', 'Fake\Test sell order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/order'
reqBody = {
'symbol': symbol, # Required (BTC, ETH, LTC,...)
'side': 'SELL', # Required (BUY or SELL)
'executionType': 'MARKET', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAK', # FAK (Fill and Kill)
'size': qty_str # Required (1 BTC)
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_sell_order_market() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status') # int type
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_sell_order_market({qty=:,.{_q_}f}): ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # ERR-208
msg_str = msg_list[0].get('message_string') # Exceeds the available balance
if msg_code == 'ERR-208': # Exceeds the available balance
pass
else:
##################################################################### debug info
print('-'*100, 'Begin: dump response from exe_sell_order_market()')
print(dict) # temp temp
print('-'*100, 'End : dump response from exe_sell_order_market()')
##################################################################### debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_market{Bcolors.ENDC}({qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-201', 'Exceeds the available balance')
##################################################################
def exe_sell_order_limit(self, qty: float, price: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test sell order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_limit{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC} ")
return (status, res_order_id, msg_code, msg_str) # (0, 'S9999-999999-999999', 'OK', 'Fake/Test sell order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/order'
reqBody = {
'symbol': symbol, # Required (BTC, ETH, LTC,...)
'side': 'SELL', # Required (BUY or SELL)
'executionType': 'LIMIT', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAS', # FAK (Fill and Store)
'price': price_str, # Required (9999999 YEN)
'size': qty_str # Required (1 BTC)
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_sell_order_limit() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_sell_order_limit({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-208'
msg_str = msg_list[0].get('message_string') # 'Exceeds the available balance'
if msg_code == 'ERR-208': # Exceeds the available balance
pass
else:
#################################################################### debug info
print('-'*100, 'Begin: dump response from exe_sell_order_limit()')
print(dict)
print('-'*100, 'End : dump response from exe_sell_order_limit()')
#################################################################### debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_order_limit{Bcolors.ENDC}({qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-5120', 'Too high price')
#######################################################################################################################
def exe_buy_close_order_market_wait(self, position_id: str, qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
if latest_close_price == 0.0: latest_close_price = price
status = 0
execution_id = 'E' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # EMMDD-HHMMSS-999999(milliseconds)
order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
position_list = [
{
'executionId': execution_id, # E9999-999999-999999
'fee': '0', # 1 => 0 (leverage trading)
'lossGain': '0',
'orderId': order_id, # S9999-999999-999999
'positionId': position_id, # P9999-999999-999999 ★
'price': latest_close_price,
'settleType': 'OPEN', # 'OPEN' or 'CLOSE'
'side': 'SELL', # 'SELL' or 'BUY'
'size': qty,
'symbol': symbol,
'timestamp': dt.datetime.utcnow()
}
]
df = pd.DataFrame(position_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string ★
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
msg_code = 'OK'
msg_str = 'Normal Return'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_id=}, {df.shape=}, {msg_code} - {msg_str}) {Bcolors.FAIL}FAKE MODE{Bcolors.ENDC} ")
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
# end of if not self.coin.real_mode:
### real mode
df = pd.DataFrame()
# exe_buy_close_order_market(symbol='BTC_JPY', position_id='999999999', qty='1.1234')'
status, order_id, msg_code, msg_str = self.exe_buy_close_order_market(position_id, qty) # ★ FAK (Fill and Kill)
# error ?
if status != 0:
return (status, df, msg_code, msg_str) # (1, df, 'msg_code', 'msg_str')
i = 0
while True:
i += 1
if i > self.coin.api_market_timeout_limit: # time out ?
status = 8 # time out
msg_code = 'ERR-888'
msg_str = f'get_price() time out error: loop count={i} '
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return (status, df, msg_code, msg_str) # error return => (9, df, 'ERR-999', 'get_price() time out error:...')
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
status, order_status = self.get_order_status_try(order_id, qty, price)
# status: # 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED, ERROR
if status != 0:
msg_code = 'ERR-999'
msg_str = f"get_order_status({order_id=}) error ▶ {status=}, {order_status=} "
return (status, df, msg_code, msg_str) # error return
### status == 0
# exe_buy_close_order_market() is pending status
if order_status in 'WAITING,MODIFYING,CANCELLING,ORDERED':
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_buy_close_order_market_wait(): get_order_status({i}) ▶ {status=}, {order_status=} ")
sleep(1) # sleep 1 sec
continue # continue get_order_status()
# order_status : EXECUTED or EXPIRED or CANCELED
### get_order_status() ▶ status==0 & order_status in 'EXECUTED,EXPIRED,CANCELED'
sleep(1) # sleep 1 sec
# get_price_try(order_id: str, qty: float, price: float, latest_close_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
status, df, msg_code, msg_str = self.get_price_try(order_id, qty, price) # qty and price are used for fake mode
# 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_buy_close_order_market_wait(): get_price({i}) ▶ {status=}, {df.shape=} ")
# error ?
if status != 0:
# empty dataframe ?
if status == 9:
sleep(1) # sleep 1 sec
continue # continue get_price()
# misc error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return
if df.shape[0] == 0:
sleep(1) # sleep 1 sec
continue # continue get_price()
### Normal Return
# ----------------------------------------------------------
# # Column Dtype
# ----------------------------------------------------------
# 0 executionId int64
# 1 fee float64
# 2 lossGain float64
# 3 orderId object(string)
# 4 positionId object(string)
# 5 price float64
# 6 settleType object('OPEN' or 'CLOSE')
# 7 side object('BUY' or 'SELL')
# 8 size float64
# 9 symbol object('BTC')
# 10 timestamp datetime64[ns]
# ---------------------------------------------------------
# real_sell_qty = df.iloc[0]['size']
real_sell_price = df.iloc[0]['price']
# real_sell_fee = df.iloc[0]['fee']
if real_sell_price > 0:
msg_code = 'OK'
msg_str = 'Normal Return'
break # got a real price ?
sleep(1) # sleep 1 sec
# continue to get_price()
# end of while True:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str}")
# normal or time out error return
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
###########################################################################
def exe_buy_close_order_market(self, position_id: str, qty: float) ->tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # BMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test buy close order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_market{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE MODE{Bcolors.ENDC} ")
return (status, res_order_id, msg_code, msg_str) # (0, 'B9999-999999-999999', 'OK', 'Fake\Test close order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/closeOrder'
reqBody = {
'symbol': symbol, # Required (BTC_JPY, ETH_JPY, LTC_JPY,...)
'side': 'BUY', # Required (BUY or SELL)
'executionType': 'MARKET', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAK', # FAK (Fill and Kill)
'settlePosition': [
{
'positionId': position_id, # 162401271
'size': qty_str # 0.01
}
]
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_buy_close_order_market() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status') # int type
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_buy_close_order_market({qty=:,.{_q_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-???'
msg_str = msg_list[0].get('message_string') # 'Error Message'
############################################################################ debug info
print('-'*100, 'Begin: dump response from exe_buy_close_order_market()')
print(dict)
print('-'*100, 'End : dump response from exe_buy_close_order_market()')
############################################################################ debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_market{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-???', 'Error Message...')
########################################################################################
def exe_buy_close_order_limit(self, position_id: str, qty: float, price: float) ->tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # BMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test buy close order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, res_order_id, msg_code, msg_str) # (0, 'B9999-999999-999999', 'OK', 'Fake/Test close order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/closeOrder'
reqBody = {
'symbol': symbol, # Required (BTC_JPY, ETH_JPY, LTC_JPY,...)
'side': 'BUY', # Required (BUY or SELL)
'executionType': 'LIMIT', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAS', # FAK (Fill and Store)
'price': price_str, # Required (9999999 YEN)
'settlePosition': [
{
'positionId': position_id, # 162401271
'size': qty_str # 0.01
}
]
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_buy_close_order_limit() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_buy_close_order_limit({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-???'
msg_str = msg_list[0].get('message_string') # 'Error Message...'
######################################################################### debug info
print('-'*100, 'Begin: dump response from exe_buy_close_order_limit()')
print(dict)
print('-'*100, 'End : dump response from exe_buy_close_order_limit()')
######################################################################### debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-???', 'Error Message')
########################################################################################################################
def exe_sell_close_order_market_wait(self, position_id: str, qty: float, price: float, latest_close_price=0.0) -> tuple: # return (status, df, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
if latest_close_price == 0.0: latest_close_price = price
status = 0
execution_id = 'E' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # EMMDD-HHMMSS-999999(milliseconds)
order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
position_list = [
{
'executionId': execution_id, # E9999-999999-999999
'fee': '0', # 1 => 0 (leverage trading)
'lossGain': '0',
'orderId': order_id, # S9999-999999-999999
'positionId': position_id, # P9999-999999-999999 ★
'price': latest_close_price,
'settleType': 'OPEN', # 'OPEN' or 'CLOSE'
'side': 'SELL', # 'SELL' or 'BUY'
'size': qty,
'symbol': symbol,
'timestamp': dt.datetime.utcnow()
}
]
df = pd.DataFrame(position_list)
# convert data types
df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True, errors='coerce') # UTC (aware) ★ add=> errors='coerce' => NaT or errors='ignore'
df['executionId'] = df['executionId'].astype(str) # int64 => string
df['orderId'] = df['orderId'].astype(str) # int64 => string
df['positionId'] = df['positionId'].astype(str) # int64 => string ★
df = df.astype({'fee': 'float', 'lossGain': 'float', 'price': 'float', 'size': 'float'})
msg_code = 'OK'
msg_str = 'Normal Return'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {order_id=}, {df.shape=}, {msg_code} - {msg_str}) {Bcolors.FAIL}FAKE MODE{Bcolors.ENDC} ")
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
# end of if not self.coin.real_mode:
### real mode
df = pd.DataFrame()
# exe_sell_close_order_market(symbol='BTC_JPY', position_id='999999999', qty='1.1234')'
status, order_id, msg_code, msg_str = self.exe_sell_close_order_market(position_id, qty) # ★ FAK (Fill and Kill)
# error ?
if status != 0:
return (status, df, msg_code, msg_str) # (1, df, 'msg_code', 'msg_str')
i = 0
while True:
i += 1
if i > self.coin.api_market_timeout_limit: # time out ?
status = 8 # time out
msg_code = 'ERR-888'
msg_str = f'get_price() time out error: loop count={i} '
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return (status, df, msg_code, msg_str) # error return => (9, df, 'ERR-999', 'get_price() time out error:...')
# get_order_status_try(order_id: str, qty: float, price: float) ->tuple: # return (status, order_status)
status, order_status = self.get_order_status_try(order_id, qty, price)
# status: # 0(ok), 4(invalid request), 8(retry error), 9(internal error), ?(misc error)
# order_status: WAITING, ORDERED, MODIFYING, CANCELLING, CANCELED, EXECUTED, EXPIRED, ERROR
if status != 0:
msg_code = 'ERR-999'
msg_str = f"get_order_status({order_id=}) error ▶ {status=}, {order_status=} "
return (status, df, msg_code, msg_str) # error return
### status == 0
# exe_sell_close_order_market() is pending status ?
if order_status in 'WAITING,MODIFYING,CANCELLING,ORDERED':
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_sell_close_order_market_wait(): get_order_status({i}) ▶ {status=}, {order_status=} ")
sleep(1) # sleep 1 sec
continue # continue get_order_status()
### order_status : EXECUTED or EXPIRED or CANCELED
### get_order_status() ▶ status==0 & order_status in 'EXECUTED,EXPIRED,CANCELED'
sleep(1) # sleep 1 sec
# get_price_try(order_id: str, qty: float, price: float, latest_close_price=0.0, debug1=False, debug2=False) -> tuple: # return (status, df, msg_code, msg_str) qty, price, latest_close_price are used for fake mode
status, df, msg_code, msg_str = self.get_price(order_id, qty, price) # qty and price are used for fake mode
# 0(ok), 8(retry error), 9(empty dataframe or internal error), ?(misc error)
self.debug.trace_write(f".... {self.gvar.callers_method_name} exe_sell_close_order_market_wait(): get_price({i}) ▶ {status=}, {df.shape=} ")
# error ?
if status != 0:
# empty dataframe ?
if status == 9:
sleep(1) # sleep 1 sec
continue # continue get_price()
# misc error
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str} ")
return status, df, msg_code, msg_str # error return
if df.shape[0] == 0:
sleep(1) # sleep 1 sec
continue # continue get_price()
### Normal Return
# -----------------------------------------------------------------
# # Column Dtype
# -----------------------------------------------------------------
# 0 executionId int64
# 1 fee float64
# 2 lossGain float64
# 3 orderId object(string)
# 4 positionId object(string)
# 5 price float64
# 6 settleType object('OPEN' or 'CLOSE')
# 7 side object('BUY' or 'SELL')
# 8 size float64
# 9 symbol object('BTC')
# 10 timestamp datetime64[ns]
# ----------------------------------------------------------------
# real_sell_qty = df.iloc[0]['size']
real_sell_price = df.iloc[0]['price']
# real_sell_fee = df.iloc[0]['fee']
if real_sell_price > 0:
msg_code = 'OK'
msg_str = 'Normal Return'
break # got a real price ?
sleep(1) # sleep 1 sec
# continue to get_price()
# end of while True:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_market_wait{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}}, {price=:,.{_p_}f}): ▶ {status=}, {df.shape=}, {msg_code} - {msg_str}")
# normal or time out error return
return (status, df, msg_code, msg_str) # (0, df, 'OK', 'Normal Return')
#############################################################################
def exe_sell_close_order_market(self, position_id: str, qty: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'B' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # BMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test sell close order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_buy_close_order_market{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, res_order_id, msg_code, msg_str) # (0, 'B9999-999999-999999', 'OK', 'Fake\Test close order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/closeOrder'
reqBody = {
'symbol': symbol, # Required (BTC_JPY, ETH_JPY, LTC_JPY,...)
'side': 'SELL', # Required (BUY or SELL)
'executionType': 'MARKET', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAK', # FAK (Fill and Kill)
'settlePosition': [
{
'positionId': position_id, # 162401271
'size': qty_str # 0.01
}
]
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_sell_close_order_market() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status') # int type
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_sell_close_order_market({qty=:,.{_q_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-???'
msg_str = msg_list[0].get('message_string') # 'Error Message'
############################################################################ debug info
print('-'*100, 'Begin: dump response from exe_sell_close_order_market()')
print(dict)
print('-'*100, 'End : dump response from exe_sell_close_order_market()')
############################################################################ debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_market{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-???', 'Error Message...')
##########################################################################################
def exe_sell_close_order_limit(self, position_id: str, qty: float, price: float) -> tuple: # return (status, res_order_id, msg_code, msg_str)
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
qty_str = f'{qty:.{_q_}f}'
price_str = f'{price:.{_p_}f}'
if not self.coin.real_mode:
status = 0
res_order_id = 'S' + dt.datetime.now().strftime('%m%d-%H%M%S-%f') # SMMDD-HHMMSS-999999(milliseconds)
msg_code = 'OK'
msg_str = 'Fake/Test sell close order...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, {res_order_id=} {Bcolors.FAIL}FAKE{Bcolors.ENDC}")
return (status, res_order_id, msg_code, msg_str) # (0, 'S9999-999999-999999', 'OK', 'Fake/Test sell close order...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/closeOrder'
reqBody = {
'symbol': symbol, # Required (BTC_JPY, ETH_JPY, LTC_JPY,...)
'side': 'SELL', # Required (BUY or SELL)
'executionType': 'LIMIT', # Required (MARKET, LIMIT, STOP)
'timeInForce': 'FAS', # FAK (Fill and Store)
'price': price_str, # Required (9999999 YEN)
'settlePosition': [
{
'positionId': position_id, # 162401271
'size': qty_str # 0.01
}
]
}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_sell_close_order_limit() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
res_order_id = '' # string
msg_code = ''
msg_str = ''
# normal return ?
if status == 0:
res_order_id = dict.get('data') # string type
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_sell_close_order_limit({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_p_}f}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # 'ERR-???'
msg_str = msg_list[0].get('message_string') # 'Error Message...'
########################################################################## debug info
print('-'*100, 'Begin: dump response from exe_sell_close_order_limit()')
print(dict)
print('-'*100, 'End : dump response from exe_sell_close_order_limit()')
########################################################################## debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_sell_close_order_limit{Bcolors.ENDC}({position_id=}, {qty=:,.{_q_}f}, {price=:,.{_q_}f}): ▶ {status=}, {res_order_id=}, {msg_code} - {msg_str}")
# normal or error return
return (status, res_order_id, msg_code, msg_str) # (0, '999999', 'OK', 'Normal Return') or (1, '', 'ERR-???', 'Error Message')
###################################################
def exe_cancel_order(self, order_id: str) -> tuple: # return (status, msg_code, msg_str)
if not self.coin.real_mode:
status = 0
msg_code = 'OK'
msg_str = 'Cancelled...'
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_cancel_order{Bcolors.ENDC}({order_id=}): {Bcolors.FAIL}FAKE{Bcolors.ENDC} ")
return (status, msg_code, msg_str) # (0, 'OK', 'Cancelled...')
# end of if not self.coin.real_mode:
### real mode
timestamp = '{0}000'.format(int(time.mktime(dt.datetime.now().timetuple())))
method = 'POST'
endPoint = 'https://api.coin.z.com/private'
path = '/v1/cancelOrder'
reqBody = {'orderId': order_id}
url = timestamp + method + path + json.dumps(reqBody)
sign = hmac.new(bytes(self.api_secret.encode('ascii')), bytes(url.encode('ascii')), hashlib.sha256).hexdigest()
headers = {
'API-KEY': self.api_key,
'API-TIMESTAMP': timestamp,
'API-SIGN': sign
}
try:
res = requests.post(endPoint + path, headers=headers, data=json.dumps(reqBody), timeout=3)
except:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}MISC ERROR:{Bcolors.ENDC} exe_cancel_order() ▶ quit python...")
self.debug.sound_alert(3)
quit()
else: # block lets you execute code when there is no error
pass
finally: # block lets you execute code, regardless of the result of the try- and except block
pass
dict = res.json()
status = dict.get('status')
msg_code = ''
msg_str = ''
# normal return
if status == 0:
msg_code = 'OK'
msg_str = 'Normal Return'
elif status == 5: # Maintenance
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.FAIL}GMO Maintenance:{Bcolors.ENDC} exe_cancel_order({order_id=}): ▶ {status=}, quit python...")
self.debug.sound_alert(3)
quit()
else: # status == ?
msg_list = dict.get('messages')
msg_code = msg_list[0].get('message_code') # ERR-5122
msg_str = msg_list[0].get('message_string') # The request is invalid due to the status of the specified order.
if msg_code == 'ERR-5122': # The request is invalid due to the status of the specified order
pass
else:
################################################################# debug info
print('-'*100, 'Begin: dump response from exe_cancel_order()')
print(dict) # temp temp
print('-'*100, 'End : dump response from exe_cancel_order()')
################################################################# debug info
# end of if status == 0:
self.debug.print_log(f".... {self.gvar.callers_method_name} {Bcolors.WARNING}exe_cancel_order{Bcolors.ENDC}({order_id=}): ▶ {status=}, {msg_code} - {msg_str} ")
# normal or error return
return (status, msg_code, msg_str) # (0, 'OK, 'Normal Return') or (1, 'ERR-5122', 'The request is invalid due to the status of the specified order')
################################################################################################
def load_master_driver(self, symbol_list: list, coin_list: list, page: int, count: int) -> None:
# self.debug.trace_warn(f".... DEBUG: load_master_driver() ")
for i, symbol in enumerate(symbol_list): # 'BTC_JPY', 'ETH_JPY', 'BCH_JPY', 'LTC_JPY', 'XRP_JPY'
coin = coin_list[i] # Coin(coin_list[i])
if not coin.suspend:
if self.gvar.reset_mode or coin.master_csv_not_found:
# load_master_data(pages: int, count: int, trace=True) -> pd.DataFrame:
api = Api(self.gvar, coin)
master_df = api.load_master_data(page, count, trace=True) # (1, 10) ★ sorted by ascending order of timestamp
coin.master_df = master_df # coin.set_master_df(master_df)
# cache master df to a csv file
# csv_file = self.folder_trading_type + f'master({symbol}).csv'
csv_file = api.folder_trading_type + f'master({symbol}).csv'
# decktop-pc/bot/buy_sell/master(BTC_JPY).csv
# decktop-pc/bot/sell_buy/master(BTC_JPY).csv
master_df.to_csv(csv_file, index=False) # overwrite
# end of for i, symbol in enumerate(symbol_list):
return
###############################################################################
def load_master_data(self, pages: int, count: int, trace=True) -> pd.DataFrame: # return merge_df
symbol = self.coin.symbol; _q_ = self.coin.qty_decimal; _p_ = self.coin.price_decimal
# self.debug.trace_warn(f".... DEBUG: load_master_data() {trace=} ")
# load the master data from the GMO
if trace:
self.debug.print_log(f".... {self.coin.fake_or_real}({symbol}) {self.coin.trade_type}: load_master_data({pages=}, {count=}): loading {symbol} master data from the GMO ")
# .... fake buy_sell: or fake sell_buy: or real buy_sell: or real sell_buy:
df_list = []
for page in range(1, pages+1): # (1, 11)
# get_crypto_try(page: int, count: int) -> tuple: # return (status, df)
status, df = self.get_crypto_try(page, count) # (1, 10)
sleep(2.5) # sleep 0.5 sec => 1 sec => 2 sec => 2.5 sec
if status == 0: df_list.append(df)
# end of for page in range(1, pages+1): # (1, 11)
if len(df_list) > 0:
merge_df = pd.concat(df_list, axis=0) # price, side, size, timestamp(UTC) descending order
else:
merge_df = pd.DataFrame()
# merge_df: gmo master dataframe
# --------------------------------------------------------------------------------
# # Column Dtype
# --------------------------------------------------------------------------------
# 0 price float64
# 1 side object 'BUY' or 'SELL'
# 2 size float64
# 3 timestamp datetime64[ns, UTC] ★ UTC descending order
# --------------------------------------------------------------------------------
### sort by timestamp(in ascending order)
if merge_df.shape[0] > 0:
merge_df.sort_values('timestamp', ascending=True, inplace=True)
return merge_df