Skip to content

ENH: Add exit tags via Trade.close(tag=) / Position.close(tag=) - #1386

Open
azerv1 wants to merge 1 commit into
kernc:masterfrom
azerv1:feat-close-position-with-some-note
Open

ENH: Add exit tags via Trade.close(tag=) / Position.close(tag=)#1386
azerv1 wants to merge 1 commit into
kernc:masterfrom
azerv1:feat-close-position-with-some-note

Conversation

@azerv1

@azerv1 azerv1 commented Aug 5, 2026

Copy link
Copy Markdown

Closes #1303, originally raised in discussion #1298.

Problem

Trades can be tagged on entry — self.buy(tag='breakout') flows through to Trade.tag and the
Tag column of stats._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 tag keyword on Trade.close() and Position.close(), readable afterwards as
Trade.exit_tag and as an ExitTag column in stats._trades:

def next(self):
    if self.session_ending:
        self.position.close(tag='session end')
    elif crossover(self.sma2, self.sma1):
        self.position.close(tag='signal flip')
    EntryTime   ExitTime  Size   Tag       ExitTag
93 2012-12-03 2013-02-28    69  None  session end

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

  • Follows the approach used when Order.tag was introduced in Add Order.tag for tracking orders and trades #200: an optional
    tag: object = None threaded through existing signatures, rather than a new method.
  • tag is keyword-only, so the existing positional portion argument is unaffected.
  • Backwards compatible. With no tag passed, the closing order still inherits Trade.tag
    exactly as before, which means ExitTag mirrors Tag by default rather than being null. This
    keeps Order.tag behaviour unchanged for close orders — deliberate, since the reporter of close position with some note #1303
    was relying on it — but I'm happy to change it if you'd prefer exit_tag be set only when
    explicitly provided.
  • Contingent SL/TP orders are deliberately left alone: they keep inheriting the entry tag, and
    whether SL or TP fired is already derivable from the existing SL/TP/ExitPrice columns.
    Giving them automatic 'sl'/'tp' tags would change existing behaviour, so I kept it out of
    scope — 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_trade splits the trade (each half keeping its own tag), and a FIFO close by an opposite
order. The existing stats._trades column assertion is updated for the new column.

python -m backtesting.test passes (84 tests), as do flake8 backtesting and mypy backtesting.
optimize() timing is unchanged within run-to-run noise.

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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

close position with some note

1 participant