From 9f64c9944aee684ff1df3a598eb2af2b16916af5 Mon Sep 17 00:00:00 2001 From: agentson Date: Fri, 27 Feb 2026 00:47:09 +0900 Subject: [PATCH] fix: correct short-side tie-break semantics in triple barrier --- src/analysis/triple_barrier.py | 8 +++---- tests/test_triple_barrier.py | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/analysis/triple_barrier.py b/src/analysis/triple_barrier.py index cf7a961..f609496 100644 --- a/src/analysis/triple_barrier.py +++ b/src/analysis/triple_barrier.py @@ -80,11 +80,11 @@ def label_with_triple_barrier( if up_touch and down_touch: if spec.tie_break == "stop_first": - touched = "stop_loss" if side == 1 else "take_profit" - label = -1 if side == 1 else 1 + touched = "stop_loss" + label = -1 else: - touched = "take_profit" if side == 1 else "stop_loss" - label = 1 if side == 1 else -1 + touched = "take_profit" + label = 1 elif up_touch: touched = "take_profit" if side == 1 else "stop_loss" label = 1 if side == 1 else -1 diff --git a/tests/test_triple_barrier.py b/tests/test_triple_barrier.py index 73efc47..1fff8e3 100644 --- a/tests/test_triple_barrier.py +++ b/tests/test_triple_barrier.py @@ -89,3 +89,43 @@ def test_short_side_inverts_barrier_semantics() -> None: ) assert out.label == 1 assert out.touched == "take_profit" + + +def test_short_tie_break_modes() -> None: + highs = [100, 101.1] + lows = [100, 97.9] + closes = [100, 100] + + stop_first = TripleBarrierSpec( + take_profit_pct=0.02, + stop_loss_pct=0.01, + max_holding_bars=1, + tie_break="stop_first", + ) + out_stop = label_with_triple_barrier( + highs=highs, + lows=lows, + closes=closes, + entry_index=0, + side=-1, + spec=stop_first, + ) + assert out_stop.label == -1 + assert out_stop.touched == "stop_loss" + + take_first = TripleBarrierSpec( + take_profit_pct=0.02, + stop_loss_pct=0.01, + max_holding_bars=1, + tie_break="take_first", + ) + out_take = label_with_triple_barrier( + highs=highs, + lows=lows, + closes=closes, + entry_index=0, + side=-1, + spec=take_first, + ) + assert out_take.label == 1 + assert out_take.touched == "take_profit"