暗号資産の確定申告2026年版|ハマりポイントとPython自動化で乗り越えた話

締め切り2日前にCSVを眺めて頭を抱えた経験、ありませんか?DeFi・ステーキング・NFTまで絡む申告をPythonで自動化した実務メモです。2025年末の国税庁ガイダンスも踏まえて整理しました。

2026年の確定申告シーズンに盛大にやらかした話

今年の3月、確定申告の締め切り2日前に取引履歴のCSVを眺めながら「これ全部手計算するの?」と頭を抱えた。ビットコイン・イーサリアムだけじゃなく、DeFiのLP報酬、ステーキング利益、NFT売買まで絡んでいて、正直「全部まとめて雑所得か……」と軽く考えていた自分を殴りたかった。

2024年ごろからずっと「暗号資産の申告分離課税化はいつ?」という話が界隈で出ていたけど、2026年現在もまだ総合課税のまま。最高税率55%(所得税45% + 住民税10%)という現実は変わっておらず、稼いだと思ったらごっそり持っていかれる構造は健在だ。一方でDeFi関連の取り扱いについて国税庁が2025年末に追加ガイダンスを出し、一部の解釈が明確になったので、その辺りも実務レベルで整理してみたい。

投資方面では以前NISA・iDeCoをPythonで理解|エンジニアの複利効果完全攻略2026を書いたけど、暗号資産は税制がまったく異なるので別個に整理する価値がある。また、不動産投資の確定申告自動化については不動産投資の確定申告2026年版|Python経費管理自動化の実践ガイドが参考になるので、アプローチの考え方は共通点が多い。


2026年時点の暗号資産税制:何が変わって何が変わっていないか

基本構造(変わっていない部分)

正直、根本的な税制は2026年現在も2023年から大きく変わっていない。雑所得として総合課税、損失の繰越控除なし——この2点が一番痛い。個人的には分離課税化の議論が何年も続いているのに一向に動かないのが一番しんどいところだ。

項目暗号資産(2026年現在)株式・FX不動産
課税方式総合課税(雑所得)申告分離課税分離・総合選択可
最高税率約55%約20%約39%
損失繰越不可3年間可能可能(条件付き)
損益通算雑所得内のみ株式・FX間で可能限定的
年間20万円以下申告不要(給与所得者)同左同左

このテーブルを見るたびに「エンジニアで高収入だと税率が恐ろしいことになる」と思う。僕の場合、エンジニア給与と合算されるので、暗号資産で100万円利益が出ると実質手残りが40〜45万円程度になる計算だ。株式なら80万円残るのに……と比べてしまうのはしょうがない。

2025年末の国税庁ガイダンス更新で変わった部分

2025年12月に国税庁がDeFi・NFT関連の取り扱いについて追加的なFAQを公開した。「そんな細かいこと気にしても……」と思うかもしれないが、DeFiをある程度触っているエンジニアには結構重要な内容で、知っているか知らないかで申告額がわりと変わってくる。主なポイントは以下だ。

DeFi・LP関連

  • Uniswap等のAMMでLPポジションを持つ場合、流動性を提供した時点では原則課税イベントにならない(従来の解釈を明確化)
  • ただし、手数料収入(LP報酬)が発生した時点で雑所得として認識する
  • インパーマネントロスは「損失」として認識できない(あくまで価値の変動であり、実現した場合の売却損益で判断)

ステーキング報酬

  • 報酬を受け取った時点の時価で雑所得として認識(この解釈は変わらず)
  • スラッシングで没収された場合の損失処理については新たに明確化。没収時点で損失として認識可能と明示された。地味に重要

NFT

  • NFT売買の利益は引き続き雑所得
  • 自分で作成したNFTの売却は事業所得になり得ると明確化(継続・反復性があれば)

僕が半年間DeFiを触ってみた経験についてはDeFiを半年触ってわかったこと|エンジニア視点で読み解く分散金融の現実に書いたので、実態感が気になる人はそちらも参考にしてほしい。


取引履歴を手計算する地獄から逃げるためにPythonで自動化した

去年の申告で一番苦労したのが取引履歴の集計だった。僕の場合、Binance・Coincheck・Kraken・MetaMask(on-chain)と複数の取引所・ウォレットにまたがっていて、それぞれCSV形式が微妙に違う。「なんでこんなに形式がバラバラなんだ」と思いながら、結局Pythonで統一する方向に倒した。

取得単価の計算:総平均法 vs 移動平均法

まず基礎知識として。暗号資産の取得単価計算には総平均法と移動平均法があり、原則として**移動平均法(継続適用)**を使う。ただし税務署に届け出れば総平均法も使える。

移動平均法だと取引のたびに単価を更新する必要があるので、リアルタイム計算が必要になる。これが手計算だと本当につらい。取引が100件を超えた時点でもう無理だと悟った。

