Back

Risk Management Models and Insights

I was wondering - if a risk management model liquidates a position (such as through a trailing stop or maximum drawdown) but an insight is still active after that liquidation, won't the algorithm framework just enter the position again on the next update?

Update Backtest







0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Hi Fabien,

Yes, that is exactly what happens in the Algorithm Framework. See the attached backtest for a demonstration. To overcome this behavior, the alpha model needs to be made aware of the securities the risk management model liquidates. However, doing so violates the separation of concerns design principle.

Best,
Derek Melchin

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


So using risk managment models inherently requires breaking separation of concerns?

0

Not necessarily, but if the PCM is too simple it may result in that behavior. The execution targets set in the execution model should be emptied once they're hit removing the "immediate rebuy" case.

 

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


By execution targets are you referring to portfolio targets handled in the execution model? I'm not clear on how that would avoid the rebuy.

0

Hi Fabien,

Yes, Jared is referring to the targets handled in the execution model's Execute method. To avoid rebuying, we can utilize a custom risk management model which saves the symbols that it has liquidated in the past. This way, if the PCM requests a position in a symbol that the risk management model has previously liquidated, it won't set targets for the execution model to repurchase.

class MaximumDrawdownPercentPerSecurityCustom(RiskManagementModel):

def __init__(self, maximumDrawdownPercent = 0.05):
self.maximumDrawdownPercent = -abs(maximumDrawdownPercent)
self.liquidated = set()

def ManageRisk(self, algorithm, targets):
targets = []
for kvp in algorithm.Securities:
security = kvp.Value

pnl = security.Holdings.UnrealizedProfitPercent
if pnl < self.maximumDrawdownPercent or security.Symbol in self.liquidated:
# liquidate
targets.append(PortfolioTarget(security.Symbol, 0))
if algorithm.Securities[security.Symbol].Invested:
self.liquidated.add(security.Symbol)
algorithm.Log(f"Liquidating {security.Symbol}")

return targets

See that attached backtest for the full example.

Best,
Derek Melchin

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Hi Derek, do you have any suggestions on how to reset self.liquidated every time new insights are emitted? 

Use case would be:

  • Insights are emitted on a scheduled basis (say every Friday)
  • Portfolio Model assigns weights, returns target quantities
  • Risk Model continuously monitors positions, liquidates above threshold
  • Liquidated symbols are saved in a set/dict like in your example to prevent rebuying
  • [Once new insights are emitted] Reset self.liquidated and start over

Example:

from QuantConnect import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Portfolio import PortfolioTarget
from QuantConnect.Algorithm.Framework.Risk import RiskManagementModel
from Portfolio.EqualWeightingPortfolioConstructionModel import EqualWeightingPortfolioConstructionModel

class RiskTest(QCAlgorithm):

def Initialize(self):
self.SetStartDate(2019, 1, 1)
self.SetEndDate(2019,2,1)
self.SetCash(1000)
self.AddEquity("SPY", Resolution.Minute)
self.Securities['SPY'].FeeModel = ConstantFeeModel(0)

self.liquidated = set()

# Emit new insights every Friday
self.Schedule.On(self.DateRules.Every(DayOfWeek.Friday),
self.TimeRules.AfterMarketOpen('SPY', 0),
self.PlaceOrder
)

self.SetPortfolioConstruction(MyPortfolioModel())
self.AddRiskManagement(MyRiskModel())
self.SetExecution(ImmediateExecutionModel())

def OnData(self, data):
pass

def PlaceOrder(self):
"""
Emit insight to short SPY every Friday
"""
self.liquidated = set() # Reset liquidated symbols

insight = Insight.Price('SPY', timedelta(days=30), InsightDirection.Down, None, None, None, 1)
self.EmitInsights([insight])

class MyPortfolioModel(EqualWeightingPortfolioConstructionModel):
def __init__(self):
pass

def CreateTargets(self, algorithm, insights):

# Filter out insights for symbols that have been liquidated by risk model
filtered_insights = [insight for insight in insights if insight.Symbol not in algorithm.liquidated]

# Simple insight weighting PCM
targets = []
for insight in filtered_insights:
targ = PortfolioTarget(insight.Symbol, insight.Direction*insight.Weight)
targets.append(targ)
return targets

class MyRiskModel(RiskManagementModel):
def __init__(self, maxDrawdown=1e-5):
self.maxDrawdown = maxDrawdown

def ManageRisk(self, algorithm, targets):
riskTargets = []
for _ in algorithm.Securities:
symbol = _.Key
security = _.Value

symbolPnL = security.Holdings.UnrealizedProfitPercent

# Liquidate
if (symbolPnL < -self.maxDrawdown):
riskTargets.append(PortfolioTarget(symbol, 0))
algorithm.Debug('Trailing stop loss hit.')

algorithm.liquidated.add(security.Symbol)

return riskTargets

However this does not work as algorithm refers to the QCAlgorithm object, not RiskTest. 

0

Got it to work with:

class MyRiskModel(RiskManagementModel):
def __init__(self, maxDrawdown=1e-5):
self.maxDrawdown = maxDrawdown
self.liquidated = set()
self.currentTargets = set()

