・DeFi取引の損益通算を自動計算する方法
本記事を参照して発生した損失・損害は、当サイトでは一切の責任を負いません。
仮想通貨取引で収益を上げたら必ず確定申告を!
仮想通貨取引は「雑所得」扱い
サラリーマンで給与所得以外の収入があった場合、確定申告が必要になります。
例えば不動産投資によって得られた所得は「不動産所得」に分類され、確定申告で収入・支出を明記して報告しなければなりません。
所得は全部で10種類あり、他の9種に当てはまらないものが雑所得に分類されます。他の9種とは給与所得、利子所得、配当所得、事業所得、退職所得、不動産所得、山林所得、譲渡所得、一時所得です。
仮想通貨取引の他にも、事業として認められない副業や、FXなどが雑所得に分類されます。
よく『雑所得が20万円以下なら申告は不要』と言われていますが、これは間違いです。
20万円の免税規定はあくまで所得税のみの話で、住民税は対象外になります。
そのため20万円以下の雑所得が発生した場合は、確定申告をするか、各市区町村の役所で住民税の手続きを行う必要があります。
仮想通貨取引の確定申告時の注意点
法定通貨と仮想通貨の取引であれば、確定申告の難易度はそこまで高くありません。
例えば100万円分のビットコインを購入し、価格が1.5倍になったので150万円で売却したとします。
差額の50万円が収益となるので、いったん諸経費のことを忘れれば、この50万円を雑所得として報告すればよいのです。
※ちゃんと諸経費も計算しましょう!
仮想通貨の確定申告で注意すべきは仮想通貨同士の取引です。
例の1.5倍になったビットコインを、日本円ではなく別の仮想通貨に交換した場合、手元に日本円は残っていませんが確定申告が必要になります。
お金の流れとしては「日本円」→「ビットコイン」→「別の仮想通貨」となっていますが、日本の税制上は「日本円」→「ビットコイン」→「日本円」→「別の仮想通貨」とみなされます。
上図の例では150万円分のビットコインをバイナンスコインに変更していますが、日本の税制上では一度日本円に換金したものとみなされるため、手元に日本円は何も残っていませんが、最初の例と同じように100万円と150万円の差額である50万円が雑所得になります。
仮想通貨の確定申告支援ツール「Cryptact」
今まで説明してきたとおり、仮想通貨取引で得た収益は確定申告の対象となり、その計算方法は非常に複雑です。
申告漏れによって突然税務署から電話がかかってくる、なんてことも起こりかねません。
「ブームに乗って気軽に始めてみたけど、確定申告のやり方がわからない!」という方も多いのではないでしょうか。
そんな方にオススメなのが、仮想通貨の確定申告サポートツール「Cryptact(クリプタクト)」です。
クリプタクトは仮想通貨の資産管理をはじめ、損益計算や確定申告のサポートを得られる投資ツールです。
58の取引所、8,600通貨に対応しているという圧倒的なサポート範囲のおかげか、仮想通貨損益計算サービスでは利用者数No.1となっています。
また後述する仮想通貨のDeFiという、より複雑な計算が必要な取引にも対応しているため、UniswapやPancakeSwapを始めた人にもオススメのサービスです。
使用方法も以下の3ステップのみで、もし分からない場合も仮想通貨取引に精通したスタッフが親身に対応してくれます。
- 取引履歴の準備
- 取引履歴のアップロード
- 自動計算された損益をチェック
DeFiの損益計算を”自分で”自動計算する方法
筆者は税理士ではないため、本記事に含まれる内容は正確ではない可能性があります。 本記事を参照して発生した損失・損害は、当サイトでは一切の責任を負いません。 管理人こん 確定申告が不安な方は、必ず税理士に相談しましょう![…]
損益自動計算の流れ
「【DeFi】PancakeSwapの確定申告手順をまとめてみた」でも説明したとおり、DeFi取引の損益ポイントは以下3点です。
- Harvestした時
- Farm・Poolをやめた時(アンステークした時)
- 仮想通貨取引に関連する全ての手数料
②に関しては、「仮想通貨取引の確定申告時の注意点」でお伝えした内容を守れば、計算すること自体は簡単です。
問題は①と③です。PancakeSwapの場合は、流動性の提供で得られたCakeをHarvestし、Syrup Poolに預けることで複利運用をすることができます。
より複利運用を加速させるために、3日に1度くらいの頻度でHarvestしている人は多いのではないでしょうか。
このHarvestのたびに収益が発生し、使用した取引手数料を経費として計算しなければなりません。
BscScanで見ることができる取引結果は日本円で計算されているわけではないので、各Harvest毎のトランザクションを日本円に直す必要があります。
そこで、世界最大の仮想通貨取引所であるバイナンスが提供するAPIを使用し、取引時間毎のCakeの価値を算出します。
実際にはccxtという各取引所のAPI操作が集約されたPythonのライブラリを使用します。
しかしccxtを使ってみたところ、Harvestで得られるCakeと日本円に対応したCAKE/JPY、Harvest毎に発生する取引手数料に使用する通貨であるBNBと日本円に対応したBNB/JPYがありませんでした。
そこで、Twelve Dataが提供するAPIを用いて、ccxtで取得できる「CAKE/AUD」とTwelve Dataで取得できる「AUD/JPY」をかけ合わせ、CAKE/JPYを擬似的に作ります。(BNBも同様)
あとはBscScanのAPIから、トランザクションデータを出力し、そのデータに基づいてCAKE/JPYをかけ合わせ、損益の計算を行います。
全体の流れは下図の通りです。
コードの全体像
import ccxt
import requests
import json
import pandas as pd
from twelvedata import TDClient
from datetime import datetime
import time
bsc_api_key = 'BscScanのAPIキー'
my_address = '自分のウォレットアドレス'
txn_list = []
##########
# 関数
##########
def aud_chart(unix):
td_api_key = 'Twelve DataのAPIキー'
ymd = datetime.fromtimestamp(int(unix))
td = TDClient(apikey=td_api_key)
aud = td.time_series(
symbol='AUD/JPY',
interval='1min',
outputsize=1,
timezone='Japan',
end_date=ymd,
)
aud = aud.as_json()
aud_jpy = aud[0]['open']
return aud_jpy
def crypto_chart(unix):
binance = ccxt.binance()
unix = int(unix) * 1000
cake_bnb = binance.fetch_ohlcv(symbol='CAKE/BNB', timeframe='1m', since=unix, limit=1)
bnb_aud = binance.fetch_ohlcv(symbol='BNB/AUD', timeframe='1m', since=unix, limit=1)
cake_bnb = cake_bnb[0][1]
bnb_aud = bnb_aud[0][1]
return cake_bnb, bnb_aud
##########
# 処理エリア
##########
url = 'https://api.bscscan.com/api'
params = {'module':'account', 'action':'tokentx', 'address':my_address, 'apikey':bsc_api_key}
r = requests.get(url, params=params)
r = r.json()
df = pd.json_normalize(r['result'])
unstake = (df[df['from'] == '0x009cf7bc57584b7998236eff51b98a168dcea9b0'])
unstake = unstake[['timeStamp', 'value', 'gasPrice', 'gasUsed']]
for data in unstake.itertuples():
ymd = datetime.fromtimestamp(int(data[1]))
ymd = ymd.strftime('%Y/%m/%d')
cake_bnb = float(crypto_chart(data[1])[0])
bnb_aud = float(crypto_chart(data[1])[1])
aud_jpy = float(aud_chart(data[1]))
cake_jpy = cake_bnb * bnb_aud * aud_jpy
bnb_jpy = bnb_aud * aud_jpy
inc_cake = float(data[2]) / 1000000000000000000
inc = inc_cake * cake_jpy
exp_bnb = float(data[3]) * float(data[4]) / 1000000000000000000
exp = exp_bnb * bnb_jpy
txn_inc = dict(Date=ymd, Type='income', value=inc_cake, jpy_value=inc)
txn_exp = dict(Date=ymd, Type='expense', value=exp_bnb, jpy_value=exp)
txn_list.append(txn_inc)
txn_list.append(txn_exp)
time.sleep(8)
stake = (df[df['to'] == '0xa80240eb5d7e05d3f250cf000eec0891d00b51cc'])
stake = stake[['timeStamp', 'value', 'gasPrice', 'gasUsed']]
for data in stake.itertuples():
ymd = datetime.fromtimestamp(int(data[1]))
ymd = ymd.strftime('%Y/%m/%d')
cake_bnb = float(crypto_chart(data[1])[0])
bnb_aud = float(crypto_chart(data[1])[1])
aud_jpy = float(aud_chart(data[1]))
cake_jpy = cake_bnb * bnb_aud * aud_jpy
bnb_jpy = bnb_aud * aud_jpy
exp_bnb = float(data[3]) * float(data[4]) / 1000000000000000000
exp = exp_bnb * bnb_jpy
txn_exp = dict(Date=ymd, Type='expense', value=exp_bnb, jpy_value=exp)
txn_list.append(txn_exp)
time.sleep(8)
txn_list = pd.DataFrame(txn_list)
txn_list.to_csv("pancake_txn.csv")
具体的な実装方法
下準備①:APIキーの取得
先述の通り、使用するAPIは3つあります。
- Binance API
- Twelve Data API
- BscScan API
うち、ccxt経由でつなぐBinance APIはパブリックAPIであるため、APIキーは必要ありません。
逆にTwelve Data APIやBscScan APIはプライベートAPIであるため、会員登録が必要です。
個人的に利用するのであれば、どちらも無料プランで問題ないので会員登録を済ませAPIキーを取得しましょう。
下準備②:pip install
恐らくこの記事を読んでいるということはccxtやBscScanAPIを始めて使う方だと思います。
今回使用するライブラリは以下の通り。
import ccxt
import requests
import json
import pandas as pd
from twelvedata import TDClient
from datetime import datetime
import time
所有していないものはpip installでインストールしてしまいましょう。
pip install ccxt
pip install twelvedata
importの後ろにとりあえず、APIキーやウォレットアドレスを定義しておいたのですが、別に最初に定義する必要はありませんでしたw
関数の定義後にこれらは使用します。
bsc_api_key = 'BscScanのAPIキー'
my_address = '自分のウォレットアドレス'
txn_list = []
AUD/JPYの取得
Twelve Data APIを使用してAUD/JPYを取得する関数です。引数は、後のBscScanで取得できるUNIX時間を使用し、戻り値はAUD/JPYとなります。
def aud_chart(unix):
td_api_key = 'Twelve DataのAPIキー'
ymd = datetime.fromtimestamp(int(unix)) #UNIXをyyyy-mm-dd hh:mm:ssに変換
td = TDClient(apikey=td_api_key) #APIの認証
aud = td.time_series(
symbol='AUD/JPY',
interval='1min',
outputsize=1,
timezone='Japan',
end_date=ymd, #取引時間のデータを取得
)
aud = aud.as_json()
aud_jpy = aud[0]['open']
return aud_jpy
Twelve Data APIではUNIX時間ではなく、「yyyy-mm-dd」もしくは「yyyy-mm-dd hh:mm:ss」で日時を指定する必要があります。
ほかはTwelve Data APIの公式ドキュメントに沿って実装したため、特筆すべきポイントはありません。
CAKE/AUD・BNB/AUDの取得
次にccxtを用いて、CAKE/AUDとBNB/AUDを取得する関数です。引数は同じくUNIX時間で、戻り値はCAKE/AUDとBNB/AUDになります。
def crypto_chart(unix):
binance = ccxt.binance()
unix = int(unix) * 1000 #ミリ秒を追加
cake_bnb = binance.fetch_ohlcv(symbol='CAKE/BNB', timeframe='1m', since=unix, limit=1)
bnb_aud = binance.fetch_ohlcv(symbol='BNB/AUD', timeframe='1m', since=unix, limit=1)
cake_bnb = cake_bnb[0][1]
bnb_aud = bnb_aud[0][1]
return cake_bnb, bnb_aud
これもccxtのGitHubに沿って実装しているので、変わったことをしているわけではありません。
ccxtではミリ秒までのUNIX時間を採用しているため×1000をしていることとくらいですかね。
BscScanのトランザクションデータ取得
BscScanのトランザクションデータを取得します。BscScanもPythonのライブラリが用意されていたのですが、使い方がイマイチ分からなかったので、WebAPIでデータ取得しています。
url = 'https://api.bscscan.com/api'
params = {'module':'account', 'action':'tokentx', 'address':my_address, 'apikey':bsc_api_key}
r = requests.get(url, params=params)
r = r.json()
df = pd.json_normalize(r['result'])
パラメータの「action」を「tokentx」に、「address」を前段に定義した「自身のウォレットアドレス」にすることで、指定のアドレスのトランザクションデータを全部引っ張ってくることができます。
それをフィルターをかけやすいようにpandasでデータフレームの形にします。
HarvestとPool時のトランザクションでフィルターをかける
今回はPancakeSwapのHarvestとPoolにトランザクションを絞りたいので、Harvest時のトランザクションを「from」のカラムを使用し、フィルターをかけます。
Harvestした際は、PancakeSwapの「0x009cf7bc57584b7998236eff51b98a168dcea9b0」というコントラクトアドレスからCakeを受け取るので、このアドレスでフィルターをかけます。
一方でHarvestしたCakeをPoolする際は、PancakeSwapの「0xa80240eb5d7e05d3f250cf000eec0891d00b51cc」というコントラクトアドレスにCakeを送るので、このアドレスを「To」カラムを使用しフィルタをかけます。
unstake = (df[df['from'] == '0x009cf7bc57584b7998236eff51b98a168dcea9b0']) #Harvestのトランザクション
unstake = unstake[['timeStamp', 'value', 'gasPrice', 'gasUsed']] #必要な列のみにする
stake = (df[df['to'] == '0xa80240eb5d7e05d3f250cf000eec0891d00b51cc']) #Poolのトランザクション
stake = stake[['timeStamp', 'value', 'gasPrice', 'gasUsed']] #必要な列のみにする
ちなみに「value」はHarvest or PoolしたCakeで、「gas Price」 × 「gasUsed」が取引手数料になります。
forのループでぶん回す
データフレームは整ったのであとは各トランザクション毎に、UNIX時間を取得し、AUD/JPY・CAKE/AUD・BNB/AUDを戻す関数に値を渡すだけです。
for data in unstake.itertuples():
ymd = datetime.fromtimestamp(int(data[1]))
ymd = ymd.strftime('%Y/%m/%d') #扱いやすいようにyyyy/mm/ddの形式に変更
cake_bnb = float(crypto_chart(data[1])[0])
bnb_aud = float(crypto_chart(data[1])[1])
aud_jpy = float(aud_chart(data[1]))
cake_jpy = cake_bnb * bnb_aud * aud_jpy #CAKE/JPYの算出
bnb_jpy = bnb_aud * aud_jpy #BNB/JPYの算出
inc_cake = float(data[2]) / 1000000000000000000
inc = inc_cake * cake_jpy #収入の計算
exp_bnb = float(data[3]) * float(data[4]) / 1000000000000000000
exp = exp_bnb * bnb_jpy #支出の計算
#辞書型にする
txn_inc = dict(Date=ymd, Type='income', value=inc_cake, jpy_value=inc) #収入の辞書
txn_exp = dict(Date=ymd, Type='expense', value=exp_bnb, jpy_value=exp) #支出の辞書
#リストに追加し、辞書リストにする
txn_list.append(txn_inc)
txn_list.append(txn_exp)
#Twelve Data APIの制約
time.sleep(8)
forでぶん回すだけなのですが、いくつか注意点があります。
1つめはBscScan APIで出力されるValueは、小数点を無視して出力されます。そのため1e+18で割る必要があります。
次にこれはBscScanではなくTwelve Data APIの問題なのですが、Twelve Data APIは無料プランだと1分間に8リクエストしかできません。
そのため「time.sleep(8)」で1分間に大量のリクエストがいかないように制御しています。
Pool時の手数料を取得するコードは以下です。
for data in stake.itertuples():
ymd = datetime.fromtimestamp(int(data[1]))
ymd = ymd.strftime('%Y/%m/%d')
cake_bnb = float(crypto_chart(data[1])[0])
bnb_aud = float(crypto_chart(data[1])[1])
aud_jpy = float(aud_chart(data[1]))
cake_jpy = cake_bnb * bnb_aud * aud_jpy
bnb_jpy = bnb_aud * aud_jpy
exp_bnb = float(data[3]) * float(data[4]) / 1000000000000000000
exp = exp_bnb * bnb_jpy
txn_exp = dict(Date=ymd, Type='expense', value=exp_bnb, jpy_value=exp)
txn_list.append(txn_exp)
time.sleep(8)
CSVに出力
最後にCSVに出力します。
txn_list = pd.DataFrame(txn_list) #辞書リストをデータフレームに変換
txn_list.to_csv("pancake_txn.csv") #CSVダウンロード
これで実行ディレクトリ内にcsvが作成されたら完了です!
作成されたcsvをGoogleスプレッドシートでみてみます。
ポチポチ手動で計算した数値と差がない結果となりました!
まとめ
私が非エンジニアということもあるのですが、この自動計算機を作るのに5~6時間くらいかかったと思います・・・。(ほとんどが調査の時間でしたが・・・)
私の場合、とりあえず今年はこのツールで乗り越えることができそうですが、この実装に自身がない人はやっぱりクリプタクトが良いと思います。