import pandas as pd
from decimal import Decimal, ROUND_DOWN
from dataclasses import dataclass
from typing import List
import json

@dataclass
class Trade:
    date: str
    trade_type: str  # 'buy' | 'sell' | 'reward'
    amount: Decimal  # 取引量(BTC等)
    price_jpy: Decimal  # 1単位あたり円価格
    fee_jpy: Decimal = Decimal('0')

@dataclass
class TaxResult:
    total_profit: Decimal
    total_loss: Decimal
    net_income: Decimal
    trade_details: List[dict]

class CryptoTaxCalculator:
    """
    移動平均法による暗号資産損益計算
    2026年確定申告対応版
    """
    
    def __init__(self):
        self.holdings: Decimal = Decimal('0')  # 保有量
        self.avg_cost: Decimal = Decimal('0')  # 平均取得単価
        self.total_cost: Decimal = Decimal('0')  # 取得総額
        self.results: List[dict] = []
    
    def process_trade(self, trade: Trade) -> dict | None:
        if trade.trade_type == 'buy':
            return self._process_buy(trade)
        elif trade.trade_type == 'sell':
            return self._process_sell(trade)
        elif trade.trade_type == 'reward':
            # ステーキング・LP報酬は受取時時価で雑所得認識
            return self._process_reward(trade)
        return None
    
    def _process_buy(self, trade: Trade) -> dict:
        cost = trade.amount * trade.price_jpy + trade.fee_jpy
        self.total_cost += cost
        self.holdings += trade.amount
        
        if self.holdings > 0:
            self.avg_cost = self.total_cost / self.holdings
        
        return {
            'date': trade.date,
            'type': 'buy',
            'amount': float(trade.amount),
            'price': float(trade.price_jpy),
            'cost': float(cost),
            'avg_cost_after': float(self.avg_cost),
            'profit_loss': 0
        }
    
    def _process_sell(self, trade: Trade) -> dict:
        if trade.amount > self.holdings:
            raise ValueError(f"売却量({trade.amount})が保有量({self.holdings})を超えています")
        
        # 売却益 = 売却収入 - (平均取得単価 × 売却量) - 手数料
        proceeds = trade.amount * trade.price_jpy - trade.fee_jpy
        cost_basis = self.avg_cost * trade.amount
        profit_loss = proceeds - cost_basis
        
        # 保有量・総コストを更新
        self.holdings -= trade.amount
        self.total_cost -= cost_basis
        
        result = {
            'date': trade.date,
            'type': 'sell',
            'amount': float(trade.amount),
            'price': float(trade.price_jpy),
            'proceeds': float(proceeds),
            'cost_basis': float(cost_basis),
            'profit_loss': float(profit_loss)
        }
        self.results.append(result)
        return result
    
    def _process_reward(self, trade: Trade) -> dict:
        # ステーキング等の報酬:受取時の時価を所得として記録
        # 同時に、この価格でbuyしたものとして保有量に追加
        income = trade.amount * trade.price_jpy
        
        buy_trade = Trade(
            date=trade.date,
            trade_type='buy',
            amount=trade.amount,
            price_jpy=trade.price_jpy,
            fee_jpy=Decimal('0')
        )
        self._process_buy(buy_trade)
        
        result = {
            'date': trade.date,
            'type': 'reward',
            'amount': float(trade.amount),
            'price': float(trade.price_jpy),
            'income': float(income),  # 雑所得として申告する金額
            'profit_loss': float(income)
        }
        self.results.append(result)
        return result
    
    def get_annual_summary(self, year: int) -> dict:
        year_results = [
            r for r in self.results 
            if r['date'].startswith(str(year))
        ]
        
        total_profit = sum(r['profit_loss'] for r in year_results if r['profit_loss'] > 0)
        total_loss = sum(r['profit_loss'] for r in year_results if r['profit_loss'] < 0)
        net = total_profit + total_loss
        
        return {
            'year': year,
            'total_profit': total_profit,
            'total_loss': abs(total_loss),
            'net_income': net,
            'taxable_income': max(0, net),  # 損失繰越不可のため0以下は0
            'transaction_count': len(year_results)
        }

# 使用例
if __name__ == '__main__':
    calc = CryptoTaxCalculator()
    
    trades = [
        Trade('2025-03-10', 'buy',    Decimal('0.5'), Decimal('8000000'), Decimal('1000')),
        Trade('2025-07-20', 'buy',    Decimal('0.3'), Decimal('9500000'), Decimal('800')),
        Trade('2025-10-05', 'sell',   Decimal('0.4'), Decimal('11000000'), Decimal('1200')),
        Trade('2025-12-01', 'reward', Decimal('0.01'), Decimal('10500000'), Decimal('0')),
        Trade('2026-01-15', 'sell',   Decimal('0.2'), Decimal('12000000'), Decimal('900')),
    ]
    
    for trade in trades:
        result = calc.process_trade(trade)
        if result:
            print(json.dumps(result, ensure_ascii=False, indent=2))
    
    print("\n=== 2025年サマリー ===")
    summary = calc.get_annual_summary(2025)
    print(json.dumps(summary, ensure_ascii=False, indent=2))

