Ledger is a powerful command-line plain-text accounting system. It has extensive built-in support for tracking complex ‘commodities’, such as currencies and inventory items, but does not support FIFO valuation.

This is a small Python script that can take input from Ledger, break down the prices of commodities based on FIFO accounting, and validate that FIFO accounting has been adhered to:

#!/usr/bin/env python3 import csv from decimal import Decimal import sys from tabulate import tabulate reader = csv . reader ( sys . stdin ) rows = list ( reader ) rows . sort ( key = lambda x : x [ 0 ]) # Sort by date balances = [] results = [] for row in rows : # Parse row value = row [ 2 ] . split ()[ 0 ] if value [ 0 ] not in '-0123456789' : # Has commodity symbol commodity = value [ 0 ] value = Decimal ( value [ 1 :]) else : # Has commodity name commodity = row [ 2 ] . split ()[ 1 ] value = Decimal ( value ) price = row [ 2 ][ row [ 2 ] . index ( '{' ) + 1 : row [ 2 ] . index ( '}' )] # Process row if value > 0 : stock_item = [ row [ 0 ], commodity , price , value ] i = len ( balances ) balances . append ( stock_item ) else : i , stock_item = next ((( i , x ) for i , x in enumerate ( balances ) if x [ 1 ] == commodity ), None ) if not stock_item : print ( 'Got {} {{{}}} on {} but none available' . format ( value , price , row [ 0 ])) sys . exit ( 1 ) if stock_item [ 2 ] != price : print ( 'Got {} {{{}}} on {} but expected {} (FIFO violation)' . format ( value , price , row [ 0 ], balances [ 0 ][ 2 ])) sys . exit ( 1 ) if stock_item [ 3 ] + value < 0 : print ( 'Got {} {{{}}} on {} but only {} available' . format ( value , price , row [ 0 ], stock_item [ 3 ])) sys . exit ( 1 ) stock_item [ 3 ] += value # Report transaction for j , x in enumerate ( balances ): result = (( row [ 0 ] . replace ( '/' , '-' ), row [ 1 ]) if j == 0 else ( '' , '' )) + (( value , commodity ) if i == j else ( '' , '' )) + ( x [ 3 ], x [ 1 ], x [ 2 ]) results . append ( result ) results . append (( '' , '' , '' , '' , '' , '' , '' )) if stock_item [ 3 ] == 0 : del balances [ i ] del results [ - 1 ] print ( tabulate ( results , colalign = ( 'left' , 'left' , 'decimal' , 'left' , 'decimal' , 'left' , 'left' )))

Use it as follows:

ledger csv --csv-format '%(quoted(date)),%(quoted(payee)),%(quoted(display_amount))

' AccountNameHere | python fifotool.py

Example output: