-
Notifications
You must be signed in to change notification settings - Fork 261
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
SimulatorCost
can't use arbitrary estimators as nested dependents; subclassing is difficult
#186
Comments
Hello, thank you. You're referring to line 443 of costs.py; I know the logic is tricky there but can you please explain which specific usecase broke for you? I'm quite confident it works fine with the derived classes implemented in Cvxportfolio, I would need to know more about your usecase to see what needs fixing. |
PS you implemented a derived cost and you have custom logic inside your |
Ok sorry now I see your point; you're probably right let me try the change and will cut a revision if necessary. Thanks! |
Thx for reply!
|
I guess I'll refactor the import cvxportfolio as cvx
import cvxpy as cp
import numpy as np
class MyCustomCost (cvx.costs.SimulatorCost):
def __init__(self, a=5/10_000, b = 1 / 10_000):
self.a = None if a is None else cvx.estimator.DataEstimator(a)
self.b = None if b is None else cvx.estimator.DataEstimator(b)
self._first_term_multiplier = None
self._second_term_multiplier = None
super().__init__()
def initialize_estimator(self, universe, **kwargs):
if self.a is not None:
self._first_term_multiplier = cp.Parameter(
len(universe)-1, nonneg=True)
if self.b is not None:
self._second_term_multiplier = cp.Parameter(
len(universe)-1, nonneg=True)
super().initialize_estimator(universe=universe, **kwargs) # initial w w_plus ...
def values_in_time(self, **kwargs):
if self.a is not None:
self._first_term_multiplier.value = np.ones(self._first_term_multiplier.size) * self.a.current_value
if self.b is not None:
self._second_term_multiplier.value = np.ones(self._second_term_multiplier.size) * self.b.current_value
def compile_to_cvxpy(self, z, **kwargs):
expression = 0
if self.a is not None:
expression += cp.abs(z[:-1]) @ self._first_term_multiplier
assert expression.is_convex()
if self.b is not None:
expression += cp.abs(z[:-1]) @ self._second_term_multiplier
assert expression.is_convex()
return expression
# send cost to simulator creator, MyCustomCost is like TransactionCost but with default fee
# and will use self.xx.current_value to populate
UNIVERSE = ['AAPL', 'GOOG']
simulator = cvx.MarketSimulator(
universe=UNIVERSE,
costs = [MyCustomCost])
print(simulator.backtest(cvx.Uniform(), start_time='2025-01-01')) And it works fine, here's the output |
SimulatorCost.simulate
should call self.values_in_time_recursive
Sorry, I recheck the code and find that cost of simulator will call |
Let me reopen this. I remembered now how I designed that logic and it does break if you push it enough. Your example works because |
SimulatorCost.simulate
should call self.values_in_time_recursive
SimulatorCost
can't use arbitrary estimators as nested dependents; subclassing is difficult
class SimlatorCost's member function
simulate
call self.values_in_time to update parameter value, but when thecost
has member field that need to update, it will make a mistake.The text was updated successfully, but these errors were encountered: