git ssb

0+

cel / ledger-scripts



Tree: f1521af2d73e96acaa4e96dfc60dbfc50373845a

Files: f1521af2d73e96acaa4e96dfc60dbfc50373845a / mtgox2ledger

6817 bytesRaw
1#!/usr/bin/env perl
2
3use strict;
4use warnings;
5use Time::Piece;
6
7my $broker_account = 'Assets:Broker:Mt.Gox';
8my $fee_account = 'Expenses:Broker:Mt.Gox';
9my $usd_account = 'Assets';
10my $btc_account = 'Assets';
11my $other_account = 'Assets';
12
13my $dateformat = '%Y-%m-%d %H:%M:%S';
14my $max_buffer_size = 30;
15
16if ($#ARGV != 1) {
17 print STDERR "Usage: $0 mtgox-history_BTC.csv mtgox-history_USD.csv\n";
18 exit 1;
19}
20
21$| = 1;
22
23# Make sure the arguments are the right ones
24my ($btc_file, $usd_file) = @ARGV;
25if ($btc_file !~ /btc/i or $btc_file =~ /usd/i or
26 $usd_file !~ /usd/i or $usd_file =~ /btc/i) {
27 print STDERR "Is $btc_file the BTC history and $usd_file the USD history?\n";
28 my $input = <STDIN>;
29 defined $input and $input =~ /^y/ or exit 1;
30}
31
32sub read_entry {
33 my $fd = shift;
34 my $prev_line = shift // '';
35 my $line = <$fd>;
36 return unless defined $line;
37 $line = $prev_line . $line;
38 my ($index, $date, $type, $info, $value, $balance) =
39 map { s/^"(.*)"$/$1/; $_ } split ',', $line;
40
41 # retry if caught a header
42 if ($index eq 'Index') {
43 return read_entry($fd)
44 }
45
46 # combine multiple lines as theyare sometimes broken
47 if (!defined $value) {
48 return read_entry($fd, $line);
49 }
50
51 $date = Time::Piece->strptime($date, $dateformat);
52 my $date_str = $date->ymd('/');
53
54 my $address;
55 my ($short_info, $tid) = $info =~ /^(.*): \[tid:(.*?)\]/;
56 if (!$tid) {
57 $address = $info;
58 $tid = '';
59 }
60 if ($type eq 'spent' or $type eq 'out' or
61 $type eq 'fee' or $type eq 'withdraw') {
62 $value *= -1;
63 }
64
65 return {
66 index => $index,
67 type => $type,
68 date => $date,
69 date_str => $date_str,
70 tid => $tid,
71 value => $value,
72 address => $address,
73 info => $short_info,
74 };
75}
76
77open BTC, '<', $btc_file or die("Unable to open BTC file: $!");
78open USD, '<', $usd_file or die("Unable to open USD file: $!");
79
80my ($btc_entry, $usd_entry);
81
82sub get_next_entry {
83 if (!eof USD and (!defined $usd_entry or
84 (defined $btc_entry and
85 $btc_entry->{date} > $usd_entry->{date}))) {
86
87 $usd_entry = read_entry \*USD;
88 $usd_entry->{currency} = 'USD';
89 return $usd_entry;
90
91 } elsif (!eof BTC) {
92 $btc_entry = read_entry \*BTC;
93 $btc_entry->{currency} = 'BTC';
94 return $btc_entry;
95 }
96}
97
98# Buffer for entries
99my @buffer;
100
101# Does an entry match a tid and type
102sub entry_matches {
103 my ($entry, $tid, $type_n) = @_;
104 my $type = $entry->{type};
105 return ($entry->{tid} eq $tid) &&
106 ($type_n > 0 ? ($type eq 'in' or $type eq 'earned') :
107 $type_n == 0 ? ($type eq 'out' or $type eq 'spent') :
108 $type_n < 0 ? ($type eq 'fee') : 0);
109}
110
111# Get the next entry, optionally of a specific tid and type, with buffering
112sub get_buffered_entry {
113 my ($tid, $type) = @_;
114 if (!defined $tid) {
115 # get any next entry
116 return shift(@buffer) // get_next_entry();
117 }
118
119 # find a particular kind of entry
120 # buffering non-matching ones
121 my ($matching, $entry);
122 for my $entry (@buffer) {
123 if (entry_matches($entry, $tid, $type)) {
124 $matching = $entry;
125 last;
126 }
127 }
128 if ($matching) {
129 # remove matching entry from buffer
130 @buffer = grep { $_ != $matching } @buffer;
131 return $matching;
132 }
133
134 do {
135 $entry = get_next_entry();
136 if ($entry and !entry_matches($entry, $tid, $type)) {
137 push @buffer, $entry;
138 } else {
139 return $entry;
140 }
141 } while (defined $entry);
142
143 return $entry;
144}
145
146sub format_money {
147 my ($amount, $currency) = @_;
148 if ($currency eq 'USD') {
149 return sprintf("\$%f", $amount);
150 } else {
151 return sprintf("%.8f ", $amount) . $currency;
152 }
153}
154
155# Print a sale transaction consisting of input, output, and fee
156sub print_sale {
157 my ($in, $out, $fee) = @_;
158 my $fee_amount = -$fee->{value} if $fee;
159 my $in_amount = $in->{value};
160 my $out_amount = $out->{value};
161 if (!$fee) {
162 } elsif ($in->{currency} eq $fee->{currency}) {
163 $in_amount -= $fee_amount;
164 } else {
165 $out_amount -= $fee_amount;
166 }
167 printf "%s %s\n", $in->{date_str}, $in->{info};
168 printf " %-32s %16s\n", $broker_account, format_money($in_amount, $in->{currency});
169 printf " %-32s %16s\n", $broker_account, format_money($out_amount, $out->{currency});
170 printf " %-32s %16s\n", $fee_account, format_money($fee_amount, $fee->{currency}) if ($fee);
171 print "\n";
172}
173
174# Print a single transaction
175sub print_entry {
176 my $entry = shift;
177 return unless $entry;
178
179 my $type = $entry->{type};
180 my $currency = $entry->{currency};
181 my $account;
182 my $description;
183
184 if ($currency eq 'USD') {
185 $account = $btc_account;
186 if ($type eq 'in' or $type eq 'deposit') {
187 $description = 'Deposit';
188 } elsif ($type eq 'out' or $type eq 'withdraw') {
189 $description = 'Withdraw';
190 } elsif ($type eq 'fee') {
191 $description = 'Fee';
192 $account = $fee_account;
193 } else {
194 die("Unknown USD transaction type $type tid $entry->{tid}");
195 }
196
197 } elsif ($currency eq 'BTC') {
198 $account = $usd_account;
199 my $address;
200 if ($type eq 'in' or $type eq 'deposit') {
201 $description = 'Deposit';
202 } elsif ($type eq 'out' or $type eq 'withdraw') {
203 $description = 'Withdraw';
204 } elsif ($type eq 'fee') {
205 $description = 'Fee';
206 $account = $fee_account;
207 } else {
208 die('Unknown BTC transaction type');
209 }
210 }
211
212 printf "%s %s\n", $entry->{date_str}, $description;
213 printf " %-32s %16s\n", $broker_account, format_money($entry->{value}, $currency);
214 printf " %s\n\n", $other_account;
215}
216
217# Process the entries
218while (my $entry = get_buffered_entry()) {
219 my $type = $entry->{type};
220
221 if (my $tid = $entry->{tid}) {
222 # if it has a tid, look for a set of three entries:
223 # BTC bought: in, fee, spent
224 # BTC sold: earned, fee, out
225
226 my $in_entry = ($type eq 'in' or $type eq 'earned') ?
227 $entry : get_buffered_entry($tid, 1, $entry->{date});
228 my $out_entry = ($type eq 'out' or $type eq 'spent') ?
229 $entry : get_buffered_entry($tid, 0, $entry->{date});
230 my $fee_entry = ($type eq 'fee') ?
231 $entry : get_buffered_entry($tid, -1, $entry->{date});
232
233 if ($in_entry and $out_entry) {
234 # Render the transaction
235 print_sale($in_entry, $out_entry, $fee_entry);
236 } else {
237 die("Unknown tid");
238 }
239
240 } else {
241 print_entry($entry);
242 }
243}
244
245

Built with git-ssb-web