実行するとこんな感じの出力が得られる:

// 2025年サマリー
{
  "year": 2025,
  "total_profit": 1053625.0,
  "total_loss": 0,
  "net_income": 1053625.0,
  "taxable_income": 1053625.0,
  "transaction_count": 3
}

ステーキング報酬の0.01 BTC × 1,050万円 = 105,000円も自動で雑所得として計算してくれる。これを手でやってたと思うとゾッとする。

取引所CSVの読み込み統合

各取引所のCSVフォーマットが違いすぎてつらい問題も、パーサーを統一することで解決できる。Coincheckはまだマシな方で、Binanceは通貨ペアの組み合わせが多すぎて最初に見たとき笑った。

import csv
from datetime import datetime

class ExchangeCSVParser:
    """複数取引所CSVを統一フォーマットに変換"""
    
    @staticmethod
    def parse_coincheck(filepath: str) -> List[Trade]:
        trades = []
        with open(filepath, encoding='utf-8') as f:
            reader = csv.DictReader(f)
            for row in reader:
                if row['操作'] in ['btc購入', 'btc売却']:
                    trade_type = 'buy' if '購入' in row['操作'] else 'sell'
                    trades.append(Trade(
                        date=row['時刻'][:10],
                        trade_type=trade_type,
                        amount=Decimal(str(abs(float(row['BTC'])))),
                        price_jpy=Decimal(str(abs(float(row['JPY'])) / abs(float(row['BTC'])))),
                        fee_jpy=Decimal(str(abs(float(row['手数料(JPY)']))))
                    ))
        return trades
    
    @staticmethod
    def parse_binance(filepath: str) -> List[Trade]:
        # Binanceの場合はQuoteがUSDTなので円換算レートが必要
        # ここは実務では為替APIを叩いて当時のレートを取得する
        # 簡略化のため省略
        pass

Binanceの場合、USDT建て取引の円換算が必要で、「取引時点のUSDT/JPYレートをどこから取ってくるか」が地味に難しい問題だった。結局、日銀が公開している過去の為替データを使ったが、これを手で調べていた過去の自分は一体何をしていたんだという気持ちになる。


税額シミュレーション:エンジニア給与と合算すると現実が見える

年収別に暗号資産利益100万円に対する実効税率を計算してみた。これを見るとモチベーションが微妙になるかもしれないけど、知らずに申告するよりはいい。年収1,000万円を超えてくると「半分持っていかれる」がほぼ現実になる。

xychart-beta
  title "年収別・暗号資産利益100万円に対する追加税額(万円)"
  x-axis ["年収400万", "年収600万", "年収800万", "年収1000万", "年収1500万"]
  y-axis "追加税額(万円)" 0 --> 60
  bar [20, 30, 39, 43, 55]

年収1,000万円のエンジニアが暗号資産で100万円稼いだ場合、追加で43万円が税金として消える。株式なら20万円で済むのに、この差は本当に大きい。キャッシュフロー設計を誤らないためにも、自分の年収帯での実効税率は先に把握しておいた方がいい。

節税で使える合法的な手段(2026年時点)

完全に節税できる魔法はないけど、合法的にできることをリストアップしておく。どれも「劇的に変わる」というより「じわじわ効く」感じのものばかりだが、知っているか知らないかの差は大きい。

1. 年内の損切り活用 含み損がある銘柄を年内に売却することで、その年の雑所得を圧縮できる。翌年以降に繰り越せないので、どのタイミングで損切りするかの判断が重要になる。12月末に慌ててやる羽目にならないよう、年間を通じて意識しておきたい。

2. 雑所得内での損益通算 FXの利益と暗号資産の損失は通算できない(2026年現在)。ただし、暗号資産同士(ビットコインの損失 vs イーサリアムの利益)は通算できる。複数銘柄を持っているなら、これは積極的に使っていい。

3. 個人事業主として申告 NFT制作・販売やステーキングサービス運営等を継続的に行っている場合、事業所得として申告できる可能性がある。事業所得なら青色申告特別控除(最大65万円)や必要経費の計上ができる。ただし税務署からの反論リスクもあるので、正直これは税理士に相談した方がいい。

