Skip to content

Commit d4954e7

Browse files
committed
fix: mean return for Portfolio must use ror.mean() formula
don't use Markowitz weights.T @ ror.mean() formula for rebalanced portfolios
1 parent 8ee614c commit d4954e7

File tree

4 files changed

+48
-25
lines changed

4 files changed

+48
-25
lines changed

main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
inflation=True
2626
)
2727

28-
print(portf_div.describe())
28+
print(portf_div.mean_return_monthly)
2929

3030

3131

main_notebook.ipynb

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"is_executing": true
88
},
99
"ExecuteTime": {
10-
"end_time": "2025-10-21T09:46:06.335187Z",
11-
"start_time": "2025-10-21T09:46:03.212159Z"
10+
"end_time": "2025-11-03T08:51:09.040420Z",
11+
"start_time": "2025-11-03T08:51:06.158525Z"
1212
}
1313
},
1414
"source": [
@@ -44,8 +44,8 @@
4444
"outputs_hidden": false
4545
},
4646
"ExecuteTime": {
47-
"end_time": "2025-10-21T09:46:08.576786Z",
48-
"start_time": "2025-10-21T09:46:08.137842Z"
47+
"end_time": "2025-11-03T08:51:09.130226Z",
48+
"start_time": "2025-11-03T08:51:09.058541Z"
4949
}
5050
},
5151
"source": [
@@ -432,8 +432,8 @@
432432
"cell_type": "code",
433433
"metadata": {
434434
"ExecuteTime": {
435-
"end_time": "2025-10-21T09:47:36.767644Z",
436-
"start_time": "2025-10-21T09:47:36.697787Z"
435+
"end_time": "2025-11-03T08:51:42.589398Z",
436+
"start_time": "2025-11-03T08:51:42.522003Z"
437437
}
438438
},
439439
"source": [
@@ -445,7 +445,7 @@
445445
"assets4 = [\"GSPC.INDX\"]"
446446
],
447447
"outputs": [],
448-
"execution_count": 9
448+
"execution_count": 4
449449
},
450450
{
451451
"cell_type": "code",
@@ -455,8 +455,8 @@
455455
"outputs_hidden": false
456456
},
457457
"ExecuteTime": {
458-
"end_time": "2025-10-21T09:47:56.632738Z",
459-
"start_time": "2025-10-21T09:47:37.394244Z"
458+
"end_time": "2025-11-03T08:52:15.663625Z",
459+
"start_time": "2025-11-03T08:51:51.214302Z"
460460
}
461461
},
462462
"source": [
@@ -480,7 +480,7 @@
480480
"name": "stdout",
481481
"output_type": "stream",
482482
"text": [
483-
"symbol portfolio_6232.PF\n",
483+
"symbol portfolio_2072.PF\n",
484484
"assets [RGBITR.INDX, RUCBTRNS.INDX, MCFTR.INDX, GC.COMM]\n",
485485
"weights [0.2, 0.2, 0.4, 0.2]\n",
486486
"rebalancing_period year\n",
@@ -489,13 +489,13 @@
489489
"currency USD\n",
490490
"inflation USD.INFL\n",
491491
"first_date 2003-01\n",
492-
"last_date 2025-08\n",
493-
"period_length 22 years, 8 months\n",
492+
"last_date 2025-09\n",
493+
"period_length 22 years, 9 months\n",
494494
"dtype: object\n"
495495
]
496496
}
497497
],
498-
"execution_count": 10
498+
"execution_count": 5
499499
},
500500
{
501501
"cell_type": "code",
@@ -8263,28 +8263,51 @@
82638263
},
82648264
{
82658265
"cell_type": "code",
8266-
"execution_count": 7,
82678266
"metadata": {
82688267
"ExecuteTime": {
8269-
"end_time": "2025-08-15T08:19:31.590235Z",
8270-
"start_time": "2025-08-15T08:19:31.546011Z"
8268+
"end_time": "2025-11-03T08:52:21.835039Z",
8269+
"start_time": "2025-11-03T08:52:21.748132Z"
82718270
}
82728271
},
8272+
"source": [
8273+
"pf.mean_return_monthly"
8274+
],
82738275
"outputs": [
82748276
{
82758277
"data": {
82768278
"text/plain": [
8277-
"np.float64(0.011438815808118081)"
8279+
"np.float64(0.008952255472527475)"
8280+
]
8281+
},
8282+
"execution_count": 6,
8283+
"metadata": {},
8284+
"output_type": "execute_result"
8285+
}
8286+
],
8287+
"execution_count": 6
8288+
},
8289+
{
8290+
"metadata": {
8291+
"ExecuteTime": {
8292+
"end_time": "2025-11-03T08:52:40.222993Z",
8293+
"start_time": "2025-11-03T08:52:40.144054Z"
8294+
}
8295+
},
8296+
"cell_type": "code",
8297+
"source": "pf.ror.mean()",
8298+
"outputs": [
8299+
{
8300+
"data": {
8301+
"text/plain": [
8302+
"np.float64(0.009401680619363958)"
82788303
]
82798304
},
82808305
"execution_count": 7,
82818306
"metadata": {},
82828307
"output_type": "execute_result"
82838308
}
82848309
],
8285-
"source": [
8286-
"pf.mean_return_monthly"
8287-
]
8310+
"execution_count": 7
82888311
},
82898312
{
82908313
"cell_type": "code",

okama/common/helpers/helpers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,11 @@ def get_portfolio_return_ts(cls, weights: list, ror: pd.DataFrame) -> pd.Series:
173173

174174
@classmethod
175175
def get_portfolio_mean_return(cls, weights: Union[list, np.array], ror: pd.DataFrame) -> float:
176-
# sourcery skip: assign-if-exp, reintroduce-else
177176
"""
178-
Computes mean return of a portfolio (monthly as ROR time series are monthly in okama).
177+
Computes arithmetic mean return of a portfolio return (monthly as ROR time series are monthly in okama).
178+
179+
WARNING: This function is correct only for monthly rebalancing strategy (or for single asset portfolio)..
179180
"""
180-
# cls.weights_sum_is_one(weights)
181181
weights = np.asarray(weights)
182182
if isinstance(ror.mean(), float): # required for a single asset portfolio
183183
return ror.mean()

okama/portfolios/core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ def mean_return_monthly(self) -> float:
454454
>>> pf
455455
0.0001803312727272665
456456
"""
457-
return helpers.Frame.get_portfolio_mean_return(self.weights, self.assets_ror)
457+
return self.ror.mean()
458458

459459
@property
460460
def mean_return_annual(self) -> float:

0 commit comments

Comments
 (0)