qstrader
qstrader copied to clipboard
Handling stop-levels?
Hi,
I just started working with qsTrader recently and really learned a lot already.
However, conceptually, it's unclear to me in which component of the system one would handle stop levels.
I was initially thinking about the position object, but that work if you allow scaling in- and out of positions very well. The strategy class is probably also not the right place, otherwise it would have to memorize all the trade-entry levels (redundancy with the position object).
The risk manager and position sizer might at some point need access to this information in order to evaluate the risk of currently open positions, etc.
Thanks for the clarification, Dirk
Hi Dirk,
IMO, these kind of things belong in the RiskManager
. Currently working on IB integration so haven't devoted much time to it, but likely you'd need to subclass the RiskManager. I imagine it should have a link to the Portfolio (if it doesn't already) in the init() function, and it should do things like handle stop losses, adjust position weights by issuing buy/sell orders, etc, as the main trading loop happens.
Hi Ryan,
Thanks for your thoughts. I believe the RiskManager is the right place. It already has access to the Portfolio object, so that should be fairly easy to do - happy to discuss further and share ideas once you have capacity again.
Dirk
Hi Dirk,
I fully agree with Ryan here - the stop loss is a good usage for a subclass of RiskManager.
I think both the PositionSizer and RiskManager have access to the Portfolio, but if they don't then I'll certainly update the code to make sure they do!
Cheers,
Mike.
Hi @mhallsmoore, @ryankennedyio, I'm currently trying to implement a strategy which specifies the stop loss as part of the strategy (in this case a percentage of the Average True Range indicator). I was thinking therefore that stop_loss
should be a field / instance variable on the SignalEvent
class and set by the strategy at the point when it creates a SignalEvent
object. But reading the comments above it sounds like perhaps the suggested approach would be to create a sub-class of RiskManager
called something like ATRStopLossRiskManager
, so this part of "the strategy" is separated - it's no longer physically implemented in the subclass of AbstractStrategy
, but is constructed in the main / run method and injected into the trading session (BackTest
) object. However I'm not clear exactly how this fits into the back test loop..
print("Running Backtest...")
while self.price_handler.continue_backtest:
try:
event = self.events_queue.get(False)
except queue.Empty:
self.price_handler.stream_next()
else:
if event is not None:
if event.type == EventType.TICK or event.type == EventType.BAR:
self.cur_time = event.time
# Generate any sentiment events here
if self.sentiment_handler is not None:
self.sentiment_handler.stream_next(
stream_date=self.cur_time
)
self.strategy.calculate_signals(event)
self.portfolio_handler.update_portfolio_value()
self.statistics.update(event.time, self.portfolio_handler)
elif event.type == EventType.SENTIMENT:
self.strategy.calculate_signals(event)
elif event.type == EventType.SIGNAL:
self.portfolio_handler.on_signal(event)
elif event.type == EventType.ORDER:
self.execution_handler.execute_order(event)
elif event.type == EventType.FILL:
self.portfolio_handler.on_fill(event)
else:
raise NotImplemented("Unsupported event.type '%s'" % event.type)
The custom RiskManager
will need access to the price data in order to produce the Average True Range indicator (unless there is a better place for that - perhaps the PriceHandler
should supply indicators along with price data?), as well as check if the stop has been hit. Furthermore:
- the custom
RiskManager
will have to keep track of which stop loss applies to which trade/position - we have to provide a mechanism for the strategy to update the stop loss (without a new buy or sell singal)
- the strategy can no longer track the invested status via a private local variable because the custom
RiskManager
may have caused the trade to exit (due to hitting the stop)
Any thoughts.. ?
Hey @tomhunter-gh -- I think I had to think about this the other day, but lost my train of thought and went onto some other stuff.
Agree, there's actually no way for the risk manager to do any risk management at the moment, other than entry & exit of trades!
Shall we, in the backtest loop, at a certain time interval, do a request on the RiskManager
class ? I.e. once a period of 1 Day has passed through the system, the RiskManager
should check all positions & be allowed to make any adjustments necessary.
The time interval could be a simple parameter, to run hourly, or every X minutes depending on timeframe of the user.
What do you think @mhallsmoore ? I think it should be included out-of-the-box in the example risk managers, just as a dummy method.
Hey all @mhallsmoore, @ryankennedyio, @DirkFR, @tomhunter-gh
Has this been settled yet? I'm currently in the process of creating something similar; an ATR based stop loss subclass within the Risk Manager, but as mentioned above, I'm unsure how best combine this within my strategy, as the Strategy Class is not linked with the Portfolio.
Would it be possible to maybe add a link to the Strategy class and the Portfolio? Therefore allowing access to portfolio data in order to make calculations based on portfolio variables?
Hi @enriqueromualdez,
Give the RiskManager access to the Strategies object and then reset like this.
class Strategies(AbstractStrategy):
"""
Strategies is a collection of strategy
"""
def __init__(self, *strategies):
self._lst_strategies = strategies
def calculate_signals(self, event):
for strategy in self._lst_strategies:
strategy.calculate_signals(event)
def reset_signals(self, referance):
for strategy in self._lst_strategies:
if referance == strategy.referance:
strategy.stopped = True
That way the RiskManager can reset the Strategy without modifying the main game loop. Currently I have the RiskManager update on every new bar/tick event.
Portfolio Handler
def _risk_update(self):
"""
Updates the RiskManager with the latest Portfolios, then
collects events from the Risk Manager and places any orders
in the queue.
"""
order_events = []
# Refine or eliminate the order via the risk manager overlay
self.risk_manager.update_risk([self.portfolio])
order_events = self.risk_manager.adjustment_orders
if order_events != []:
# Place orders onto events queue
self._place_orders_onto_queue(order_events)
def update_portfolio_value(self):
"""
Update the portfolio to reflect current market value as
based on last bid/ask of each ticker.
"""
self.portfolio._update_portfolio()
self._risk_update()
RiskManager
def update_risk(self, portfolio_lst):
"""
Perform a risk assessment for each Portfolio
and Position.
Calls the PorfolioRisk class to perform porfolio risk
analysis. Once finished with the Portfolio, each Position is
analysed.
"""
self.adjustment_orders = []
for pf in portfolio_lst:
self.portfolio_risk.assess_portfolio(pf)
for ps in pf.positions:
order_event = self.position_assessment.analysis(
pf.positions[ps])
if order_event != None:
if order_event.stop_price == None:
self.strategys.reset_signals(ps)
self.adjustment_orders.append(order_event)
Hi @JamesKBowler, thanks for the help! I actually posted my solution here: #210.
A totally separate issue, but would you happen to have any suggestions for how to best solve #200? I'm working on my own approach, but would love to hear how you would go about this.