4. 法人化の検討 年間利益が大きい場合は法人化の検討も。法人税率は実効約30%で、総合課税の最高税率55%より低い。ただし法人維持コストがかかるので損益分岐点がある。個人的には年間利益300〜400万円あたりから真剣に試算する価値が出てくる印象だ。


申告作業フロー:実際にやったこと

フローとして整理するとこうなる。「MetaMask on-chain履歴」の取り込みが一番手間がかかった部分で、ここだけで半日近く使ってしまった。

flowchart TD
    A[各取引所CSVダウンロード] --> B[Pythonで統一フォーマット変換]
    B --> C[移動平均法で損益計算]
    C --> D[ステーキング・LP報酬の集計]
    D --> E[年間サマリーCSV出力]
    E --> F{確認・修正}
    F -- 修正あり --> B
    F -- OK --> G[国税庁e-Tax入力]
    G --> H[雑所得欄に記入]
    H --> I[申告完了]
    
    subgraph データソース
        J[Coincheck CSV]
        K[Binance CSV]
        L[MetaMask On-chain履歴]
        M[ステーキング報酬記録]
    end
    
    J --> A
    K --> A
    L --> A
    M --> D
    
    subgraph 注意点
        N[DeFi LP報酬は受取時時価]
        O[スワップ取引は課税イベント]
        P[海外取引所も申告必要]
    end

一番見落としがちなのが「スワップ取引(ETH→USDCなど)も課税イベント」という点だ。日本円に換えていなくても、ある暗号資産から別の暗号資産に交換した時点で売却したとみなされる。DeFiをよく使っている人はこの取引数が膨大になるので、早めに記録を整理しておくことを強くすすめる。僕は去年これに気づくのが遅くて申告直前に大量の取引を遡って確認する羽目になった。

MetaMask on-chain取引の取得

import requests
from datetime import datetime

def get_etherscan_transactions(address: str, api_key: str, start_block: int = 0) -> list:
    """
    Etherscan APIでウォレットの取引履歴を取得
    """
    url = 'https://api.etherscan.io/api'
    params = {
        'module': 'account',
        'action': 'txlist',
        'address': address,
        'startblock': start_block,
        'endblock': 99999999,
        'sort': 'asc',
        'apikey': api_key
    }
    
    response = requests.get(url, params=params)
    data = response.json()
    
    if data['status'] != '1':
        raise ValueError(f"API Error: {data['message']}")
    
    transactions = []
    for tx in data['result']:
        # 成功した取引のみ
        if tx['isError'] == '0':
            timestamp = datetime.fromtimestamp(int(tx['timeStamp']))
            transactions.append({
                'date': timestamp.strftime('%Y-%m-%d'),
                'hash': tx['hash'],
                'value_eth': float(tx['value']) / 1e18,
                'gas_fee_eth': float(tx['gasUsed']) * float(tx['gasPrice']) / 1e18,
                'from': tx['from'],
                'to': tx['to']
            })
    
    return transactions

# ERC-20トークン転送も取得する場合はaction='tokentx'で同様に取得

ガス代(手数料)は取得単価に含めるか、別途経費として計上するかの判断が必要で、ここも正直まだ完全にクリアな解釈が出ていない部分だ。国税庁のFAQでは「取得費に含める」という方向性が示されているが、細かいケースでは税理士判断になる。「とりあえず取得費に含めておいた」というのが今の僕の運用で、グレーゾーンを踏んでいる自覚はある。


まとめ

2026年の暗号資産確定申告で実際に痛感したことをまとめる。

要点3つ

  1. 税制は2026年も総合課税のまま。最高55%は変わらず、損失繰越不可も健在。エンジニア高収入層は特に合算後の税率に注意が必要

  2. DeFi・ステーキング報酬の取り扱いは2025年末のガイダンスで一部明確化。LP報酬は受取時時価で雑所得認識、スラッシング損失は計上可能になった

  3. 取引履歴の集計は早期に自動化すべき。特に複数取引所・DeFiを使っている場合、手計算は現実的ではない。Pythonで移動平均法ベースの計算器を作っておくと申告直前に焦らなくて済む

次のアクション

  • 年内に全取引所のCSVを月次でバックアップする習慣をつける
  • MetaMask等のon-chain取引はEtherscan APIで定期取得を自動化しておく
  • 年間利益が一定規模(目安300万円以上)になってきたら税理士に相談する価値がある
  • 法人化の損益分岐点を自分の収益規模で試算しておく

「毎年3月になって焦る」を繰り返している人は、ぜひ年間を通じた自動集計の仕組みを先に作っておいてほしい。今年の自分がその典型だったので説得力はある……と思う。自動化の発想という意味では不動産投資の確定申告2026年版|Python経費管理自動化の実践ガイドも参考になる。皆さんはどうやって取引履歴を管理してます?もっとスマートな方法があれば教えてほしい。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事