GMOcoin の税金計算

来年の確定申告用にGMOcoinの実現損益計算Pythonプログラムを作成した.計算方法は移動平均を用いていて,クロスチェックには下記のサイトの例を使用した.

上記サイトの計算例である,
ビットコインを、①〜④の順で購入・売却した。
①時価100円/BTCで1BTCを購入
②時価150円/BTCで1BTCを購入
③時価200円/BTCで1BTCを売却
④時価275円/BTCで1BTCを購入
のトレード履歴を,GMOcoinからエクスポートできるtrading reportのフォーマットでダミー生成した.この例では75円の実現損益になる.

trading report のフォーマット(列省略あり)

それでは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として保存される.

計算結果を確認してみよう.

df_seg_BTC_PL.csv

qty_integ の列でBTCの数量が積算されている.これをもとに平均取得単価を計算すると,unit_price の列になる.したがって,売ったとき生じる損益は gain_JPY の列になる.しっかり75円のプラスがでており,計算は正しくできている.sequenceの列は,その timestamp までの合計損益を示す.

複数の通貨があるとき

上のコードは複数の通貨にも対応している.確認のためにXRPをBTCと同じ例として売買したときをみてみよう.75円の利益がでるのだったから,同じようにXRPを売買すれば150円の利益になるはず.

2024_trading_report_dummy.csv

取引データを用意した(BTCコピーしただけ).計算結果として3つのファイルが生成される.通貨ごとの実現損益とまとめファイルであるdf_PL_all.csvである.

生成ファイル
df_seg_XRP_PL.csv

しっかり75円の利益がでている.df_PL_all.csvには全ての取引がまとめられてある.

df_PL_all.csv

しっかり合計損益が75+75=150円になっている.海外取引所などが入ってくるとコードは少し複雑になるが,基本的に#data processing のデータフレーム形式に整形すれば計算できる.


いいなと思ったら応援しよう!