ENH: Add exit_tag support for trades and positions to indicate closur… - #1403
Open
Harsh4r0ra wants to merge 1 commit into
Open
ENH: Add exit_tag support for trades and positions to indicate closur…#1403Harsh4r0ra wants to merge 1 commit into
Harsh4r0ra wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Expose why a trade closed:
tag=onPosition.close()/Trade.close(),Trade.exit_tag, auto"sl"/"tp"taggingCloses [kernc/backtesting.py#1352](#1352)
Summary
Order.tag/Trade.tagalready let you tag why a trade was opened, but there was no way to know why a trade was closed — hit its stop-loss, hit its take-profit, an explicitposition.close()/trade.close()call, or closed as a side effect of an opposing signal.Trade.close()always re-used the trade's opening tag on the closing order, so that information was lost.This implements the design agreed on in the issue thread between
kerncandatharvajoshi01:Position.close()/Trade.close()gain an optional keyword-onlytag=so callers can say why they're closing, e.g.self.position.close(tag="stop condition X")."sl"/"tp"with no extra API needed — the reason is always inferable.Trade.exit_tagproperty surfaces this, parallel to the existing (opening)Trade.tag.'ExitTag'column is added tostats._trades, alongside the existing'Tag'column (which keeps its current meaning — the opening tag — unchanged).Changes
backtesting/backtesting.pyPosition.close(self, portion=1., *, tag=None)— threadstagto eachtrade.close(portion, tag=tag)call.Trade.close(self, portion=1., *, tag=None)— usestagfor the closing order if given; otherwise falls back to the trade's opening tag (unchanged behavior — fully backward compatible).Trade.exit_tag(new property) — the tag of the order that closed the trade;Nonewhile the trade is still open.Trade.tagis untouched — still the opening tag, fixed for the life of the trade._Broker._process_orders()— when the closing order istrade._sl_orderortrade._tp_order, the exit tag defaults to"sl"/"tp"unless that order's tag was already explicitly set to something other than the trade's opening tag (i.e.order.tag == trade.tagis treated as "unset"). An explicittrade.close(tag=...)/position.close(tag=...)order always uses its own tag and is never overwritten._Broker._reduce_trade()/_close_trade()— gained anexit_tag=Noneparameter that's threaded through and stamped onto the closedTradeviatrade._replace(exit_tag=...). For partial closes,_reduce_trade()correctly stamps the tag onto the closed copy of the trade, not the remaining open one.backtesting/_stats.py'ExitTag'column totrades_df, sourced fromt.exit_tag, next to the existing'Tag'column.'Tag'itself is untouched.backtesting/test/_test.pytag=ontrade.close()/position.close(); automatic"sl"/"tp"tagging on SL/TP-triggered exits; theNonedefault for a trade closed via an opposing order fill (notrade.close()call at all); a partial close landing the tag on the correct (closed) half while the remaining open trade keepsexit_tag is None; and backward compatibility (notag=used anywhere still produces identicalTrade.tag/'Tag'behavior as before).test_compute_stats's hardcoded list of expectedstats._tradescolumns to include'ExitTag'.CHANGELOG.md### 0.x.x(unreleased).Judgment calls — flagging for @kernc
trade.close()call anywhere: I default this toNone. There's no natural tag to infer here since the close isn't driven by any tagged order/call — happy to change this if you'd rather it inherit the new order's tag instead.order.tag == trade.tagto mean "not explicitly set," sinceTrade.__set_contingent()(backingtrade.sl =/trade.tp =) doesn't currently accept a custom tag for the contingent order — so in practice this branch always fires today. This just future-proofs the rule per your note in the issue, without adding new public API surface right now (notag=kwarg was requested forsl/tpassignment itself).Test plan
Red/green-verified: temporarily disabled the SL/TP auto-tagging branch — the two new SL/TP tests failed as expected (
None != 'sl',None != 'tp'); restored and both pass again.