def ManageRisk(self, algorithm, targets):

# Reset liquidated symbols on new targets
if set(targets) != self.currentTargets:
self.currentTargets = set(targets)
self.liquidated = set()

riskTargets = []
for _ in algorithm.Securities:
symbol = _.Key
security = _.Value

symbolPnL = security.Holdings.UnrealizedProfitPercent

# Liquidate
if (symbolPnL < -self.maxDrawdown) or (symbol in self.liquidated):
riskTargets.append(PortfolioTarget(symbol, 0))
algorithm.Debug('Trailing stop loss hit.')

self.liquidated.add(symbol)

return riskTargets

 

1

Hi Adam,

We can see in the attached backtest logs that the `liquidated` set is cleared on each call to the ManageRisk method. This causes the algorithm to reinvest in securities immediately after the risk management model liquidates a position, even if no new insights have been emitted by the alpha model. The only workarounds to this problem violate the separation of concerns design principle. We could either move the risk management logic inside the alpha model or just save a reference to the risk management model inside the alpha model. Applying the latter means removing any symbols in the `liquidated` set before the alpha model emits an insight for it.

In this situation, I recommend using the classic algorithm design instead.

Best,
Derek Melchin

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Interesting. I think the reason why 'liquidated' is being cleared with every call to the risk model in your example is that EqualWeightingPortfolioConstructionModel is using '.DetermineTargetPercent' which is a function of portfolio value. Thus as portfolio value changes, new targets are set and continuously rebalanced.

Here's a slight modification of your example with a custom portfolio model's '.CreateTargets'. We can see that the workaround I posted above works in this case: 'liquidated' is reset once at the start of the algorithm, hits the stop loss, then remains flat for the rest of the backtest.

Logs:

2020-02-20 00:00:00 : Different Targets: Quantity 1.0
2020-02-26 00:00:00 : Liquidating SPY

 

1


Here's an example where multiple insights are emitted during a backtest (in this case, every Friday).

In short, the workaround:

# Reset trackers on new targets
if set(targets) != self.currentTargets:
self.liquidatedSymbols = set()
self.currentTargets = set(targets)

can be used to reset the Risk Model when new insights are emitted only if the portfolio model does not change the targets (i.e. via rebalancing) in between insight emissions.

0


Hi Adam,

Nice catch. Indeed, this procedure works if we utilize the custom portfolio construction model described above. Although, it should be pointed out that if a risk management model relies on the use of a specific portfolio construction model to operate, the two models are still coupled together to some degree.

Best,
Derek Melchin

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Agreed. For anyone considering implementing that workaround, please keep that in mind.

Separation of Concerns is a great programming design principle for developing clean modular code that is nicely abstracted (and very reusable via a plug-and-play approach), and I highly recommend users try to adhere to it as much as possible. 

However for financial algorithms, there does often tend to be cases where it is necessary to break this separation to some extent. For instance, an Alpha Model based on computing relative weights of asset holdings in a portfolio does not make sense with an equal weighting portfolio model. Or a trend-following strategy based on some indicator cross-overs might want to set stop losses/targets (risk management) based on values computed by the Alpha Model rather than a fixed threshold. However the abstraction into modules and keeping the coupling to a minimum is still good practice.

What are your thoughts on implementing some sort of optional "state storage" that allows for minimal communication between modules in the Framework? For instance, we define an object StateStorage() that holds certain variables. 

Simple example:

- In Alpha Model, compute something and set the variable StateStorage.stopLossPrice = 1.3 based on confidence

- In Risk Model, we liquidate if price < StateStorage.stopLossPrice

- As new data comes in, Alpha Model updates the variable

Naturally this violates the separation of concerns to an extent, but each module is still concerned with a specific task and at least any breaking (if necessary) is more readily transparent.

0

I hope we can make the framework fully support all use cases. It does require thinking slightly differently though. The alphas are factors, not responsible for anything related to trading. They are simply setting a prediction of the future.

In your simple example I'd recommend:

 - Alpha Model - prediction and confidence.
 - Risk Model - Confidence Weighted Maximum Drawdown Risk Management Model
 - Alpha Model - continues to emit insights based on new information. The risk model can choose to update its risk exposure or not.

I know its tempting to install a storage/communication system but I think/hope there's always a way to avoid it! 

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


That makes sense. If i'm not mistaken however, the risk model receives the algorithm instance and an array of PortfolioTargets. Thus if someone wanted to set stop loss drawdowns based on real-time Alpha Model's computations (to let's say, a drawdown % of 1-confidence), how would a Confidence Weighted Drawdown Risk Model work?

"Communication" between Alpha Model/Portfolio Model is feasible and enough for most use cases based on the emitted Insight properties, but between Alpha Model/Risk is a bit unclear.

0

Hi Adam,

After confirming with Jared, the above is a typo. Instead, it should read

As explained above, the risk model does not receive insights and thus cannot make decisions based on confidence.

Best,
Derek Melchin

0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Update Backtest





0

The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.


Loading...

This discussion is closed