@@ -44,7 +44,7 @@ def parse_frano_transactions(reader):
4444 'price' : float (row [4 ]),
4545 'total' : float (row [5 ]),
4646 'linked_symbol' : (row [6 ] if row [6 ] != '' else None ),
47- });
47+ })
4848
4949 return parsed
5050
@@ -78,7 +78,7 @@ def parse_google_transactions(reader):
7878 'quantity' : quantity ,
7979 'price' : price ,
8080 'total' : ((quantity * price ) + (commission_multiplier * commission )),
81- });
81+ })
8282
8383 return parsed
8484
@@ -138,17 +138,19 @@ def parse_ameritrade_transactions(reader):
138138 'price' : price ,
139139 'total' : ((quantity * price ) + (commission_multiplier * commission )),
140140 'linked_symbol' : linked_symbol ,
141- });
141+ })
142142
143143 return parsed
144144
145145def parse_zecco_transactions (reader ):
146+ split_map = { }
147+
146148 parsed = []
147149 for row in reader :
150+ as_of_date = datetime .strptime (row [0 ], '%m/%d/%Y' ).date ()
148151 account_type = row [1 ]
149152 transaction_type = row [2 ]
150153 description_field = row [5 ]
151- date_field = row [0 ]
152154 symbol_field = row [3 ]
153155 quantity_field = row [7 ]
154156 price_field = row [8 ]
@@ -187,6 +189,12 @@ def parse_zecco_transactions(reader):
187189 price = 1.0
188190 commission = 0.0
189191 linked_symbol = symbol_field
192+
193+
194+ # splits are processed after all the parsing is done, just record and skip them
195+ elif transaction_type in [ 'Security Journal' ] and description_field .endswith ('SPLIT' ):
196+ _record_split (split_map , as_of_date , symbol_field , float (quantity_field ))
197+ continue
190198
191199 # otherwise just skip it for now
192200 else :
@@ -197,15 +205,26 @@ def parse_zecco_transactions(reader):
197205 commission_multiplier = - 1.0
198206
199207 parsed .append ({
200- 'date' : datetime . strptime ( date_field , '%m/%d/%Y' ). date () ,
208+ 'date' : as_of_date ,
201209 'type' : type ,
202210 'symbol' : symbol ,
203211 'quantity' : quantity ,
204212 'price' : price ,
205213 'total' : ((quantity * price ) + (commission_multiplier * commission )),
206214 'linked_symbol' : linked_symbol ,
207- });
208-
215+ })
216+
217+ # split processing - adjust price and quantity of all pre-split transactions
218+ # the double loop is intentional since a stock can split more than once, start processing by earliest date
219+ splits = [ split for sub_list in split_map .values () for split in sub_list ]
220+ for split in sorted (splits , key = (lambda split : split .as_of_date )):
221+ for transaction in parsed :
222+ if transaction .get ('symbol' ) == split .from_symbol and transaction .get ('date' ) <= split .as_of_date :
223+ factor = split .to_quantity / split .from_quantity
224+ transaction ['symbol' ] = split .to_symbol
225+ transaction ['quantity' ] = transaction .get ('quantity' ) * factor
226+ transaction ['price' ] = transaction .get ('price' ) / factor
227+
209228 return parsed
210229
211230def parse_scottrade_transactions (reader ):
@@ -247,7 +266,7 @@ def parse_scottrade_transactions(reader):
247266 'quantity' : (price * quantity ),
248267 'price' : 1.0 ,
249268 'total' : (price * quantity ),
250- });
269+ })
251270
252271 symbol = symbol_field
253272 type = 'BUY'
@@ -274,7 +293,7 @@ def parse_scottrade_transactions(reader):
274293 'price' : price ,
275294 'total' : ((quantity * price ) + (commission_multiplier * commission )),
276295 'linked_symbol' : linked_symbol ,
277- });
296+ })
278297
279298 return parsed
280299
@@ -320,7 +339,7 @@ def parse_charles_transactions(reader):
320339 'quantity' : (price * quantity ),
321340 'price' : 1.0 ,
322341 'total' : (price * quantity ),
323- });
342+ })
324343
325344 type = 'BUY'
326345 commission = 0.0
@@ -346,7 +365,7 @@ def parse_charles_transactions(reader):
346365 'price' : price ,
347366 'total' : ((quantity * price ) + (commission_multiplier * commission )),
348367 'linked_symbol' : linked_symbol ,
349- });
368+ })
350369
351370 return parsed
352371
@@ -403,7 +422,7 @@ def parse_fidelity_transactions(reader):
403422 'price' : price ,
404423 'total' : total ,
405424 'linked_symbol' : linked_symbol ,
406- });
425+ })
407426
408427 return parsed
409428
@@ -435,7 +454,7 @@ def parse_mercer_401_transactions(reader):
435454 'quantity' : amount ,
436455 'price' : 1.0 ,
437456 'total' : amount ,
438- });
457+ })
439458
440459 type = 'BUY'
441460
@@ -455,7 +474,7 @@ def parse_mercer_401_transactions(reader):
455474 'price' : 1.0 ,
456475 'total' : amount ,
457476 'linked_symbol' : symbol ,
458- });
477+ })
459478
460479 type = ('BUY' if action == 'DIVIDEND' else 'SELL' )
461480 quantity = abs (quantity )
@@ -472,10 +491,53 @@ def parse_mercer_401_transactions(reader):
472491 'price' : price ,
473492 'total' : amount ,
474493 'linked_symbol' : linked_symbol ,
475- });
494+ })
476495
477496 return parsed
478497
498+ #-----------------\
499+ # VALUE OBJECTS |
500+ #-----------------/
501+
502+ class Split :
503+ def __init__ (self , as_of_date , symbol , quantity ):
504+ self .as_of_date = as_of_date
505+
506+ if quantity > 0 :
507+ self .to_symbol = symbol
508+ self .to_quantity = quantity
509+
510+ else :
511+ self .from_symbol = symbol
512+ self .from_quantity = abs (quantity )
513+
514+ def __repr__ (self ):
515+ return "Split: %.4f of %s to %.4f of %s" % (self .from_quantity , self .from_symbol , self .to_quantity , self .to_symbol )
516+
479517#-------------------\
480518# LOCAL FUNCTIONS |
481519#-------------------/
520+
521+ def _record_split (split_map , as_of_date , symbol , quantity ):
522+ splits_on_date = split_map .get (as_of_date , None )
523+ if splits_on_date == None :
524+ splits_on_date = [ Split (as_of_date , symbol , quantity ) ]
525+ split_map [as_of_date ] = splits_on_date
526+
527+ else :
528+ found = False
529+ for split in splits_on_date :
530+ if quantity > 0 and split .from_symbol != None and split .from_symbol .startswith (symbol ):
531+ split .to_symbol = symbol
532+ split .to_quantity = quantity
533+ found = True
534+ break
535+
536+ elif split .to_symbol != None and symbol .startswith (split .to_symbol ):
537+ split .from_symbol = symbol
538+ split .from_quantity = abs (quantity )
539+ found = True
540+ break
541+
542+ if not found :
543+ splits_on_date .append (Split (as_of_date , symbol , quantity ))
0 commit comments