Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export transactions, 8949 sales and some calculations as a ODS spreadsheet #15

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions CoinTaxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

import os
import yaml
import copy

import exchanges
from formats import fill_8949, turbo_tax
from formats import fill_8949, turbo_tax, spreadsheet


def get_exchange(name, config):
Expand Down Expand Up @@ -120,7 +121,7 @@ def main():
"""Main function to collect information and create the forms."""

parser = argparse.ArgumentParser(description='CoinTaxes', formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--input', default='config.yml', required=True,
parser.add_argument('--input', default='config.yml',
help='Configuration file to read.')
parser.add_argument('--output', default='output',
help='Directory to output autogenerated files.')
Expand Down Expand Up @@ -160,8 +161,8 @@ def main():

# Get the full order information to be used on form 8949
full_orders = fill_8949.get_cost_basis(
sells_sorted,
buys_sorted,
copy.deepcopy(sells_sorted), # pass a copy to preserve the original
copy.deepcopy(buys_sorted), # pass a copy to preserve the original
basis_type='highest',
tax_year=config['year']
)
Expand All @@ -174,8 +175,11 @@ def main():
turbo_tax.make_txf(full_orders, year=config['year'])

# Make the 8949 forms
fill_8949.make_pdf(full_orders, "test", config['name'], config['social'], config['year'])
if 'fill_8949' in config and config['fill_8949']:
fill_8949.make_pdf(full_orders, "test", config['name'], config['social'], config['year'])

if 'spreadsheet' in config and config['spreadsheet']:
spreadsheet.make_spreadsheet(full_orders, buys_sorted, sells_sorted, year=config['year'])

if __name__ == '__main__':
main()
2 changes: 2 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ six = "==1.10.0"
websocket-client = "==0.40.0"
pyyaml = "*"
pycryptodome = "==3.6.6"
pyexcel = "0.6.6"
pyexcel-ods3 = "0.6.0"

[dev-packages]

Expand Down
144 changes: 131 additions & 13 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 6 additions & 2 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ name: 'nitrocode'
# Full hyphen delimited social security number or leave empty
social: '000-00-0000'
# tax year
year: 2017
# use turbo tax?
year: 2018
# generate turbo tax file?
txf: true
# generate PDFs of Form 8949?
fill_8949: false
# generate a spreadsheet with all data and some calculations?
spreadsheet: true

# view only api keys
# comment out unused exchanges by prefixing lines with a pound sign #
Expand Down
68 changes: 68 additions & 0 deletions formats/spreadsheet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import os
import datetime
import pyexcel as p
from pyexcel._compact import OrderedDict

def make_spreadsheet(full_orders, buys_sorted, sells_sorted, output_dir='output', year=2017):
out_file = os.path.join(output_dir, '_'.join(['Transactions', 'Crypto', str(year)])) + '.ods'
book = OrderedDict()

# "Transactions" sheet
trans_sheet = []
book['Transactions'] = trans_sheet
trans_sheet.append([ 'Order Time UTC', 'product', 'currency', 'currency_pair', 'buysell', 'cost', 'amount', 'cost_per_coin' ])
# 'order_time', 'product', 'currency', 'currency_pair', 'buysell', 'cost', 'amount', 'cost_per_coin'
buys_sells_sorted = sorted(buys_sorted+sells_sorted, key=lambda order: order['order_time'])
for order in buys_sells_sorted:
trans_sheet.append([
order['order_time'].isoformat(),
order['product'],
order['currency'],
order['currency_pair'],
order['buysell'],
order['cost'],
order['amount'],
order['cost_per_coin']
])

# "8949" sheet
form_8949_sheet = []
book['8949'] = form_8949_sheet
form_8949_sheet.append([ 'Description', 'Date bought', 'Date sold', 'Proceeds', 'Cost basis', 'Gain/Loss' ])

# Full order is [description, date acquired, date sold, proceeds, cost basis, gain/loss] (populated in fill_8949.py)
DESC, DATE_ACQ, DATE_SOLD, PROCEEDS, COST_BASIS, GAIN_LOSS = range(5+1)
form_8949_sales_by_month = { month_num: { 'first_idx': -1, 'last_idx': -1, 'proceeds': 0, 'gain_loss': 0 } for month_num in range(1,12+1) }
total_8949_proceeds = 0
total_8949_gain_loss = 0

for idx, full_order in enumerate(full_orders):
form_8949_sheet.append(full_order)
# caclulate start/end indices of sales by month
sale_dt = datetime.datetime.strptime(full_order[DATE_SOLD], '%m/%d/%Y')
month_sales = form_8949_sales_by_month[sale_dt.month]
if month_sales['first_idx'] == -1:
month_sales['first_idx'] = idx

month_sales['last_idx'] = idx
month_sales['proceeds'] += full_order[PROCEEDS]
total_8949_proceeds += full_order[PROCEEDS]
month_sales['gain_loss'] += full_order[GAIN_LOSS]
total_8949_gain_loss += full_order[GAIN_LOSS]

# "Calculated" sheet
calc_sheet = []
book['Calculated'] = calc_sheet
calc_sheet.append([ 'Total 8949 gain/loss:', "=SUM($'8949'.F2:F%d)" % (len(form_8949_sheet)), total_8949_gain_loss ])
calc_sheet.append([ 'Total 8949 proceeds:', "=SUM($'8949'.D2:D%d)" % (len(form_8949_sheet)), total_8949_proceeds ])
for month_num in range(1, 12+1):
month_sales = form_8949_sales_by_month[month_num]
month_first_idx = month_sales['first_idx']
month_last_idx = month_sales['last_idx']
if month_first_idx != -1:
calc_sheet.append([ 'Total 8949 proceeds in month #%d:' % (month_num), "=SUM($'8949'.D%d:D%d)" % (month_first_idx+2, month_last_idx+2), month_sales['proceeds'] ])
# calc_sheet.append([ 'Total trans amounts:', "=SUM($'Transactions'.F2:F%d)" % (len(buys_sells_sorted)) ])

p.save_book_as(bookdict=book, dest_file_name=out_file)
print("Saved spreadsheet as %s" %(out_file) )