DeFi の税計算のためのコインの historical price 取得のメモ

## 背景

DeFi で yield farming とか色々取引して取引量が多くて大変! 流石に手動ではめんどくさすぎるのでスクリプトで処理したい.

etherscan で DeFi トランザクションを集計して税計算を行う(準備中. 進捗 30%)
https://qiita.com/syoyo/items/288f14fe76fa0d4d02bf

DeFi だとオンチェーンで取引記録が残っていてスクリプトで集計とかやりやすいですよね.

DeFi 税計算で, 各取引時点での timestamp での時価を計算する必要があります.
(Ethereum みたいに十分発展したチェーンであれば, ブロックの timestamp の値は十分信頼できる)
手動でコインのレート取得めんどいので Python でぺろっと価格履歴取得したい.

とりあえずは Coingecko が良いのがわかりました.

いずれは TokenTax https://tokentax.co/ や Koinly https://koinly.io/ みたいなアドレス入れたらよろしく集計や税計算してくれるサービスが出てくるでしょう. それまでのつなぎです. 

## 日付の変換

Python 日付、時刻の処理
https://qiita.com/xza/items/9618e25a8cb08c44cdb0

ありがとうございます.

## hisotirical price service

* Coinmarketcap
* Coingecko

有名どころとしてはこのふたつでしょうか.

### Coinmarketcap

Free API では価格履歴は取得できない. また取得できても 90 日前まで.


Web Scraping Crypto Prices With Python
https://towardsdatascience.com/web-scraping-crypto-prices-with-python-41072ea5b5bf

Downloading historical data from Coinmarketcap
https://medium.com/coinmonks/downloading-historical-data-from-coinmarketcap-41a2b0111baf

web をクロールする手もあるがサイトのレイアウト変わるとアウトなのでおすすめできない.
(上記 2018 年の記事そのままではだめだった)

### Coingecko

Free で使えます!

https://www.coingecko.com/ja/api

色々なところで使われているので信頼性抜群です.

https://pypi.org/project/pycoingecko/

python wrapper ありました.

$sushi 🍣 の価格を取得してみます.

```py
import pprint
import datetime

from pycoingecko import CoinGeckoAPI
cg = CoinGeckoAPI()

# ids = 'bitcoin', 'ethereum', 'sushi', 'usdc', ...

dt = "01-09-2020"
p = cg.get_coin_history_by_id('sushi', dt)
pprint.pprint(p)
````

```
{'community_data': {'facebook_likes': None,
                    'reddit_accounts_active_48h': None,
                    'reddit_average_comments_48h': 0.0,
                    'reddit_average_posts_48h': 0.0,
                    'reddit_subscribers': None,
                    'twitter_followers': None},
 'developer_data': {'closed_issues': None,
                    'code_additions_deletions_4_weeks': {'additions': None,
                                                         'deletions': None},
                    'commit_count_4_weeks': None,
                    'forks': None,
                    'pull_request_contributors': None,
                    'pull_requests_merged': None,
                    'stars': None,
                    'subscribers': None,
                    'total_issues': None},
 'id': 'sushi',
 'image': {'small': 'https://assets.coingecko.com/coins/images/12271/small/512x512_Logo_no_chop.png?1606986688',
           'thumb': 'https://assets.coingecko.com/coins/images/12271/thumb/512x512_Logo_no_chop.png?1606986688'},
 'localization': {'ar': 'Sushi',
                  'de': 'Sushi',
                  'en': 'Sushi',
                  'es': 'Sushi',
                  'fr': 'Sushi',
                  'hu': 'Sushi',
                  'id': 'Sushi',
                  'it': 'Sushi',
                  'ja': 'スシ',
                  'ko': 'Sushi',
                  'nl': 'Sushi',
                  'pl': 'Sushi',
                  'pt': 'Sushi',
                  'ro': 'Sushi',
                  'ru': 'Sushi',
                  'sv': 'Sushi',
                  'th': 'Sushi',
                  'tr': 'Sushi',
                  'vi': 'Sushi',
                  'zh': 'Sushi',
                  'zh-tw': 'Sushi'},
 'market_data': {'current_price': {'aed': 24.46630178296021,
                                   'ars': 493.7104139902546,
                                   'aud': 9.030324687533575,
                                   'bch': 0.024291458414488095,
                                   'bdt': 564.3208256796754,
                                   'bhd': 2.5111396340207204,
                                   'bits': 571.3316651177959,
                                   'bmd': 6.660759496613372,
                                   'bnb': 0.28795507724478886,
                                   'brl': 36.585553687048176,
                                   'btc': 0.0005713316651177959,
                                   'cad': 8.684831092444227,
                                   'chf': 6.021686265951307,
                                   'clp': 5171.402642952888,
                                   'cny': 45.61754356445592,
                                   'czk': 146.5746752546245,
                                   'dkk': 41.53516406898157,
                                   'dot': 1.058781513465198,
                                   'eos': 2.068779919211652,
                                   'eth': 0.015305428107455554,
                                   'eur': 5.579951356895393,
                                   'gbp': 4.984452814860172,
                                   'hkd': 51.62321736457735,
                                   'huf': 1982.7082793568998,
                                   'idr': 96838.1646427797,
                                   'ils': 22.336856971892868,
                                   'inr': 489.15152376039265,
                                   'jpy': 706.0305155017685,
                                   'krw': 7912.582636406864,
                                   'kwd': 2.033623124949012,
                                   'link': 0.42341660687360194,
                                   'lkr': 1237.186600374392,

...

 'name': 'Sushi',
 'public_interest_stats': {'alexa_rank': 134398, 'bing_matches': None},
 'symbol': 'sushi'}
```

Voala!

#### 制約

過去の価格は 1 日の平均(?)価格のようです.
より詳細に 1 時間単位などでの取得は 90 日前までっぽい.

最近(2023)だと 10 回くらい call すると rate limit にかかります(5 分くらい?).
必要に応じてリトライや, すでに取得した価格は日付でキャッシュしておくなどが必要です.

## TODO


* https://3commas.io/ などで各取引所の履歴を取得してより正確な時価計算できるか試す.
* ChainLink などの price oracle でより reliable な価格履歴を取得した方がいいかも...?
* Uniswap V2 では on chain に価格情報を書き込んでいるとあるので, これらの情報をうまく抜き出してみたい https://uniswap.org/docs/v2/core-concepts/oracles/