GMOcoin の税金計算
来年の確定申告用にGMOcoinの実現損益計算Pythonプログラムを作成した.計算方法は移動平均を用いていて,クロスチェックには下記のサイトの例を使用した.
上記サイトの計算例である,
ビットコインを、①〜④の順で購入・売却した。
①時価100円/BTCで1BTCを購入
②時価150円/BTCで1BTCを購入
③時価200円/BTCで1BTCを売却
④時価275円/BTCで1BTCを購入
のトレード履歴を,GMOcoinからエクスポートできるtrading reportのフォーマットでダミー生成した.この例では75円の実現損益になる.
それではPythonで計算コードを書いていく.下がコード全文.
import pandas as pd
pd.set_option('display.width', 400)
pd.set_option('display.max_columns', 30)
import numpy as np
# read GMO trading report --------------------------------------------------------------------------------
f1=r"your directly\2024_tax_season_dummy"
f2=f1+r'\2024_trading_report_dummy.csv'
df_seed = pd.read_csv(f2)
# data processing ---------------------------------------------------------------------------------------------------
df_seed.dropna(subset=['売買区分'], how='any', inplace=True)
df_gmo = pd.DataFrame()
df_gmo['timestamp'] = pd.to_datetime(df_seed['日時']).apply(lambda x: int(x.timestamp() - 60 * 60 * 9))
df_gmo['date']=''
df_gmo['exchange'] = 'GMO'
df_gmo['section'] = df_seed['売買区分']
df_gmo.loc[df_gmo['section'] == '買', 'section'] = 'buy'
df_gmo.loc[df_gmo['section'] == '売', 'section'] = 'sell'
df_gmo['from'] = df_seed['銘柄名']
df_gmo['to'] = df_seed['銘柄名']
df_gmo.loc[df_gmo['section'] == 'buy', 'from'] = 'JPY'
df_gmo.loc[df_gmo['section'] == 'sell', 'to'] = 'JPY'
df_gmo['qty_from'] = ''
df_gmo.loc[df_gmo.section == 'buy', 'qty_from'] = df_seed['約定金額']
df_gmo.loc[df_gmo.section == 'sell', 'qty_from'] = df_seed['約定数量']
df_gmo['price'] = ''
df_gmo.loc[df_gmo.section == 'buy', 'price'] = df_seed['約定レート']
df_gmo.loc[df_gmo.section == 'sell', 'price'] = df_seed['約定レート'].apply(lambda x: 1 / x)
df_gmo['qty_to'] = ''
df_gmo.loc[df_gmo.section == 'buy', 'qty_to'] = df_seed['約定数量']
df_gmo.loc[df_gmo.section == 'sell', 'qty_to'] = df_seed['約定金額']
df_gmo['fee'] = df_seed['注文手数料']
df_gmo['fee_token'] = 'JPY'
df_gmo['liquidation'] = df_seed['日本円受渡金額'].apply(abs)
df = df_gmo.sort_values(by='timestamp').reset_index(drop=True)
df.replace(np.nan,'',inplace=True)
df.insert(6,'liq_token','')
for i in range(0,len(df)):
if df['to'].loc[i]!='':
df.liq_token.loc[i]=df['to'].loc[i]
else:
pass
df.drop(columns='date',inplace=True)
# computation section ------------------------------------------------------------------------------------------
def computePL(df, symbol,savef):
df_seg = df[(df['from'] == symbol) | (df['to'] == symbol)].reset_index(drop=True)
df_seg['qty_to_integ'] = ''
df_seg['unit_price'] = ''
# integrate quantity of 'to' currency
for i in range(0, len(df_seg)):
if df_seg['to'].loc[i] == symbol:
if i == 0:
df_seg.qty_to_integ.loc[i] = df_seg.qty_to.loc[i]
else:
df_seg.qty_to_integ.loc[i] = df_seg.qty_to.loc[i] + df_seg.qty_to_integ.loc[i - 1]
elif df_seg['from'].loc[i] == symbol:
df_seg.qty_to_integ.loc[i] = - df_seg.qty_from.loc[i] + df_seg.qty_to_integ.loc[i - 1]
# calculate unit price
for i in range(0, len(df_seg)):
if df_seg['to'].loc[i] == symbol:
if i == 0:
df_seg.unit_price.loc[i] = df_seg.liquidation.loc[i] / df_seg.qty_to.loc[i]
else:
df_seg.unit_price.loc[i] = (df_seg.liquidation.loc[i] +
df_seg.qty_to_integ.loc[i - 1] *
df_seg.unit_price.loc[i - 1]) / df_seg.qty_to_integ.loc[i]
elif df_seg['from'].loc[i] == symbol:
df_seg.unit_price.loc[i] = df_seg.unit_price.loc[i - 1]
# calculate gain using move average method
df_seg['gain_JPY'] = ''
for i in range(0, len(df_seg)):
if df_seg['to'].loc[i] == symbol:
if df_seg['from'].loc[i] != '':
df_seg.gain_JPY.loc[i] = 0
else:
df_seg.gain_JPY.loc[i] = df_seg.liquidation.loc[i]
elif df_seg['from'].loc[i] == symbol:
df_seg.gain_JPY.loc[i] = df_seg.liquidation.loc[i] - df_seg.unit_price.loc[i] * df_seg.qty_from.loc[i]
# calculate sequential P&L
df_seg['sequence'] = [sum(df_seg.gain_JPY.values[:s + 1]) for s in range(0, len(df_seg))]
path = savef.format(symbol)
df_seg.to_csv(path,index=False)
return df_seg
savef = f1 + r'\df_seg_{}_PL.csv'
symbols = pd.DataFrame({'coin': (list(df['from'].values) + list(df['to'].values))}).dropna(how='any')
symbol_loop = symbols[(~symbols.coin.str.match('JPY')) & ~(symbols.coin=='')].coin.unique()
df_seed = []
for i in symbol_loop:
df_seed.append(computePL(df=df,
symbol=i,
savef=savef))
df_summary = pd.concat(df_seed,axis=0).sort_values(by=['timestamp']).reset_index(drop=True)
df_summary['sequence'] = [sum(df_summary.gain_JPY.values[:s + 1]) for s in range(0, len(df_summary))]
df_summary.to_csv(f1 + r'\df_PL_all.csv',
index=False)
コード構成は,
# read GMO trading report
# data processing
# computation section
の全3セクションから成る.
# read GMO trading report
ただファイル読むだけ.
# data processing
GMOcoin の report を後の計算のために整形している.このセクションで下記のデータフレームを作成する.
timestamp: 日時
exchange: 取引所の場所,つまりGMOcoin
section: 売買のどちらか
from: 通貨の交換元
to: 通貨の交換先
liq_token: 交換先通貨(後の列のための情報タグ)
qty_from: 交換元通貨の数量
qty_to: 交換先通貨の数量
price: liq_tokenの値段
fee: 取引手数料
fee_token: 手数料の通貨
liquidation: 正味の取引金額
# computation section
計算して結果を保存するセクション.やっていることは基本そのもの,
・購入した通貨の数量を積算する
・平均取得単価を算出する
・損益計算
を計算する.これらの結果はyour directlyにcsvとして保存される.
計算結果を確認してみよう.
qty_integ の列でBTCの数量が積算されている.これをもとに平均取得単価を計算すると,unit_price の列になる.したがって,売ったとき生じる損益は gain_JPY の列になる.しっかり75円のプラスがでており,計算は正しくできている.sequenceの列は,その timestamp までの合計損益を示す.
複数の通貨があるとき
上のコードは複数の通貨にも対応している.確認のためにXRPをBTCと同じ例として売買したときをみてみよう.75円の利益がでるのだったから,同じようにXRPを売買すれば150円の利益になるはず.
取引データを用意した(BTCコピーしただけ).計算結果として3つのファイルが生成される.通貨ごとの実現損益とまとめファイルであるdf_PL_all.csvである.
しっかり75円の利益がでている.df_PL_all.csvには全ての取引がまとめられてある.
しっかり合計損益が75+75=150円になっている.海外取引所などが入ってくるとコードは少し複雑になるが,基本的に#data processing のデータフレーム形式に整形すれば計算できる.