@@ -43,7 +43,8 @@ def get_wealth_indexes_fv_with_cashflow(
4343 ror_cashflow_df = ror .assign (cashflow_ts = cash_flow_ts )
4444 ror_cashflow_df .fillna (0 , inplace = True )
4545 n_rows = ror .shape [0 ]
46- discount_factors = (1.0 + dcf_object .discount_rate / settings ._MONTHS_PER_YEAR ) ** np .arange (n_rows )
46+ monthly_discount_rate = (1 + dcf_object .discount_rate ) ** (1 / settings ._MONTHS_PER_YEAR ) - 1
47+ discount_factors = (1.0 + monthly_discount_rate ) ** np .arange (n_rows )
4748 if task == 'backtest' :
4849 if dcf_object .cashflow_parameters .time_series_discounted_values :
4950 ror_cashflow_df .loc [:, "cashflow_ts" ] = ror_cashflow_df .loc [:, "cashflow_ts" ].mul (discount_factors , axis = 0 )
@@ -53,28 +54,39 @@ def get_wealth_indexes_fv_with_cashflow(
5354 else :
5455 raise ValueError (f"Unknown task: { task } . It must be 'monte_carlo' or 'backtest'" )
5556 else :
56- ror_cashflow_df = ror . to_frame () if not isinstance ( ror , pd . DataFrame ) else ror
57+ ror_cashflow_df = ror
5758 ror_cashflow_df .loc [:, "cashflow_ts" ] = 0
5859 cash_flow_ts = ror_cashflow_df ["cashflow_ts" ] # cash flow monthly time series
5960 periods_per_year = settings .frequency_periods_per_year [cashflow_parameters .frequency ]
61+ if hasattr (cashflow_parameters , "indexation" ) and cashflow_parameters .frequency != "none" :
62+ indexation_per_period = (1 + cashflow_parameters .indexation ) ** (1 / periods_per_year ) - 1
6063 if cashflow_parameters .frequency == "month" or cashflow_parameters .NAME == "time_series" :
6164 # Fast Calculation
6265 s = pd .Series (dtype = float , name = portfolio_symbol )
6366 for n , row in enumerate (ror .itertuples ()):
6467 date = row [0 ]
6568 r = row [portfolio_position + 1 ]
6669 if cashflow_parameters .NAME == "fixed_amount" :
67- cashflow = amount * (1 + cashflow_parameters . indexation / settings . _MONTHS_PER_YEAR ) ** n
70+ cashflow = amount * (1 + indexation_per_period ) ** n
6871 elif cashflow_parameters .NAME == "fixed_percentage" :
6972 cashflow = cashflow_parameters .percentage / periods_per_year * period_initial_amount
7073 elif cashflow_parameters .NAME == "time_series" :
7174 cashflow = 0
75+ elif cashflow_parameters .NAME == "CWID" :
76+ withdrawal_without_drawdowns = amount * (1 + indexation_per_period ) ** n
77+ if drawdowns [date ] < 0 :
78+ cashflow = cashflow_parameters .calculate_withdrawal_size (
79+ drawdown = drawdowns [date ],
80+ withdrawal_without_drawdowns = withdrawal_without_drawdowns ,
81+ )
82+ else :
83+ cashflow = withdrawal_without_drawdowns
7284 else :
7385 raise ValueError ("Wrong cashflow strategy name value." )
7486 period_initial_amount = period_initial_amount * (r + 1 ) + cashflow + cash_flow_ts [date ]
7587 date = row [0 ]
7688 s [date ] = period_initial_amount
77- else :
89+ elif cashflow_parameters . frequency != "month" and cashflow_parameters . frequency != "none" :
7890 # Slow Calculation
7991 pandas_frequency = cashflow_parameters ._pandas_frequency
8092 months_in_full_period = settings ._MONTHS_PER_YEAR / cashflow_parameters .periods_per_year
@@ -98,7 +110,7 @@ def get_wealth_indexes_fv_with_cashflow(
98110 period_wealth_index = period_initial_amount * (1 + ror_ts ).cumprod ()
99111 # CashFlow END period
100112 if cashflow_parameters .NAME == "fixed_amount" :
101- cashflow_value = amount * (1 + cashflow_parameters . indexation / periods_per_year ) ** n
113+ cashflow_value = amount * (1 + indexation_per_period ) ** n
102114 elif cashflow_parameters .NAME == "fixed_percentage" :
103115 cashflow_value = cashflow_parameters .percentage / periods_per_year * period_initial_amount
104116 elif cashflow_parameters .NAME == "VDS" :
@@ -108,14 +120,14 @@ def get_wealth_indexes_fv_with_cashflow(
108120 number_of_periods = n ,
109121 )
110122 elif cashflow_parameters .NAME == "CWID" :
111- regular_withdrawal = amount * (1 + cashflow_parameters . indexation / periods_per_year ) ** n
123+ withdrawal_without_drawdowns = amount * (1 + indexation_per_period ) ** n
112124 if drawdowns [last_date ] < 0 :
113125 cashflow_value = cashflow_parameters .calculate_withdrawal_size (
114126 drawdown = drawdowns [last_date ],
115- regular_withdrawal = regular_withdrawal ,
127+ withdrawal_without_drawdowns = withdrawal_without_drawdowns ,
116128 )
117129 else :
118- cashflow_value = regular_withdrawal
130+ cashflow_value = withdrawal_without_drawdowns
119131 else :
120132 raise ValueError ("Wrong cashflow_method value." )
121133 cashflow_value *= period_fraction # adjust cash flow to the period length (months)
@@ -124,6 +136,10 @@ def get_wealth_indexes_fv_with_cashflow(
124136 period_initial_amount = period_final_balance
125137 wealth_df = pd .concat ([None if wealth_df .empty else wealth_df , period_wealth_index ], sort = False )
126138 s = wealth_df .squeeze ()
139+ elif cashflow_parameters .frequency == "none" :
140+ s = helpers .Frame .get_wealth_indexes (
141+ ror = ror .loc [:, portfolio_symbol ], initial_amount = period_initial_amount_cached
142+ )
127143 first_date = s .index [0 ]
128144 first_wealth_index_date = first_date - 1 # set first date to one month earlie
129145 s .loc [first_wealth_index_date ] = period_initial_amount_cached
@@ -167,7 +183,8 @@ def get_cash_flow_fv(
167183 ror_cashflow_df = ror .assign (cashflow_ts = cash_flow_ts )
168184 ror_cashflow_df .fillna (0 , inplace = True )
169185 n_rows = ror .shape [0 ]
170- discount_factors = (1.0 + dcf_object .discount_rate / settings ._MONTHS_PER_YEAR ) ** np .arange (n_rows )
186+ monthly_discount_rate = (1 + dcf_object .discount_rate ) ** (1 / settings ._MONTHS_PER_YEAR ) - 1
187+ discount_factors = (1.0 + monthly_discount_rate ) ** np .arange (n_rows )
171188 if task == 'backtest' :
172189 if dcf_object .cashflow_parameters .time_series_discounted_values :
173190 ror_cashflow_df .loc [:, "cashflow_ts" ] = ror_cashflow_df .loc [:, "cashflow_ts" ].mul (discount_factors ,
@@ -183,25 +200,36 @@ def get_cash_flow_fv(
183200 ror_cashflow_df .loc [:, "cashflow_ts" ] = 0
184201 cash_flow_ts = ror_cashflow_df ["cashflow_ts" ] # cash flow monthly time series
185202 periods_per_year = settings .frequency_periods_per_year [cashflow_parameters .frequency ]
203+ if hasattr (cashflow_parameters , "indexation" ) and cashflow_parameters .frequency != "none" :
204+ indexation_per_period = (1 + cashflow_parameters .indexation ) ** (1 / periods_per_year ) - 1
186205 if cashflow_parameters .frequency == "month" or cashflow_parameters .NAME == "time_series" :
187206 # Fast Calculation
188207 for n , row in enumerate (ror .itertuples ()):
189208 date = row [0 ]
190209 r = row [portfolio_position + 1 ]
191210 # Calculate regular cash flow
192211 if cashflow_parameters .NAME == "fixed_amount" :
193- cashflow = amount * (1 + cashflow_parameters . indexation / settings . _MONTHS_PER_YEAR ) ** n
212+ cashflow = amount * (1 + indexation_per_period ) ** n
194213 elif cashflow_parameters .NAME == "fixed_percentage" :
195214 cashflow = cashflow_parameters .percentage / periods_per_year * period_initial_amount
196215 elif cashflow_parameters .NAME == "time_series" :
197216 cashflow = 0
217+ elif cashflow_parameters .NAME == "CWID" :
218+ withdrawal_without_drawdowns = amount * (1 + indexation_per_period ) ** n
219+ if drawdowns [date ] < 0 :
220+ cashflow = cashflow_parameters .calculate_withdrawal_size (
221+ drawdown = drawdowns [date ],
222+ withdrawal_without_drawdowns = withdrawal_without_drawdowns ,
223+ )
224+ else :
225+ cashflow = withdrawal_without_drawdowns
198226 else :
199227 raise ValueError ("Wrong cashflow strategy name value." )
200228 # add Extra Withdrawals/Contributions
201229 cs_value = cashflow + cash_flow_ts [date ]
202230 period_initial_amount = period_initial_amount * (r + 1 ) + cs_value
203231 cs_fv [date ] = cs_value
204- else :
232+ elif cashflow_parameters . frequency != "month" and cashflow_parameters . frequency != "none" :
205233 # Slow Calculation
206234 pandas_frequency = settings .frequency_mapping [cashflow_parameters .frequency ]
207235 months_in_full_period = settings ._MONTHS_PER_YEAR / cashflow_parameters .periods_per_year
@@ -224,7 +252,7 @@ def get_cash_flow_fv(
224252 period_wealth_index = period_initial_amount * (1 + ror_ts ).cumprod ()
225253 # CashFlow END period (Regular Cash Flow)
226254 if cashflow_parameters .NAME == "fixed_amount" :
227- cashflow_value = amount * (1 + cashflow_parameters . indexation / periods_per_year ) ** n
255+ cashflow_value = amount * (1 + indexation_per_period ) ** n
228256 elif cashflow_parameters .NAME == "fixed_percentage" :
229257 cashflow_value = cashflow_parameters .percentage / periods_per_year * period_initial_amount
230258 elif cashflow_parameters .NAME == "VDS" :
@@ -234,7 +262,7 @@ def get_cash_flow_fv(
234262 number_of_periods = n ,
235263 )
236264 elif cashflow_parameters .NAME == "CWID" :
237- withdrawal_without_drawdowns = amount * (1 + cashflow_parameters . indexation / periods_per_year ) ** n
265+ withdrawal_without_drawdowns = amount * (1 + indexation_per_period ) ** n
238266 if drawdowns [last_date ] < 0 :
239267 cashflow_value = cashflow_parameters .calculate_withdrawal_size (
240268 drawdown = drawdowns [last_date ],
@@ -251,6 +279,8 @@ def get_cash_flow_fv(
251279 period_initial_amount = period_final_balance
252280 cashflow_ts_local .iloc [- 1 ] += cashflow_value
253281 cs_fv = pd .concat ([None if cs_fv .empty else cs_fv , cashflow_ts_local ], sort = False )
282+ elif cashflow_parameters .frequency == "none" :
283+ cs_fv = pd .Series ([0 ] * len (ror .index ), index = ror .index ) # all zeroes
254284 return cs_fv
255285
256286
0 commit comments