Last active
April 17, 2021 19:26
-
-
Save kylemanna/4104c84a0d1293d5af8e53572b5674a4 to your computer and use it in GitHub Desktop.
#armory #cointracking #taxes #fml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
"""Script to convert Bitcoin Armory transaction history to a format that Cointracking.info CSV""" | |
import argparse | |
import collections | |
import csv | |
import datetime | |
import json | |
import os | |
def parse_armory_csv(fp): | |
# Consume header lines | |
last_pos = fp.tell() | |
while not fp.readline().startswith('Date'): | |
last_pos = fp.tell() | |
fp.seek(last_pos) | |
dialect = csv.excel | |
dialect.skipinitialspace = True | |
reader = csv.DictReader(fp, dialect=dialect) | |
data = [] | |
for row in reader: | |
float_keys = ['Number of Confirmations', | |
'Credit', | |
'Debit', | |
'Fee (paid by this wallet)', | |
'Wallet Balance', | |
'Total Balance' | |
] | |
for k in float_keys: | |
row[k] = float(row[k]) if len(row[k]) > 0 else 0 | |
#row['Date'] = datetime.datetime.strptime(row['Date'], '%Y-%b-%d %I:%M%p').isoformat() | |
row['Date'] = datetime.datetime.strptime(row['Date'], '%Y-%b-%d %I:%M%p') | |
data.append(row) | |
return data | |
def armory_to_cointracking(armory): | |
data = [] | |
for a in armory: | |
type = 'Unknown' | |
if a['Credit'] and not a['Debit']: | |
if a['Label'].lower().startswith('mining'): | |
type = 'Mining' | |
else: | |
type = 'Deposit' | |
elif not a['Credit'] and a['Debit']: | |
type = 'Withdrawal' | |
d = collections.OrderedDict() | |
d['Type'] = type | |
d['Buy'] = a['Credit'] | |
d['Buy_Cur.'] = 'BTC' | |
d['Sell'] = a['Debit'] | |
d['Sell_Cur.'] = 'BTC' | |
d['Fee'] = a['Fee (paid by this wallet)'] | |
d['Fee_Cur.'] = 'BTC' | |
d['Exchange'] = 'Armory@' + a['Wallet ID'] | |
d['Group'] = a['Wallet Name'] | |
d['Comment'] = a['Label'] | |
d['Date'] = a['Date'].strftime('%Y-%m-%d %H:%M:%S') | |
d['Trade ID'] = 'armory:{}:{}'.format(a['Wallet ID'], a['Transaction ID']) | |
data.append(d) | |
if '(STS)' in a['Label'] and type == 'Deposit': | |
# This was a sell to self aka transfer | |
d2 = d.copy() | |
d2['Type'] = 'Withdrawal' | |
d2['Buy'] = a['Debit'] | |
d2['Sell'] = a['Credit'] + a['Fee (paid by this wallet)'] | |
d2['Fee'] = 0 | |
data.append(d2) | |
return data | |
def export_cointracking_csv(fp, ct): | |
keys = ct[0].keys() | |
dialect=csv.unix_dialect | |
# Use the CSV library to write the header that has multiple 'Cur.' headers: | |
# "Type","Buy","Cur.","Sell","Cur.","Fee","Cur.","Exchange","Group","Comment","Date" | |
keys_mutilated = ['Cur.' if k.endswith('_Cur.') else k for k in keys] | |
csv.DictWriter(fp, fieldnames=keys_mutilated, dialect=dialect).writeheader() | |
writer = csv.DictWriter(fp, fieldnames=keys, dialect=dialect) | |
for row in ct: | |
writer.writerow(row) | |
# Delete trailing newline which causes import errors with CoinTracking | |
# creating duplicate final entries | |
fp.seek(fp.tell()-1, os.SEEK_SET) | |
fp.truncate() | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser(description='Convert Armory Transactions to CoinTracking Trade Table') | |
parser.add_argument('file', metavar='FILE', type=str, nargs='*', | |
help='CSV files to process') | |
args = parser.parse_args() | |
for f_in_name in args.file: | |
f_out_name = os.path.splitext(f_in_name)[0] + '.cointracking.csv' | |
with open(f_in_name,'r') as f_in, open(f_out_name,'w') as f_out: | |
armory = parse_armory_csv(f_in) | |
ct = armory_to_cointracking(armory) | |
export_cointracking_csv(f_out, ct) | |
#print(json.dumps(ct)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment