14
14
class SimplePortfolioLedger :
15
15
16
16
_ledger_columns = (
17
+ 'opid' ,
18
+ 'subopid' ,
17
19
'date_execution' ,
18
20
'operation' ,
19
21
'instrument' ,
@@ -37,6 +39,8 @@ class SimplePortfolioLedger:
37
39
# Column description in a dict
38
40
_ledger_columns_attrs = {
39
41
'column notes' : {
42
+ 'opid' : 'Operation id, used to keep track of multiple rows operations.' ,
43
+ 'subopid' : 'Sub operation id, used to keep track of sub operations.' ,
40
44
'date_execution' : 'The date the transaction was actually done.' ,
41
45
'date_order' : 'The date the transaction was created.' ,
42
46
#
@@ -686,14 +690,59 @@ def rm_empty_rows(self):
686
690
# MARK: OPERATIONS
687
691
# *****************
688
692
689
- def _add_row (self , value_dict ):
693
+ def _add_rows (self , data : dict | List [dict ]) -> tuple [int , pd .DataFrame ]:
694
+ """Add one or multiple rows to the ledger.
695
+
696
+ The new row(s) will have an opid incremented by one from the max opid in the ledger. Or it will be 0 if max is a nan.
697
+
698
+ Parameters
699
+ ----------
700
+ data : dict | List[dict]
701
+ The data to be added to the ledger. If a dict, the function adds only one row. If a list of dicts, the function adds as many items in the list.
702
+
703
+ Returns
704
+ -------
705
+ tuple[int, pd.DataFrame]
706
+ Returns a tuple of the inserted opid and the pd.DataFrame inserted.
707
+
708
+ Raises
709
+ ------
710
+ ValueError
711
+ If data is a list and an item in that list is not a dict, an error is raised.
712
+ ValueError
713
+ If data is neither a dict nor a List, an error is raised.
714
+ """
715
+ row_list = []
716
+
717
+ # Get the last opid and set the one used for the next row
718
+ last_opid = self ._ledger_df ['opid' ].max ()
719
+ if pd .isna (last_opid ):
720
+ new_op_id = 0
721
+ else :
722
+ new_op_id = int (last_opid ) + 1
723
+
724
+ # Fill the array of rows to be added
725
+ if isinstance (data , dict ):
726
+ row_list .append ({** data , 'opid' : new_op_id , 'subopid' : 0 })
727
+ elif isinstance (data , list ):
728
+ for i , vd in enumerate (data ):
729
+ if isinstance (vd , dict ):
730
+ row_list .append ({** vd , 'opid' : new_op_id , 'subopid' : i })
731
+ else :
732
+ raise ValueError ('When creating a row, the item in the list is not a dict.' )
733
+ else :
734
+ raise ValueError ('Unsupported type for value_dict, must be a dict or a list of dicts.' )
735
+
736
+ # Add rows to the ledger
737
+ # Specifying columns order, `*` needed because self._ledger_columns is a tuple
738
+ new_rows = pd .DataFrame (row_list )[[* self ._ledger_columns ]]
690
739
self ._ledger_df = (
691
- pd .concat ([self ._ledger_df , pd . DataFrame ([ value_dict ]) ])
740
+ pd .concat ([self ._ledger_df , new_rows ])
692
741
# Make the index coherent (else index might have duplicates)
693
742
.reset_index (drop = True )
694
743
)
744
+ return new_op_id , new_rows
695
745
696
- # This a two row operation
697
746
def buy (
698
747
self ,
699
748
date_execution ,
@@ -704,7 +753,7 @@ def buy(
704
753
account ,
705
754
commission = 0 ,
706
755
tax = 0 ,
707
- stated_total = None ,
756
+ stated_total = None , # Used to verify if inner calculation is correct
708
757
date_order = None ,
709
758
notes = '' ,
710
759
commission_notes = '' ,
@@ -780,9 +829,7 @@ def buy(
780
829
op_1 ['account' ] = account # invest
781
830
op_2 ['account' ] = account # buy
782
831
783
- # Create rows
784
- self ._add_row (op_1 ) # invest
785
- self ._add_row (op_2 ) # buy
832
+ return self ._add_rows (data = [op_1 , op_2 ])
786
833
787
834
def deposit (
788
835
self ,
@@ -814,28 +861,28 @@ def deposit(
814
861
notes : str, optional
815
862
Deposit notes. By default ''.
816
863
"""
817
- self . _add_row ( # deposit
818
- {
819
- 'date_execution ' : date_execution ,
820
- 'operation ' : 'deposit' ,
821
- 'instrument ' : instrument ,
822
- 'origin ' : '' ,
823
- 'destination ' : instrument ,
824
- 'price_in ' : instrument ,
825
- 'price ' : 1 ,
826
- 'price_w_expenses ' : 1 ,
827
- 'size ' : amount ,
828
- 'commission ' : 0 ,
829
- 'tax ' : 0 ,
830
- 'stated_total' : amount ,
831
- 'date_order ' : ( date_order if date_order is not None else date_execution ) ,
832
- 'description ' : f'Deposit { instrument } .' ,
833
- 'notes ' : notes ,
834
- 'commission_notes ' : '' ,
835
- 'tax_notes ' : '' ,
836
- 'account' : account ,
837
- }
838
- )
864
+ op_deposit = {
865
+ 'date_execution' : date_execution ,
866
+ 'operation ' : 'deposit' ,
867
+ 'instrument ' : instrument ,
868
+ 'origin ' : '' ,
869
+ 'destination ' : instrument ,
870
+ 'price_in ' : instrument ,
871
+ 'price ' : 1 ,
872
+ 'price_w_expenses ' : 1 ,
873
+ 'size ' : amount ,
874
+ 'commission ' : 0 ,
875
+ 'tax ' : 0 ,
876
+ 'stated_total ' : amount ,
877
+ 'date_order' : ( date_order if date_order is not None else date_execution ) ,
878
+ 'description ' : f'Deposit { instrument } .' ,
879
+ 'notes ' : notes ,
880
+ 'commission_notes ' : '' ,
881
+ 'tax_notes ' : '' ,
882
+ 'account ' : account ,
883
+ }
884
+
885
+ return self . _add_rows ( op_deposit )
839
886
840
887
def dividend (
841
888
self ,
@@ -864,28 +911,28 @@ def dividend(
864
911
)
865
912
866
913
price = amount / size
867
- self . _add_row ( # dividend
868
- {
869
- 'date_execution ' : date_execution ,
870
- 'operation ' : 'dividend' ,
871
- 'instrument ' : instrument_received ,
872
- 'origin ' : instrument_from ,
873
- 'destination ' : '' ,
874
- 'price_in ' : instrument_received ,
875
- 'price ' : price ,
876
- 'price_w_expenses ' : price ,
877
- 'size ' : size ,
878
- 'commission ' : 0 ,
879
- 'tax ' : 0 ,
880
- 'stated_total' : amount ,
881
- 'date_order ' : ( date_order if date_order is not None else date_execution ) ,
882
- 'description ' : f'Dividend from { instrument_from } .' ,
883
- 'notes ' : notes ,
884
- 'commission_notes ' : '' ,
885
- 'tax_notes ' : '' ,
886
- 'account' : account ,
887
- }
888
- )
914
+ op_dividend = {
915
+ 'date_execution' : date_execution ,
916
+ 'operation ' : 'dividend' ,
917
+ 'instrument ' : instrument_received ,
918
+ 'origin ' : instrument_from ,
919
+ 'destination ' : '' ,
920
+ 'price_in ' : instrument_received ,
921
+ 'price ' : price ,
922
+ 'price_w_expenses ' : price ,
923
+ 'size ' : size ,
924
+ 'commission ' : 0 ,
925
+ 'tax ' : 0 ,
926
+ 'stated_total ' : amount ,
927
+ 'date_order' : ( date_order if date_order is not None else date_execution ) ,
928
+ 'description ' : f'Dividend from { instrument_from } .' ,
929
+ 'notes ' : notes ,
930
+ 'commission_notes ' : '' ,
931
+ 'tax_notes ' : '' ,
932
+ 'account ' : account ,
933
+ }
934
+
935
+ return self . _add_rows ( op_dividend )
889
936
890
937
def sell (
891
938
self ,
@@ -897,7 +944,7 @@ def sell(
897
944
account ,
898
945
commission = 0 ,
899
946
tax = 0 ,
900
- stated_total = None ,
947
+ stated_total = None , # Used to verify if inner calculation is correct
901
948
date_order = None ,
902
949
notes = '' ,
903
950
commission_notes = '' ,
@@ -973,9 +1020,7 @@ def sell(
973
1020
op_1 ['account' ] = account # sell
974
1021
op_2 ['account' ] = account # uninvest
975
1022
976
- # Create rows
977
- self ._add_row (op_1 ) # sell
978
- self ._add_row (op_2 ) # uninvest
1023
+ return self ._add_rows (data = [op_1 , op_2 ])
979
1024
980
1025
def stock_dividend (
981
1026
self ,
@@ -987,28 +1032,28 @@ def stock_dividend(
987
1032
date_order = None ,
988
1033
notes = '' ,
989
1034
):
990
- self . _add_row ( # stock dividend
991
- {
992
- 'date_execution ' : date_execution ,
993
- 'operation ' : 'stock dividend' ,
994
- 'instrument ' : instrument ,
995
- 'origin ' : instrument ,
996
- 'destination ' : '' ,
997
- 'price_in ' : price_in ,
998
- 'price ' : 0 ,
999
- 'price_w_expenses ' : 0 ,
1000
- 'size ' : size ,
1001
- 'commission ' : 0 ,
1002
- 'tax ' : 0 ,
1003
- 'stated_total' : 0 ,
1004
- 'date_order ' : ( date_order if date_order is not None else date_execution ) ,
1005
- 'description ' : f'Stock dividend from { instrument } .' ,
1006
- 'notes ' : notes ,
1007
- 'commission_notes ' : '' ,
1008
- 'tax_notes ' : '' ,
1009
- 'account' : account ,
1010
- }
1011
- )
1035
+ op_stock_dividend = {
1036
+ 'date_execution' : date_execution ,
1037
+ 'operation ' : 'stock dividend' ,
1038
+ 'instrument ' : instrument ,
1039
+ 'origin ' : instrument ,
1040
+ 'destination ' : '' ,
1041
+ 'price_in ' : price_in ,
1042
+ 'price ' : 0 ,
1043
+ 'price_w_expenses ' : 0 ,
1044
+ 'size ' : size ,
1045
+ 'commission ' : 0 ,
1046
+ 'tax ' : 0 ,
1047
+ 'stated_total ' : 0 ,
1048
+ 'date_order' : ( date_order if date_order is not None else date_execution ) ,
1049
+ 'description ' : f'Stock dividend from { instrument } .' ,
1050
+ 'notes ' : notes ,
1051
+ 'commission_notes ' : '' ,
1052
+ 'tax_notes ' : '' ,
1053
+ 'account ' : account ,
1054
+ }
1055
+
1056
+ return self . _add_rows ( op_stock_dividend )
1012
1057
1013
1058
def withdraw (
1014
1059
self ,
@@ -1040,28 +1085,28 @@ def withdraw(
1040
1085
notes : str, optional
1041
1086
Withdraw notes. By default ''.
1042
1087
"""
1043
- self . _add_row ( # Withdraw
1044
- {
1045
- 'date_execution ' : date_execution ,
1046
- 'operation ' : 'withdraw' ,
1047
- 'instrument ' : instrument ,
1048
- 'origin ' : instrument ,
1049
- 'destination ' : '' ,
1050
- 'price_in ' : instrument ,
1051
- 'price ' : 1 ,
1052
- 'price_w_expenses ' : 1 ,
1053
- 'size ' : - amount ,
1054
- 'commission ' : 0 ,
1055
- 'tax ' : 0 ,
1056
- 'stated_total' : - amount ,
1057
- 'date_order ' : ( date_order if date_order is not None else date_execution ) ,
1058
- 'description ' : f'Withdraw { instrument } .' ,
1059
- 'notes ' : notes ,
1060
- 'commission_notes ' : '' ,
1061
- 'tax_notes ' : '' ,
1062
- 'account' : account ,
1063
- }
1064
- )
1088
+ op_withdraw = {
1089
+ 'date_execution' : date_execution ,
1090
+ 'operation ' : 'withdraw' ,
1091
+ 'instrument ' : instrument ,
1092
+ 'origin ' : instrument ,
1093
+ 'destination ' : '' ,
1094
+ 'price_in ' : instrument ,
1095
+ 'price ' : 1 ,
1096
+ 'price_w_expenses ' : 1 ,
1097
+ 'size ' : - amount ,
1098
+ 'commission ' : 0 ,
1099
+ 'tax ' : 0 ,
1100
+ 'stated_total ' : - amount ,
1101
+ 'date_order' : ( date_order if date_order is not None else date_execution ) ,
1102
+ 'description ' : f'Withdraw { instrument } .' ,
1103
+ 'notes ' : notes ,
1104
+ 'commission_notes ' : '' ,
1105
+ 'tax_notes ' : '' ,
1106
+ 'account ' : account ,
1107
+ }
1108
+
1109
+ return self . _add_rows ( op_withdraw )
1065
1110
1066
1111
# *****************
1067
1112
# MARK: METADATA
0 commit comments