-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement exponential mode for annualized_return calculation in risk_… #1433
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,16 +24,14 @@ | |
logger = get_module_logger("Evaluate") | ||
|
||
|
||
def risk_analysis(r, N: int = None, freq: str = "day"): | ||
def risk_analysis(r, N: int = None, freq: str = "day", accumulation_mode = "summation"): | ||
"""Risk Analysis | ||
NOTE: | ||
The calculation of annulaized return is different from the definition of annualized return. | ||
It is implemented by design. | ||
Qlib tries to cumulated returns by summation instead of production to avoid the cumulated curve being skewed exponentially. | ||
All the calculation of annualized returns follows this principle in Qlib. | ||
|
||
TODO: add a parameter to enable calculating metrics with production accumulation of return. | ||
|
||
Parameters | ||
---------- | ||
r : pandas.Series | ||
|
@@ -42,6 +40,8 @@ def risk_analysis(r, N: int = None, freq: str = "day"): | |
scaler for annualizing information_ratio (day: 252, week: 50, month: 12), at least one of `N` and `freq` should exist | ||
freq: str | ||
analysis frequency used for calculating the scaler, at least one of `N` and `freq` should exist | ||
accumulation_mode: str | ||
the mode of calculating the cumulative return, options are 'exponential' and 'summation' | ||
""" | ||
|
||
def cal_risk_analysis_scaler(freq): | ||
|
@@ -61,10 +61,16 @@ def cal_risk_analysis_scaler(freq): | |
warnings.warn("risk_analysis freq will be ignored") | ||
if N is None: | ||
N = cal_risk_analysis_scaler(freq) | ||
|
||
if accumulation_mode not in ["summation", "exponential"]: | ||
raise ValueError("Invalid value for `accumulation_mode`. Only 'summation' and 'exponential' are supported") | ||
|
||
mean = r.mean() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be geometric mean if production is considered |
||
std = r.std(ddof=1) | ||
annualized_return = mean * N | ||
if accumulation_mode == "summation": | ||
annualized_return = mean * N | ||
else: | ||
annualized_return = (np.prod(1 + r) - 1) | ||
information_ratio = mean / std * np.sqrt(N) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if the annualized volatility should be chagned if production is considered. |
||
max_drawdown = (r.cumsum() - r.cumsum().cummax()).min() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The maxdrawdown should also be changed if production is considered |
||
data = { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use
Literal
https://github.com/microsoft/qlib/blob/main/qlib/typehint.py#L13