mirror of
https://github.com/wassname/catalyst.git
synced 2026-06-27 19:14:36 +08:00
fixed a few type-os to repair document generation.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
finance Package
|
||||
===============
|
||||
|
||||
:mod:`data` Module
|
||||
------------------
|
||||
:mod:`performance` Module
|
||||
-------------------------
|
||||
|
||||
.. automodule:: zipline.finance.data
|
||||
.. automodule:: zipline.finance.performance
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
+17
-8
@@ -9,14 +9,6 @@ zipline Package
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`cli` Module
|
||||
-----------------
|
||||
|
||||
.. automodule:: zipline.cli
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`component` Module
|
||||
-----------------------
|
||||
|
||||
@@ -57,6 +49,14 @@ zipline Package
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`simulator` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: zipline.simulator
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`sources` Module
|
||||
---------------------
|
||||
|
||||
@@ -65,6 +65,14 @@ zipline Package
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`topology` Module
|
||||
----------------------
|
||||
|
||||
.. automodule:: zipline.topology
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`topos` Module
|
||||
-------------------
|
||||
|
||||
@@ -86,6 +94,7 @@ Subpackages
|
||||
|
||||
.. toctree::
|
||||
|
||||
zipline.finance
|
||||
zipline.test
|
||||
zipline.transforms
|
||||
|
||||
|
||||
+35
-3
@@ -9,10 +9,18 @@ test Package
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_devsimulator` Module
|
||||
-------------------------------
|
||||
:mod:`factory` Module
|
||||
---------------------
|
||||
|
||||
.. automodule:: zipline.test.test_devsimulator
|
||||
.. automodule:: zipline.test.factory
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_finance` Module
|
||||
--------------------------
|
||||
|
||||
.. automodule:: zipline.test.test_finance
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
@@ -33,6 +41,22 @@ test Package
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_perf_tracking` Module
|
||||
--------------------------------
|
||||
|
||||
.. automodule:: zipline.test.test_perf_tracking
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_risk` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: zipline.test.test_risk
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`test_sanity` Module
|
||||
-------------------------
|
||||
|
||||
@@ -41,3 +65,11 @@ test Package
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
:mod:`transform` Module
|
||||
-----------------------
|
||||
|
||||
.. automodule:: zipline.test.transform
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
+210
-56
@@ -40,37 +40,131 @@ class PerformanceTracker():
|
||||
starting_cash = capital_base
|
||||
)
|
||||
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
Creates a dictionary representing the state of this tracker.
|
||||
Returns a dict object of the form:
|
||||
|
||||
def update(self, event):
|
||||
self.event_count += 1
|
||||
if(event.dt >= self.market_close):
|
||||
self.handle_market_close()
|
||||
+-----------------+----------------------------------------------------+
|
||||
| key | value |
|
||||
+=================+====================================================+
|
||||
| period_start | The beginning of the period to be tracked. datetime|
|
||||
| | in pytz.utc timezone. Will always be 0:00 on the |
|
||||
| | date in UTC. The fact that the time may be on the |
|
||||
| | prior day in the exchange's local time is ignored |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| period_end | The end of the period to be tracked. datetime |
|
||||
| | in pytz.utc timezone. Will always be 23:59 on the |
|
||||
| | date in UTC. The fact that the time may be on the |
|
||||
| | next day in the exchange's local time is ignored |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| progress | percentage of test completed |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| cumulative_capti| The net capital used (positive is spent) through |
|
||||
| al_used | the course of all the events sent to this tracker |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| max_capital_used| The maximum amount of capital deployed through the |
|
||||
| | course of all the events sent to this tracker |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| last_close | The most recent close of the market. datetime in |
|
||||
| | pytz.utc timezone. Will always be 23:59 on the |
|
||||
| | date in UTC. The fact that the time may be on the |
|
||||
| | next day in the exchange's local time is ignored |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| last_open | The most recent open of the market. datetime in |
|
||||
| | pytz.utc timezone. Will always be 00:00 on the |
|
||||
| | date in UTC. The fact that the time may be on the |
|
||||
| | next day in the exchange's local time is ignored |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| capital_base | The initial capital assumed for this tracker. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| returns | List of dicts representing daily returns. See the |
|
||||
| | comments for |
|
||||
| | :py:meth:`zipline.finance.risk.DailyReturn.to_dict`|
|
||||
+-----------------+----------------------------------------------------+
|
||||
| cumulative_perf | A dictionary representing the cumulative |
|
||||
| | performance through all the events delivered to |
|
||||
| | this tracker. For details see the comments on |
|
||||
| | :py:meth:`PerformancePeriod.to_dict` |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| todays_perf | A dictionary representing the cumulative |
|
||||
| | performance through all the events delivered to |
|
||||
| | this tracker with datetime stamps between last_open|
|
||||
| | and last_close. For details see the comments on |
|
||||
| | :py:meth:`PerformancePeriod.to_dict` |
|
||||
| | TODO: adding this because we calculate it. May be |
|
||||
| | overkill. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| cumulative_risk | A dictionary representing the risk metrics |
|
||||
| _metrics | calculated based on the positions aggregated |
|
||||
| | through all the events delivered to this tracker. |
|
||||
| | For details look at the comments for |
|
||||
| | :py:meth:`zipline.finance.risk.RiskMetrics.to_dict`|
|
||||
+-----------------+----------------------------------------------------+
|
||||
|
||||
|
||||
|
||||
"""
|
||||
returns_list = [x.to_dict() for x in self.returns]
|
||||
d = {
|
||||
'period_start' : self.period_start,
|
||||
'period_end' : self.period_end,
|
||||
'progress' : self.progress,
|
||||
'cumulative_captial_used' : self.cumulative_captial_used,
|
||||
'max_capital_used' : self.max_capital_used,
|
||||
'last_close' : self.market_close,
|
||||
'last_open' : self.market_open,
|
||||
'capital_base' : self.capital_base,
|
||||
'returns' : returns_list,
|
||||
'cumulative_perf' : self.cumulative_perf.to_dict(),
|
||||
'todays_perf' : self.todays_perf.to_dict(),
|
||||
'cumulative_risk_metrics' : self.cumulative_risk_metrics.to_dict()
|
||||
}
|
||||
|
||||
def update(self, event_frame):
|
||||
for dt, event_series in event_frame.iteritems():
|
||||
self.process_event(event_series)
|
||||
|
||||
def process_event(self, event):
|
||||
qutil.LOGGER.debug("series is " + str(event))
|
||||
self.event_count += 1
|
||||
if(event.dt >= self.market_close):
|
||||
self.handle_market_close()
|
||||
|
||||
if event.TRANSACTION != None:
|
||||
self.txn_count += 1
|
||||
self.cumulative_performance.execute_transaction(event.TRANSACTION)
|
||||
self.todays_performance.execute_transaction(event.TRANSACTION)
|
||||
|
||||
if event.TRANSACTION != None:
|
||||
self.txn_count += 1
|
||||
self.cumulative_performance.execute_transaction(event.TRANSACTION)
|
||||
self.todays_performance.execute_transaction(event.TRANSACTION)
|
||||
|
||||
#we're adding a 10% cushion to the capital used, and then rounding to the nearest 5k
|
||||
self.cumulative_capital_used += event.TRANSACTION.price * event.TRANSACTION.amount
|
||||
if(math.fabs(self.cumulative_capital_used) > self.max_capital_used):
|
||||
self.max_capital_used = math.fabs(self.cumulative_capital_used)
|
||||
self.max_capital_used = self.round_to_nearest(1.1 * self.max_capital_used, base=5000)
|
||||
self.max_leverage = self.max_capital_used/self.capital_base
|
||||
# we're adding a 10% cushion to the capital used,
|
||||
# and then rounding to the nearest 5k
|
||||
transaction_cost = event.TRANSACTION.price * event.TRANSACTION.amount
|
||||
self.cumulative_capital_used += transaction_cost
|
||||
if(math.fabs(self.cumulative_capital_used) > self.max_capital_used):
|
||||
self.max_capital_used = math.fabs(self.cumulative_capital_used)
|
||||
|
||||
#update last sale
|
||||
self.cumulative_performance.update_last_sale(event)
|
||||
self.todays_performance.update_last_sale(event)
|
||||
|
||||
#calculate performance as of last trade
|
||||
self.cumulative_performance.calculate_performance()
|
||||
self.todays_performance.calculate_performance()
|
||||
|
||||
cushioned_capital = 1.1 * self.max_capital_used
|
||||
self.max_capital_used = self.round_to_nearest(
|
||||
cushioned_capital,
|
||||
base=5000
|
||||
)
|
||||
self.max_leverage = self.max_capital_used/self.capital_base
|
||||
|
||||
#update last sale
|
||||
self.cumulative_performance.update_last_sale(event)
|
||||
self.todays_performance.update_last_sale(event)
|
||||
|
||||
#calculate performance as of last trade
|
||||
self.cumulative_performance.calculate_performance()
|
||||
self.todays_performance.calculate_performance()
|
||||
|
||||
def handle_market_close(self):
|
||||
#add the return results from today to the list of daily return objects.
|
||||
#add the return results from today to the list of DailyReturn objects.
|
||||
todays_date = self.market_close.replace(hour=0, minute=0, second=0)
|
||||
todays_return_obj = risk.daily_return(todays_date, self.todays_performance.returns)
|
||||
todays_return_obj = risk.DailyReturn(
|
||||
todays_date,
|
||||
self.todays_performance.returns
|
||||
)
|
||||
self.returns.append(todays_return_obj)
|
||||
|
||||
#calculate risk metrics for cumulative performance
|
||||
@@ -92,14 +186,10 @@ class PerformanceTracker():
|
||||
|
||||
#calculate progress of test
|
||||
self.progress = self.day_count / self.total_days
|
||||
|
||||
|
||||
|
||||
|
||||
######################################################################################################
|
||||
#######TODO: report/relay metrics out to qexec -- values come from self.cur_period_metrics ###########
|
||||
#######TODO: report/relay position data out to qexec -- values come from self.cumulative_performance #
|
||||
######################################################################################################
|
||||
|
||||
####################################################################
|
||||
#######TODO: relay the results of self.to_dict() ###########
|
||||
####################################################################
|
||||
|
||||
#roll over positions to current day.
|
||||
self.todays_performance.calculate_performance()
|
||||
@@ -115,24 +205,21 @@ class PerformanceTracker():
|
||||
self.trading_environment
|
||||
)
|
||||
|
||||
######################################################################################################
|
||||
#######TODO: report/relay metrics out to qexec -- values come from self.risk_report ###########
|
||||
######################################################################################################
|
||||
####################################################################
|
||||
#######TODO: relay the results of self.risk_report.to_dict() #######
|
||||
####################################################################
|
||||
|
||||
def round_to_nearest(self, x, base=5):
|
||||
return int(base * round(float(x)/base))
|
||||
|
||||
class Position():
|
||||
sid = None
|
||||
amount = None
|
||||
cost_basis = None
|
||||
last_sale = None
|
||||
last_date = None
|
||||
|
||||
def __init__(self, sid):
|
||||
self.sid = sid
|
||||
self.amount = 0
|
||||
self.cost_basis = 0.0 ##per share
|
||||
self.last_sale_price = None
|
||||
self.last_sale_date = None
|
||||
|
||||
def update(self, txn):
|
||||
if(self.sid != txn.sid):
|
||||
@@ -156,24 +243,53 @@ class Position():
|
||||
|
||||
def __repr__(self):
|
||||
template = "sid: {sid}, amount: {amount}, cost_basis: {cost_basis}, \
|
||||
last_sale: {last_sale}"
|
||||
last_sale_price: {last_sale_price}"
|
||||
return template.format(
|
||||
sid=self.sid,
|
||||
amount=self.amount,
|
||||
cost_basis=self.cost_basis,
|
||||
last_sale=self.last_sale
|
||||
last_sale_price=self.last_sale_price
|
||||
)
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
Creates a dictionary representing the state of this position.
|
||||
Returns a dict object of the form:
|
||||
+-----------------+----------------------------------------------------+
|
||||
| key | value |
|
||||
+=================+====================================================+
|
||||
| sid | the identifier for the security held in this |
|
||||
| | position. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| amount | whole number of shares in the position |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| last_sale_price | price at last sale of the security on the exchange |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| last_sale_date | datetime of the last trade of the position's |
|
||||
| | security on the exchange |
|
||||
+-----------------+----------------------------------------------------+
|
||||
"""
|
||||
state = {
|
||||
'sid':self.sid,
|
||||
'amount':self.amount,
|
||||
'cost_basis':self.cost_basis,
|
||||
'last_sale_price':self.last_sale_price,
|
||||
'last_sale_date':self.last_sale_date
|
||||
}
|
||||
return state
|
||||
|
||||
class PerformancePeriod():
|
||||
|
||||
def __init__(self, initial_positions, starting_value, starting_cash):
|
||||
self.ending_value = 0.0
|
||||
self.period_capital_used = 0.0
|
||||
self.positions = initial_positions #sid => position object
|
||||
self.starting_value = starting_value
|
||||
self.ending_value = 0.0
|
||||
self.period_capital_used = 0.0
|
||||
self.pnl = 0.0
|
||||
#sid => position object
|
||||
self.positions = initial_positions
|
||||
self.starting_value = starting_value
|
||||
#cash balance at start of period
|
||||
self.starting_cash = starting_cash
|
||||
self.ending_cash = starting_cash
|
||||
self.starting_cash = starting_cash
|
||||
self.ending_cash = starting_cash
|
||||
|
||||
def calculate_performance(self):
|
||||
self.ending_value = self.calculate_positions_value()
|
||||
@@ -194,7 +310,6 @@ class PerformancePeriod():
|
||||
self.positions[txn.sid].update(txn)
|
||||
self.period_capital_used += -1 * txn.price * txn.amount
|
||||
|
||||
|
||||
def calculate_positions_value(self):
|
||||
mktValue = 0.0
|
||||
for key,pos in self.positions.iteritems():
|
||||
@@ -202,9 +317,48 @@ class PerformancePeriod():
|
||||
return mktValue
|
||||
|
||||
def update_last_sale(self, event):
|
||||
if self.positions.has_key(event.sid) and event.type == zp.DATASOURCE_TYPE.TRADE:
|
||||
self.positions[event.sid].last_sale = event.price
|
||||
self.positions[event.sid].last_date = event.dt
|
||||
is_trade = event.type == zp.DATASOURCE_TYPE.TRADE
|
||||
if self.positions.has_key(event.sid) and is_trade:
|
||||
self.positions[event.sid].last_sale_price = event.price
|
||||
self.positions[event.sid].last_sale_date = event.dt
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
Creates a dictionary representing the state of this performance period
|
||||
Returns a dict object of the form:
|
||||
|
||||
+---------------+-----------------------------------------------------------+
|
||||
| key | value |
|
||||
+===============+===========================================================+
|
||||
| ending_value | the total market value of the positions held at the |
|
||||
| | end of the period |
|
||||
+---------------+-----------------------------------------------------------+
|
||||
| capital_used | the net capital consumed (positive means spent) by |
|
||||
| | buying and selling securities in the period |
|
||||
+---------------+-----------------------------------------------------------+
|
||||
| starting_value| the total market value of the positions held at the |
|
||||
| | start of the period |
|
||||
+---------------+-----------------------------------------------------------+
|
||||
| starting_cash | cash on hand at the beginning of the period |
|
||||
+---------------+-----------------------------------------------------------+
|
||||
| ending_cash | cash on hand at the end of the period |
|
||||
+---------------+-----------------------------------------------------------+
|
||||
| positions | a list of dicts representing positions, see |
|
||||
| | :py:meth:`Position.to_dict()` |
|
||||
| | for details on the contents of the dict |
|
||||
+---------------+-----------------------------------------------------------+
|
||||
"""
|
||||
d = {
|
||||
'ending_value':self.ending_value,
|
||||
'capital_used':self.capital_used,
|
||||
'starting_value':self.starting_value,
|
||||
'starting_cash':self.starting_cash,
|
||||
'ending_cash':self.ending_cash
|
||||
}
|
||||
|
||||
|
||||
|
||||
position_list = []
|
||||
for pos in self.positions:
|
||||
position_list.append(pos.to_dict())
|
||||
|
||||
d['positions'] = positions_list
|
||||
return d
|
||||
+138
-42
@@ -7,20 +7,25 @@ import zipline.util as qutil
|
||||
import zipline.protocol as zp
|
||||
from pymongo import ASCENDING, DESCENDING
|
||||
|
||||
class daily_return():
|
||||
class DailyReturn():
|
||||
|
||||
def __init__(self, date, returns):
|
||||
self.date = date
|
||||
self.returns = returns
|
||||
|
||||
def to_dict(self):
|
||||
d = {
|
||||
'dt': self.date,
|
||||
'returns': self.returns
|
||||
}
|
||||
|
||||
return d
|
||||
|
||||
def __repr__(self):
|
||||
return str(self.date) + " - " + str(self.returns)
|
||||
|
||||
class RiskMetrics():
|
||||
def __init__(self, start_date, end_date, returns, trading_environment):
|
||||
"""
|
||||
:param treasury_curves: {datetime in utc -> {duration label -> interest rate}}
|
||||
"""
|
||||
|
||||
self.treasury_curves = trading_environment.treasury_curves
|
||||
self.start_date = start_date
|
||||
@@ -31,32 +36,100 @@ class RiskMetrics():
|
||||
|
||||
self.benchmark_period_returns, self.benchmark_returns = self.calculate_period_returns(benchmark_returns)
|
||||
if(len(self.benchmark_returns) != len(self.algorithm_returns)):
|
||||
raise Exception("Mismatch between benchmark_returns ({bm_count}) and algorithm_returns ({algo_count}) in range {start} : {end}".format(
|
||||
bm_count=len(self.benchmark_returns),
|
||||
algo_count=len(self.algorithm_returns),
|
||||
start=start_date,
|
||||
end=end_date))
|
||||
message = "Mismatch between benchmark_returns ({bm_count}) and \
|
||||
algorithm_returns ({algo_count}) in range {start} : {end}"
|
||||
message.format(
|
||||
bm_count=len(self.benchmark_returns),
|
||||
algo_count=len(self.algorithm_returns),
|
||||
start=start_date,
|
||||
end=end_date
|
||||
)
|
||||
|
||||
raise Exception(messge)
|
||||
self.trading_days = len(self.benchmark_returns)
|
||||
self.benchmark_volatility = self.calculate_volatility(self.benchmark_returns)
|
||||
self.algorithm_volatility = self.calculate_volatility(self.algorithm_returns)
|
||||
self.treasury_period_return = self.choose_treasury()
|
||||
self.sharpe = self.calculate_sharpe()
|
||||
self.beta, self.algorithm_covariance, self.benchmark_variance, self.condition_number, self.eigen_values = self.calculate_beta()
|
||||
self.beta, self.algorithm_covariance, self.benchmark_variance, \
|
||||
self.condition_number, self.eigen_values = self.calculate_beta()
|
||||
self.alpha = self.calculate_alpha()
|
||||
self.excess_return = self.algorithm_period_returns - self.treasury_period_return
|
||||
self.max_drawdown = self.calculate_max_drawdown()
|
||||
|
||||
def to_dict(self):
|
||||
"""
|
||||
+-----------------+----------------------------------------------------+
|
||||
| key | value |
|
||||
+=================+====================================================+
|
||||
| trading_days | The number of trading days between self.start_date |
|
||||
| | and self.end_date |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| benchmark_volat\| The volatility of the benchmark between |
|
||||
| ility | self.start_date and self.end_date. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| algo_volatility | The volatility of the algo between self.start_date |
|
||||
| | and self.end_date. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| treasury_period\| The return of treasuries over the period. Treasury |
|
||||
| _return | maturity is chosen to match the duration of the |
|
||||
| | test period. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| sharpe | The sharpe ratio based on the _algorithm_ (rather |
|
||||
| | than the static portfolio) returns. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| beta | The _algorithm_ beta to the benchmark. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| alpha | The _algorithm_ alpha to the benchmark. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| excess_return | The excess return of the algorithm over the |
|
||||
| | benchmark. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
| max_drawdown | The largest relative peak to relative trough move |
|
||||
| | for the portfolio returns between self.start_date |
|
||||
| | and self.end_date. |
|
||||
+-----------------+----------------------------------------------------+
|
||||
"""
|
||||
d = {
|
||||
'trading_days' : self.trading_days,
|
||||
'benchmark_volatility' : self.benchmark_volatility,
|
||||
'algo_volatility' : self.algo_volatility,
|
||||
'treasury_period_return': self.treasury_period_return,
|
||||
'sharpe' : self.sharpe,
|
||||
'beta' : self.beta,
|
||||
'alpha' : self.alpha,
|
||||
'excess_return' : self.excess_return,
|
||||
'max_drawdown' : self.max_drawdown
|
||||
}
|
||||
|
||||
def __repr__(self):
|
||||
statements = []
|
||||
for metric in ["algorithm_period_returns", "benchmark_period_returns", "excess_return", "trading_days", "benchmark_volatility", "algorithm_volatility", "sharpe", "algorithm_covariance", "benchmark_variance", "beta", "alpha", "max_drawdown", "algorithm_returns", "benchmark_returns", "condition_number", "eigen_values"]:
|
||||
for metric in [
|
||||
"algorithm_period_returns",
|
||||
"benchmark_period_returns",
|
||||
"excess_return",
|
||||
"trading_days",
|
||||
"benchmark_volatility",
|
||||
"algorithm_volatility",
|
||||
"sharpe",
|
||||
"algorithm_covariance",
|
||||
"benchmark_variance",
|
||||
"beta",
|
||||
"alpha",
|
||||
"max_drawdown",
|
||||
"algorithm_returns",
|
||||
"benchmark_returns",
|
||||
"condition_number",
|
||||
"eigen_values"
|
||||
]:
|
||||
value = getattr(self, metric)
|
||||
statements.append("{metric}:{value}".format(metric=metric, value=value))
|
||||
statements.append("{m}:{v}".format(m=metric, v=value))
|
||||
|
||||
return '\n'.join(statements)
|
||||
|
||||
def calculate_period_returns(self, daily_returns):
|
||||
#TODO: replace this with pandas.
|
||||
returns = [x.returns for x in daily_returns if x.date >= self.start_date and x.date <= self.end_date and self.trading_environment.is_trading_day(x.date)]
|
||||
#qutil.LOGGER.debug("using {count} daily returns out of {total}".format(count=len(returns),total=len(daily_returns)))
|
||||
period_returns = 1.0
|
||||
for r in returns:
|
||||
period_returns = period_returns * (1.0 + r)
|
||||
@@ -71,8 +144,8 @@ class RiskMetrics():
|
||||
return (self.algorithm_period_returns - self.treasury_period_return) / self.algorithm_volatility
|
||||
|
||||
def calculate_beta(self):
|
||||
#qutil.LOGGER.debug("algorithm has {acount} days, benchmark has {bmcount} days".format(acount=len(self.algorithm_returns), bmcount=len(self.benchmark_returns)))
|
||||
#it doesn't make much sense to calculate beta for less than two days, so return none.
|
||||
#it doesn't make much sense to calculate beta for less than two days,
|
||||
#so return none.
|
||||
if len(self.algorithm_returns) < 2:
|
||||
return 0.0, 0.0, 0.0, 0.0, []
|
||||
returns_matrix = np.vstack([self.algorithm_returns, self.benchmark_returns])
|
||||
@@ -82,7 +155,6 @@ class RiskMetrics():
|
||||
algorithm_covariance = C[0][1]
|
||||
benchmark_variance = C[1][1]
|
||||
beta = C[0][1] / C[1][1]
|
||||
#qutil.LOGGER.debug("bm variance is {bmv}, returns matrix is {rm}, covariance is {c}, beta is {beta}".format(rm=returns_matrix, bmv=C[1][1], c=C, beta=beta))
|
||||
|
||||
return beta, algorithm_covariance, benchmark_variance, condition_number, eigen_values
|
||||
|
||||
@@ -101,7 +173,6 @@ class RiskMetrics():
|
||||
cur_return = 0.0
|
||||
compounded_returns.append(cur_return)
|
||||
|
||||
#qutil.LOGGER.debug("compounded returns are {cr}".format(cr=compounded_returns))
|
||||
cur_max = None
|
||||
max_drawdown = None
|
||||
for cur in compounded_returns:
|
||||
@@ -112,7 +183,6 @@ class RiskMetrics():
|
||||
if max_drawdown == None or drawdown < max_drawdown:
|
||||
max_drawdown = drawdown
|
||||
|
||||
#qutil.LOGGER.debug("max drawdown is: {dd}".format(dd=max_drawdown))
|
||||
if max_drawdown == None:
|
||||
return 0.0
|
||||
|
||||
@@ -146,10 +216,10 @@ class RiskMetrics():
|
||||
one_day = datetime.timedelta(days=1)
|
||||
|
||||
curve = None
|
||||
#in case end date is not a trading day, search for the next market day for an interest rate
|
||||
# in case end date is not a trading day, search for the next market
|
||||
# day for an interest rate
|
||||
for i in range(7):
|
||||
if(self.treasury_curves.has_key(self.end_date + i * one_day)):
|
||||
#qutil.LOGGER.info(self.treasury_curves[self.end_date + i * one_day])
|
||||
curve = self.treasury_curves[self.end_date + i * one_day]
|
||||
break
|
||||
|
||||
@@ -162,38 +232,58 @@ class RiskMetrics():
|
||||
if rate != None:
|
||||
return rate * (td.days + 1) / 365
|
||||
|
||||
raise Exception("no rate for end date = {dt} and term = {term}. Using zero.".format(dt=self.end_date,
|
||||
term=self.treasury_duration))
|
||||
message = "no rate for end date = {dt} and term = {term}. Using zero."
|
||||
message = message.format(dt=self.end_date,term=self.treasury_duration)
|
||||
raise Exception(message)
|
||||
|
||||
class RiskReport():
|
||||
|
||||
def __init__(self, algorithm_returns, benchmark_returns, treasury_curves, trading_environment):
|
||||
"""algorithm_returns needs to be a list of daily_return objects sorted in date ascending order"""
|
||||
def __init__(self, algorithm_returns, trading_environment):
|
||||
""" algorithm_returns needs to be a list of daily_return objects
|
||||
sorted in date ascending order
|
||||
"""
|
||||
|
||||
self.algorithm_returns = algorithm_returns
|
||||
self.treasury_curves = treasury_curves
|
||||
self.trading_environment = trading_environment
|
||||
|
||||
start_date = self.algorithm_returns[0].date
|
||||
end_date = self.algorithm_returns[-1].date
|
||||
|
||||
self.month_periods = self.periodsInRange(1, start_date, end_date)
|
||||
self.three_month_periods = self.periodsInRange(3, start_date, end_date)
|
||||
self.six_month_periods = self.periodsInRange(6, start_date, end_date)
|
||||
self.year_periods = self.periodsInRange(12, start_date, end_date)
|
||||
|
||||
#calculate month ends
|
||||
self.month_periods = self.periodsInRange(1, self.algorithm_returns[0].date, self.algorithm_returns[-1].date)
|
||||
#calculate 3 month ends
|
||||
self.three_month_periods = self.periodsInRange(3, self.algorithm_returns[0].date, self.algorithm_returns[-1].date)
|
||||
#calculate 6 month ends
|
||||
self.six_month_periods = self.periodsInRange(6, self.algorithm_returns[0].date, self.algorithm_returns[-1].date)
|
||||
#calculate 1 year ends
|
||||
self.year_periods = self.periodsInRange(12, self.algorithm_returns[0].date, self.algorithm_returns[-1].date)
|
||||
#calculate 3 year ends
|
||||
self.three_year_periods = self.periodsInRange(36, self.algorithm_returns[0].date, self.algorithm_returns[-1].date)
|
||||
#calculate 5 year ends
|
||||
self.five_year_periods = self.periodsInRange(60, self.algorithm_returns[0].date, self.algorithm_returns[-1].date)
|
||||
def to_dict(self):
|
||||
"""
|
||||
RiskMetrics are calculated for rolling windows in four lengths::
|
||||
- 1_month
|
||||
- 3_month
|
||||
- 6_month
|
||||
- 12_month
|
||||
|
||||
The return value of this funciton is a dictionary keyed by the above
|
||||
list of durations. The value of each entry is a list of RiskMetric
|
||||
dicts of the same duration as denoted by the top_level key.
|
||||
|
||||
See :py:meth:`RiskMetrics.to_dict` for the detailed list of fields
|
||||
provided for each period.
|
||||
"""
|
||||
d = {
|
||||
'1_month' : [x.to_dict() for x in self.month_periods],
|
||||
'3_month' : [x.to_dict() for x in self.three_year_periods],
|
||||
'6_month' : [x.to_dict() for x in self.six_month_periods],
|
||||
'12_month' : [x.to_dict() for x in self.month_periods]
|
||||
}
|
||||
|
||||
return d
|
||||
|
||||
def periodsInRange(self, months_per, start, end):
|
||||
one_day = datetime.timedelta(days = 1)
|
||||
ends = []
|
||||
cur_start = start.replace(day=1)
|
||||
#ensure that we have an end at the end of a calendar month, in case the return series ends mid-month...
|
||||
#ensure that we have an end at the end of a calendar month, in case
|
||||
#the return series ends mid-month...
|
||||
the_end = advance_by_months(end.replace(day=1),1) - one_day
|
||||
while True:
|
||||
cur_end = advance_by_months(cur_start, months_per) - one_day
|
||||
@@ -223,9 +313,10 @@ def advance_by_months(dt, jump_in_months):
|
||||
years = month / 12
|
||||
month = month % 12
|
||||
|
||||
#no remainder means that we are landing in december.
|
||||
#modulo is, in a way, a zero indexed circular array.
|
||||
#this is a way of converting to 1 indexed months. (in our modulo index, december is zeroth)
|
||||
# no remainder means that we are landing in december.
|
||||
# modulo is, in a way, a zero indexed circular array.
|
||||
# this is a way of converting to 1 indexed months.
|
||||
# (in our modulo index, december is zeroth)
|
||||
if(month == 0):
|
||||
month = 12
|
||||
years = years - 1
|
||||
@@ -245,7 +336,12 @@ class TradingEnvironment(object):
|
||||
self.trading_day_map[bm.date] = bm
|
||||
|
||||
def normalize_date(self, test_date):
|
||||
return datetime.datetime(year=test_date.year, month=test_date.month, day=test_date.day, tzinfo=pytz.utc)
|
||||
return datetime.datetime(
|
||||
year=test_date.year,
|
||||
month=test_date.month,
|
||||
day=test_date.day,
|
||||
tzinfo=pytz.utc
|
||||
)
|
||||
|
||||
def is_trading_day(self, test_date):
|
||||
dt = self.normalize_date(test_date)
|
||||
|
||||
+14
-10
@@ -15,7 +15,7 @@ class TradeSimulationClient(qmsg.Component):
|
||||
qmsg.Component.__init__(self)
|
||||
self.received_count = 0
|
||||
self.prev_dt = None
|
||||
self.event_queue = []
|
||||
self.event_queue = None
|
||||
self.event_callbacks = []
|
||||
self.txn_count = 0
|
||||
self.current_dt = simulation_dt
|
||||
@@ -60,12 +60,10 @@ class TradeSimulationClient(qmsg.Component):
|
||||
if event.source_id != zp.FINANCE_COMPONENT.ORDER_SOURCE:
|
||||
#mark the start time for client's processing of this event.
|
||||
event_start = datetime.datetime.utcnow()
|
||||
|
||||
self.queue_event(event)
|
||||
for cb in self.event_callbacks:
|
||||
if(event.dt < self.current_dt):
|
||||
self.queue_event(event)
|
||||
else:
|
||||
cb(self.event_frame)
|
||||
if(event.dt >= self.current_dt):
|
||||
cb(self.get_frame())
|
||||
|
||||
#update time based on receipt of the order
|
||||
self.last_iteration_duration = datetime.datetime.utcnow() - event_start
|
||||
@@ -90,10 +88,16 @@ class TradeSimulationClient(qmsg.Component):
|
||||
def signal_order_done(self):
|
||||
self.order_socket.send(str(zp.ORDER_PROTOCOL.DONE))
|
||||
|
||||
def frame_event(self, event):
|
||||
if self.event_frame == None:
|
||||
self.event_frame = pandas.DataFrame()
|
||||
self.event_frame.append(event)
|
||||
def queue_event(self, event):
|
||||
if self.event_queue == None:
|
||||
self.event_queue = {}
|
||||
self.event_queue[event.dt] = event.as_series()
|
||||
|
||||
def get_frame(self):
|
||||
sorted_dates = sorted(self.event_queue.keys())
|
||||
frame = pandas.DataFrame(self.event_queue, index=sorted_dates)
|
||||
self.event_queue = None
|
||||
return frame
|
||||
|
||||
class OrderDataSource(qmsg.DataSource):
|
||||
"""DataSource that relays orders from the client"""
|
||||
|
||||
+2
-1
@@ -208,7 +208,8 @@ class namedict(object):
|
||||
return self.__dict__.has_key(name)
|
||||
|
||||
def as_series(self):
|
||||
s = pandas.Series(self.values(), self.keys())
|
||||
s = pandas.Series(self.__dict__.values(), self.__dict__.keys())
|
||||
return s
|
||||
|
||||
# ================
|
||||
# Control Protocol
|
||||
|
||||
@@ -19,7 +19,7 @@ def load_market_data():
|
||||
tzinfo=pytz.utc
|
||||
)
|
||||
|
||||
daily_return = risk.daily_return(date=event_dt, returns=returns)
|
||||
daily_return = risk.DailyReturn(date=event_dt, returns=returns)
|
||||
bm_returns.append(daily_return)
|
||||
bm_returns = sorted(bm_returns, key=lambda(x): x.date)
|
||||
fp_tr = open("./zipline/test/treasury_curves.msgpack", "rb")
|
||||
@@ -93,7 +93,7 @@ def create_returns(daycount, start, trading_calendar):
|
||||
one_day = datetime.timedelta(days = 1)
|
||||
while i < daycount:
|
||||
i += 1
|
||||
r = risk.daily_return(current, random.random())
|
||||
r = risk.DailyReturn(current, random.random())
|
||||
test_range.append(r)
|
||||
current = current + one_day
|
||||
return [ x for x in test_range if(trading_calendar.is_trading_day(x.date)) ]
|
||||
@@ -109,7 +109,7 @@ def create_returns_from_range(start, end, trading_calendar):
|
||||
current = current + one_day
|
||||
if(not trading_calendar.is_trading_day(current)):
|
||||
continue
|
||||
r = risk.daily_return(current, random.random())
|
||||
r = risk.DailyReturn(current, random.random())
|
||||
i += 1
|
||||
test_range.append(r)
|
||||
|
||||
@@ -122,7 +122,7 @@ def create_returns_from_list(returns, start, trading_calendar):
|
||||
i = 0
|
||||
while len(test_range) < len(returns):
|
||||
if(trading_calendar.is_trading_day(current)):
|
||||
r = risk.daily_return(current, returns[i])
|
||||
r = risk.DailyReturn(current, returns[i])
|
||||
i += 1
|
||||
test_range.append(r)
|
||||
current = current + one_day
|
||||
|
||||
Reference in New Issue
Block a user