mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-14 11:15:09 +08:00
Merge branch with annualized cumulative risk metrics.
This commit is contained in:
@@ -227,10 +227,26 @@ class AnswerKey(object):
|
||||
'Sim Cumulative', 'D', 4, 254),
|
||||
|
||||
'ALGORITHM_CUMULATIVE_VOLATILITY': DataIndex(
|
||||
'Sim Cumulative', 'O', 4, 254),
|
||||
'Sim Cumulative', 'P', 4, 254),
|
||||
|
||||
'ALGORITHM_CUMULATIVE_SHARPE': DataIndex(
|
||||
'Sim Cumulative', 'R', 4, 254)
|
||||
'Sim Cumulative', 'R', 4, 254),
|
||||
|
||||
'CUMULATIVE_DOWNSIDE_RISK': DataIndex(
|
||||
'Sim Cumulative', 'U', 4, 254),
|
||||
|
||||
'CUMULATIVE_SORTINO': DataIndex(
|
||||
'Sim Cumulative', 'V', 4, 254),
|
||||
|
||||
'CUMULATIVE_INFORMATION': DataIndex(
|
||||
'Sim Cumulative', 'Y', 4, 254),
|
||||
|
||||
'CUMULATIVE_BETA': DataIndex(
|
||||
'Sim Cumulative', 'AB', 4, 254),
|
||||
|
||||
'CUMULATIVE_ALPHA': DataIndex(
|
||||
'Sim Cumulative', 'AC', 4, 254),
|
||||
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
@@ -289,4 +305,15 @@ RISK_CUMULATIVE = pd.DataFrame({
|
||||
'volatility': pd.Series(dict(zip(
|
||||
DATES, ANSWER_KEY.ALGORITHM_CUMULATIVE_VOLATILITY))),
|
||||
'sharpe': pd.Series(dict(zip(
|
||||
DATES, ANSWER_KEY.ALGORITHM_CUMULATIVE_SHARPE)))})
|
||||
DATES, ANSWER_KEY.ALGORITHM_CUMULATIVE_SHARPE))),
|
||||
'downside_risk': pd.Series(dict(zip(
|
||||
DATES, ANSWER_KEY.CUMULATIVE_DOWNSIDE_RISK))),
|
||||
'sortino': pd.Series(dict(zip(
|
||||
DATES, ANSWER_KEY.CUMULATIVE_SORTINO))),
|
||||
'information': pd.Series(dict(zip(
|
||||
DATES, ANSWER_KEY.CUMULATIVE_INFORMATION))),
|
||||
'alpha': pd.Series(dict(zip(
|
||||
DATES, ANSWER_KEY.CUMULATIVE_ALPHA))),
|
||||
'beta': pd.Series(dict(zip(
|
||||
DATES, ANSWER_KEY.CUMULATIVE_BETA))),
|
||||
})
|
||||
|
||||
@@ -6,3 +6,8 @@
|
||||
97dfb557c3501179504926e4079e6446
|
||||
cc507b6fca18aabadac69657181edd4e
|
||||
5b48e6a70181d73ecb7f07df5a3092e2
|
||||
3343940379161143630503413627a53a
|
||||
820235c4157a3c55474836438019ef2e
|
||||
75c1b1441efbc2431215835a5079ccc6
|
||||
37e3ea4a1788f1aa6f3ee0986bc625ae
|
||||
651e611e723e2a58b1ded91d0cd39b66
|
||||
|
||||
@@ -15,15 +15,98 @@
|
||||
|
||||
import unittest
|
||||
|
||||
from . answer_key import AnswerKey
|
||||
import datetime
|
||||
import numpy as np
|
||||
import pytz
|
||||
import zipline.finance.risk as risk
|
||||
from zipline.utils import factory
|
||||
|
||||
ANSWER_KEY = AnswerKey()
|
||||
from zipline.finance.trading import SimulationParameters
|
||||
|
||||
import answer_key
|
||||
ANSWER_KEY = answer_key.ANSWER_KEY
|
||||
|
||||
|
||||
class TestRisk(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
pass
|
||||
start_date = datetime.datetime(
|
||||
year=2006,
|
||||
month=1,
|
||||
day=1,
|
||||
hour=0,
|
||||
minute=0,
|
||||
tzinfo=pytz.utc)
|
||||
end_date = datetime.datetime(
|
||||
year=2006, month=12, day=29, tzinfo=pytz.utc)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
self.sim_params = SimulationParameters(
|
||||
period_start=start_date,
|
||||
period_end=end_date
|
||||
)
|
||||
|
||||
self.algo_returns_06 = factory.create_returns_from_list(
|
||||
answer_key.ALGORITHM_RETURNS.values,
|
||||
self.sim_params
|
||||
)
|
||||
|
||||
self.cumulative_metrics_06 = risk.RiskMetricsCumulative(
|
||||
self.sim_params)
|
||||
|
||||
for dt, returns in answer_key.RETURNS_DATA.iterrows():
|
||||
self.cumulative_metrics_06.update(dt,
|
||||
returns['Algorithm Returns'],
|
||||
returns['Benchmark Returns'])
|
||||
|
||||
def test_algorithm_volatility_06(self):
|
||||
np.testing.assert_almost_equal(
|
||||
ANSWER_KEY.ALGORITHM_CUMULATIVE_VOLATILITY,
|
||||
self.cumulative_metrics_06.metrics.algorithm_volatility.values)
|
||||
|
||||
def test_sharpe_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.sharpe.iterkv():
|
||||
np.testing.assert_almost_equal(
|
||||
value,
|
||||
self.cumulative_metrics_06.metrics.sharpe[dt],
|
||||
decimal=2,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_downside_risk_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.downside_risk.iterkv():
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.downside_risk[dt],
|
||||
value,
|
||||
decimal=2,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_sortino_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.sortino.iterkv():
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.sortino[dt],
|
||||
value,
|
||||
decimal=2,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_information_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.information.iterkv():
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.information[dt],
|
||||
value,
|
||||
decimal=2,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_alpha_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.alpha.iterkv():
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.alpha[dt],
|
||||
value,
|
||||
decimal=2,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
def test_beta_06(self):
|
||||
for dt, value in answer_key.RISK_CUMULATIVE.beta.iterkv():
|
||||
np.testing.assert_almost_equal(
|
||||
self.cumulative_metrics_06.metrics.beta[dt],
|
||||
value,
|
||||
decimal=2,
|
||||
err_msg="Mismatch at %s" % (dt,))
|
||||
|
||||
@@ -145,8 +145,8 @@ class TestEventsThroughRisk(unittest.TestCase):
|
||||
# at least be an early warning against changes.
|
||||
expected_sharpe = {
|
||||
first_date: np.nan,
|
||||
second_date: -1.630920,
|
||||
third_date: -1.016842,
|
||||
second_date: -31.56903265,
|
||||
third_date: -11.459888981,
|
||||
}
|
||||
|
||||
for bar in gen:
|
||||
@@ -305,9 +305,9 @@ class TestEventsThroughRisk(unittest.TestCase):
|
||||
self.assertEqual(1, len(algo.portfolio.positions), "There should "
|
||||
"be one position after the first day.")
|
||||
|
||||
self.assertTrue(
|
||||
np.isnan(
|
||||
crm.metrics.algorithm_volatility[algo.datetime.date()]),
|
||||
self.assertEquals(
|
||||
0,
|
||||
crm.metrics.algorithm_volatility[algo.datetime.date()],
|
||||
"On the first day algorithm volatility does not exist.")
|
||||
|
||||
second_msg = gen.next()
|
||||
|
||||
@@ -1208,7 +1208,7 @@ class TestPerformanceTracker(unittest.TestCase):
|
||||
commission=0.50)
|
||||
benchmark_event_1 = Event({
|
||||
'dt': start_dt,
|
||||
'returns': 1.0,
|
||||
'returns': 0.01,
|
||||
'type': DATASOURCE_TYPE.BENCHMARK
|
||||
})
|
||||
|
||||
@@ -1218,7 +1218,7 @@ class TestPerformanceTracker(unittest.TestCase):
|
||||
'bar', 11.0, 20, start_dt + datetime.timedelta(minutes=1))
|
||||
benchmark_event_2 = Event({
|
||||
'dt': start_dt + datetime.timedelta(minutes=1),
|
||||
'returns': 2.0,
|
||||
'returns': 0.02,
|
||||
'type': DATASOURCE_TYPE.BENCHMARK
|
||||
})
|
||||
|
||||
@@ -1270,3 +1270,8 @@ class TestPerformanceTracker(unittest.TestCase):
|
||||
msg_1['minute_perf']['period_close'])
|
||||
self.assertEquals(foo_event_2.dt,
|
||||
msg_2['minute_perf']['period_close'])
|
||||
|
||||
# Ensure that a Sharpe value for cumulative metrics is being
|
||||
# created.
|
||||
self.assertIsNotNone(msg_1['cumulative_risk_metrics']['sharpe'])
|
||||
self.assertIsNotNone(msg_2['cumulative_risk_metrics']['sharpe'])
|
||||
|
||||
@@ -114,7 +114,8 @@ class PerformanceTracker(object):
|
||||
|
||||
self.cumulative_risk_metrics = \
|
||||
risk.RiskMetricsCumulative(self.sim_params,
|
||||
returns_frequency='daily')
|
||||
returns_frequency='daily',
|
||||
create_first_day_stats=True)
|
||||
|
||||
self.minute_performance = PerformancePeriod(
|
||||
# initial cash is your capital base.
|
||||
|
||||
@@ -13,29 +13,107 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import functools
|
||||
import logbook
|
||||
import math
|
||||
import numpy as np
|
||||
|
||||
import zipline.finance.trading as trading
|
||||
import zipline.utils.math_utils as zp_math
|
||||
|
||||
import pandas as pd
|
||||
from pandas.tseries.tools import normalize_date
|
||||
|
||||
|
||||
from . risk import (
|
||||
alpha,
|
||||
check_entry,
|
||||
choose_treasury,
|
||||
information_ratio,
|
||||
sharpe_ratio,
|
||||
sortino_ratio,
|
||||
)
|
||||
|
||||
log = logbook.Logger('Risk Cumulative')
|
||||
|
||||
|
||||
choose_treasury = functools.partial(choose_treasury, lambda *args: '10year',
|
||||
compound=False)
|
||||
|
||||
|
||||
def sharpe_ratio(algorithm_volatility, annualized_return, treasury_return):
|
||||
"""
|
||||
http://en.wikipedia.org/wiki/Sharpe_ratio
|
||||
|
||||
Args:
|
||||
algorithm_volatility (float): Algorithm volatility.
|
||||
algorithm_return (float): Algorithm return percentage.
|
||||
treasury_return (float): Treasury return percentage.
|
||||
|
||||
Returns:
|
||||
float. The Sharpe ratio.
|
||||
"""
|
||||
if zp_math.tolerant_equals(algorithm_volatility, 0):
|
||||
return np.nan
|
||||
|
||||
return (
|
||||
(annualized_return - treasury_return)
|
||||
# The square of the annualization factor is in the volatility,
|
||||
# because the volatility is also annualized,
|
||||
# i.e. the sqrt(annual factor) is in the volatility's numerator.
|
||||
# So to have the the correct annualization factor for the
|
||||
# Sharpe value's numerator, which should be the sqrt(annual factor).
|
||||
# The square of the sqrt of the annual factor, i.e. the annual factor
|
||||
# itself, is needed in the numerator to factor out the division by
|
||||
# its square root.
|
||||
/ algorithm_volatility)
|
||||
|
||||
|
||||
def sortino_ratio(annualized_algorithm_return, treasury_return, downside_risk):
|
||||
"""
|
||||
http://en.wikipedia.org/wiki/Sortino_ratio
|
||||
|
||||
Args:
|
||||
algorithm_returns (np.array-like):
|
||||
Returns from algorithm lifetime.
|
||||
algorithm_period_return (float):
|
||||
Algorithm return percentage from latest period.
|
||||
mar (float): Minimum acceptable return.
|
||||
|
||||
Returns:
|
||||
float. The Sortino ratio.
|
||||
"""
|
||||
if np.isnan(downside_risk) or zp_math.tolerant_equals(downside_risk, 0):
|
||||
return 0.0
|
||||
|
||||
return (annualized_algorithm_return - treasury_return) / downside_risk
|
||||
|
||||
|
||||
def information_ratio(algo_volatility, algorithm_return, benchmark_return):
|
||||
"""
|
||||
http://en.wikipedia.org/wiki/Information_ratio
|
||||
|
||||
Args:
|
||||
algorithm_returns (np.array-like):
|
||||
All returns during algorithm lifetime.
|
||||
benchmark_returns (np.array-like):
|
||||
All benchmark returns during algo lifetime.
|
||||
|
||||
Returns:
|
||||
float. Information ratio.
|
||||
"""
|
||||
if zp_math.tolerant_equals(algo_volatility, 0):
|
||||
return np.nan
|
||||
|
||||
return (
|
||||
(algorithm_return - benchmark_return)
|
||||
# The square of the annualization factor is in the volatility,
|
||||
# because the volatility is also annualized,
|
||||
# i.e. the sqrt(annual factor) is in the volatility's numerator.
|
||||
# So to have the the correct annualization factor for the
|
||||
# Sharpe value's numerator, which should be the sqrt(annual factor).
|
||||
# The square of the sqrt of the annual factor, i.e. the annual factor
|
||||
# itself, is needed in the numerator to factor out the division by
|
||||
# its square root.
|
||||
/ algo_volatility)
|
||||
|
||||
|
||||
class RiskMetricsCumulative(object):
|
||||
"""
|
||||
:Usage:
|
||||
@@ -49,11 +127,14 @@ class RiskMetricsCumulative(object):
|
||||
'sharpe',
|
||||
'algorithm_volatility',
|
||||
'benchmark_volatility',
|
||||
'downside_risk',
|
||||
'sortino',
|
||||
'information',
|
||||
)
|
||||
|
||||
def __init__(self, sim_params, returns_frequency=None):
|
||||
def __init__(self, sim_params,
|
||||
returns_frequency=None,
|
||||
create_first_day_stats=False):
|
||||
"""
|
||||
- @returns_frequency allows for configuration of the whether
|
||||
the benchmark and algorithm returns are in units of minutes or days,
|
||||
@@ -83,6 +164,8 @@ class RiskMetricsCumulative(object):
|
||||
|
||||
self.sim_params = sim_params
|
||||
|
||||
self.create_first_day_stats = create_first_day_stats
|
||||
|
||||
if returns_frequency is None:
|
||||
returns_frequency = self.sim_params.emission_rate
|
||||
|
||||
@@ -102,6 +185,10 @@ class RiskMetricsCumulative(object):
|
||||
# returns container.
|
||||
self.algorithm_returns = None
|
||||
self.benchmark_returns = None
|
||||
self.mean_returns = None
|
||||
self.annualized_mean_returns = None
|
||||
self.mean_benchmark_returns = None
|
||||
self.annualized_benchmark_returns = None
|
||||
|
||||
self.compounded_log_returns = pd.Series(index=cont_index)
|
||||
self.algorithm_period_returns = pd.Series(index=cont_index)
|
||||
@@ -143,9 +230,41 @@ class RiskMetricsCumulative(object):
|
||||
self.algorithm_returns_cont[dt] = algorithm_returns
|
||||
self.algorithm_returns = self.algorithm_returns_cont.valid()
|
||||
|
||||
if self.create_first_day_stats:
|
||||
if len(self.algorithm_returns) == 1:
|
||||
self.algorithm_returns = pd.Series(
|
||||
{'null return': 0.0}).append(
|
||||
self.algorithm_returns)
|
||||
|
||||
self.mean_returns = pd.rolling_mean(self.algorithm_returns,
|
||||
window=len(self.algorithm_returns),
|
||||
min_periods=1)
|
||||
|
||||
self.annualized_mean_returns = self.mean_returns * 252
|
||||
|
||||
self.benchmark_returns_cont[dt] = benchmark_returns
|
||||
self.benchmark_returns = self.benchmark_returns_cont.valid()
|
||||
|
||||
self.mean_benchmark_returns = pd.rolling_mean(
|
||||
self.benchmark_returns,
|
||||
window=len(self.benchmark_returns),
|
||||
min_periods=1)
|
||||
|
||||
self.annualized_benchmark_returns = self.mean_benchmark_returns * 252
|
||||
|
||||
if self.create_first_day_stats:
|
||||
if len(self.benchmark_returns) == 1:
|
||||
self.benchmark_returns = pd.Series(
|
||||
{'null return': 0.0}).append(
|
||||
self.benchmark_returns)
|
||||
|
||||
self.mean_benchmark_returns = pd.rolling_mean(
|
||||
self.benchmark_returns,
|
||||
window=len(self.benchmark_returns),
|
||||
min_periods=1)
|
||||
|
||||
self.annualized_benchmark_returns = self.mean_benchmark_returns * 252
|
||||
|
||||
self.num_trading_days = len(self.algorithm_returns)
|
||||
|
||||
self.update_compounded_log_returns()
|
||||
@@ -197,10 +316,24 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
self.metrics.beta[dt] = self.calculate_beta()
|
||||
self.metrics.alpha[dt] = self.calculate_alpha(dt)
|
||||
self.metrics.sharpe[dt] = self.calculate_sharpe()
|
||||
self.metrics.downside_risk[dt] = self.calculate_downside_risk()
|
||||
self.metrics.sortino[dt] = self.calculate_sortino()
|
||||
self.metrics.information[dt] = self.calculate_information()
|
||||
self.max_drawdown = self.calculate_max_drawdown()
|
||||
|
||||
if self.create_first_day_stats:
|
||||
# Remove placeholder 0 return
|
||||
if 'null return' in self.algorithm_returns:
|
||||
self.algorithm_returns = self.algorithm_returns.drop(
|
||||
'null return')
|
||||
self.algorithm_returns.index = pd.to_datetime(
|
||||
self.algorithm_returns.index)
|
||||
if 'null return' in self.benchmark_returns:
|
||||
self.benchmark_returns = self.benchmark_returns.drop(
|
||||
'null return')
|
||||
self.benchmark_returns.index = pd.to_datetime(
|
||||
self.benchmark_returns.index)
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
Creates a dictionary representing the state of the risk report.
|
||||
@@ -306,38 +439,43 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
http://en.wikipedia.org/wiki/Sharpe_ratio
|
||||
"""
|
||||
return sharpe_ratio(self.metrics.algorithm_volatility[self.latest_dt],
|
||||
self.algorithm_period_returns[self.latest_dt],
|
||||
self.treasury_period_return)
|
||||
self.annualized_mean_returns[self.latest_dt],
|
||||
self.daily_treasury[self.latest_dt.date()])
|
||||
|
||||
def calculate_sortino(self, mar=None):
|
||||
def calculate_sortino(self):
|
||||
"""
|
||||
http://en.wikipedia.org/wiki/Sortino_ratio
|
||||
"""
|
||||
if mar is None:
|
||||
mar = self.treasury_period_return
|
||||
|
||||
return sortino_ratio(self.algorithm_returns,
|
||||
self.algorithm_period_returns[self.latest_dt],
|
||||
mar)
|
||||
return sortino_ratio(self.annualized_mean_returns[self.latest_dt],
|
||||
self.daily_treasury[self.latest_dt.date()],
|
||||
self.metrics.downside_risk[self.latest_dt])
|
||||
|
||||
def calculate_information(self):
|
||||
"""
|
||||
http://en.wikipedia.org/wiki/Information_ratio
|
||||
"""
|
||||
return information_ratio(self.algorithm_returns,
|
||||
self.benchmark_returns)
|
||||
return information_ratio(
|
||||
self.metrics.algorithm_volatility[self.latest_dt],
|
||||
self.annualized_mean_returns[self.latest_dt],
|
||||
self.annualized_benchmark_returns[self.latest_dt])
|
||||
|
||||
def calculate_alpha(self, dt):
|
||||
"""
|
||||
http://en.wikipedia.org/wiki/Alpha_(investment)
|
||||
"""
|
||||
return alpha(self.algorithm_period_returns[self.latest_dt],
|
||||
return alpha(self.annualized_mean_returns[self.latest_dt],
|
||||
self.treasury_period_return,
|
||||
self.benchmark_period_returns[self.latest_dt],
|
||||
self.annualized_benchmark_returns[self.latest_dt],
|
||||
self.metrics.beta[dt])
|
||||
|
||||
def calculate_volatility(self, daily_returns):
|
||||
return np.std(daily_returns, ddof=1) * math.sqrt(self.num_trading_days)
|
||||
return np.std(daily_returns) * math.sqrt(252)
|
||||
|
||||
def calculate_downside_risk(self):
|
||||
rets = self.algorithm_returns
|
||||
mar = self.mean_returns
|
||||
downside_diff = (rets[rets < mar] - mar).valid()
|
||||
return np.std(downside_diff) * math.sqrt(252)
|
||||
|
||||
def calculate_beta(self):
|
||||
"""
|
||||
@@ -350,11 +488,11 @@ algorithm_returns ({algo_count}) in range {start} : {end} on {dt}"
|
||||
"""
|
||||
# it doesn't make much sense to calculate beta for less than two days,
|
||||
# so return none.
|
||||
if len(self.algorithm_returns) < 2:
|
||||
if len(self.annualized_mean_returns) < 2:
|
||||
return 0.0
|
||||
|
||||
returns_matrix = np.vstack([self.algorithm_returns,
|
||||
self.benchmark_returns])
|
||||
returns_matrix = np.vstack([self.annualized_mean_returns,
|
||||
self.annualized_benchmark_returns])
|
||||
C = np.cov(returns_matrix, ddof=1)
|
||||
algorithm_covariance = C[0][1]
|
||||
benchmark_variance = C[1][1]
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import functools
|
||||
|
||||
import logbook
|
||||
import math
|
||||
import numpy as np
|
||||
@@ -22,10 +24,10 @@ import zipline.finance.trading as trading
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import risk
|
||||
from . risk import (
|
||||
alpha,
|
||||
check_entry,
|
||||
choose_treasury,
|
||||
information_ratio,
|
||||
sharpe_ratio,
|
||||
sortino_ratio,
|
||||
@@ -33,6 +35,9 @@ from . risk import (
|
||||
|
||||
log = logbook.Logger('Risk Period')
|
||||
|
||||
choose_treasury = functools.partial(risk.choose_treasury,
|
||||
risk.select_treasury_duration)
|
||||
|
||||
|
||||
class RiskMetricsPeriod(object):
|
||||
def __init__(self, start_date, end_date, returns,
|
||||
|
||||
@@ -233,8 +233,9 @@ def select_treasury_duration(start_date, end_date):
|
||||
return treasury_duration
|
||||
|
||||
|
||||
def choose_treasury(treasury_curves, start_date, end_date):
|
||||
treasury_duration = select_treasury_duration(start_date, end_date)
|
||||
def choose_treasury(select_treasury, treasury_curves, start_date, end_date,
|
||||
compound=True):
|
||||
treasury_duration = select_treasury(start_date, end_date)
|
||||
end_day = end_date.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
search_day = None
|
||||
|
||||
@@ -274,7 +275,10 @@ treasury history range."
|
||||
|
||||
if search_day:
|
||||
td = end_date - start_date
|
||||
return rate * (td.days + 1) / 365
|
||||
if compound:
|
||||
return rate * (td.days + 1) / 365
|
||||
else:
|
||||
return rate
|
||||
|
||||
message = "No rate for end date = {dt} and term = {term}. Check \
|
||||
that date doesn't exceed treasury history range."
|
||||
|
||||
Reference in New Issue
Block a user