ENH: Add exit tags via Trade.close(tag=) / Position.close(tag=) - #1386
Open
azerv1 wants to merge 1 commit into
Open
ENH: Add exit tags via Trade.close(tag=) / Position.close(tag=)#1386azerv1 wants to merge 1 commit into
azerv1 wants to merge 1 commit into
Conversation
Trades could already be tagged on entry (Order.tag, inherited as Trade.tag), but there was no way to record *why* a trade was closed: the order placed by Trade.close() unconditionally inherited the entry tag, so post-run analysis could not tell a session-end exit from a signal flip from a discretionary close. Add an optional `tag` keyword to Trade.close() and Position.close() which marks the closing order, and surface it afterwards as Trade.exit_tag and as the ExitTag column of stats._trades. This enables subgroup analysis of exit reasons. The tag recorded is that of whichever order actually closed the trade, so a trade closed FIFO by an opposite order picks up that order's tag. When no tag is passed the closing order keeps inheriting Trade.tag exactly as before, so existing behaviour is unchanged. Contingent SL/TP orders are likewise left alone. Closes kernc#1303 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Closes #1303, originally raised in discussion #1298.
Problem
Trades can be tagged on entry —
self.buy(tag='breakout')flows through toTrade.tagand theTagcolumn ofstats._trades— but there is no equivalent for the exit.Trade.close()unconditionally gives the closing order the entry tag, so after a run you cannot distinguish a
session-end exit from a signal flip from a discretionary close.
Change
An optional
tagkeyword onTrade.close()andPosition.close(), readable afterwards asTrade.exit_tagand as anExitTagcolumn instats._trades:The tag recorded is that of whichever order actually closed the trade, so a trade closed FIFO by an
opposite order picks up that order's tag.
Design notes
Order.tagwas introduced in Add Order.tag for tracking orders and trades #200: an optionaltag: object = Nonethreaded through existing signatures, rather than a new method.tagis keyword-only, so the existing positionalportionargument is unaffected.tagpassed, the closing order still inheritsTrade.tagexactly as before, which means
ExitTagmirrorsTagby default rather than being null. Thiskeeps
Order.tagbehaviour unchanged for close orders — deliberate, since the reporter of close position with some note #1303was relying on it — but I'm happy to change it if you'd prefer
exit_tagbe set only whenexplicitly provided.
whether SL or TP fired is already derivable from the existing
SL/TP/ExitPricecolumns.Giving them automatic
'sl'/'tp'tags would change existing behaviour, so I kept it out ofscope — say the word if you'd like it.
Tests
Three new tests covering an explicit close tag, inheritance when omitted, a partial close where
_reduce_tradesplits the trade (each half keeping its own tag), and a FIFO close by an oppositeorder. The existing
stats._tradescolumn assertion is updated for the new column.python -m backtesting.testpasses (84 tests), as doflake8 backtestingandmypy backtesting.optimize()timing is unchanged within run-to-run noise.