@@ -199,7 +199,8 @@ def buy(self, *,
199199 limit : Optional [float ] = None ,
200200 stop : Optional [float ] = None ,
201201 sl : Optional [float ] = None ,
202- tp : Optional [float ] = None ):
202+ tp : Optional [float ] = None ,
203+ tag : object = None ):
203204 """
204205 Place a new long order. For explanation of parameters, see `Order` and its properties.
205206
@@ -209,14 +210,15 @@ def buy(self, *,
209210 """
210211 assert 0 < size < 1 or round (size ) == size , \
211212 "size must be a positive fraction of equity, or a positive whole number of units"
212- return self ._broker .new_order (size , limit , stop , sl , tp )
213+ return self ._broker .new_order (size , limit , stop , sl , tp , tag )
213214
214215 def sell (self , * ,
215216 size : float = _FULL_EQUITY ,
216217 limit : Optional [float ] = None ,
217218 stop : Optional [float ] = None ,
218219 sl : Optional [float ] = None ,
219- tp : Optional [float ] = None ):
220+ tp : Optional [float ] = None ,
221+ tag : object = None ):
220222 """
221223 Place a new short order. For explanation of parameters, see `Order` and its properties.
222224
@@ -228,7 +230,7 @@ def sell(self, *,
228230 """
229231 assert 0 < size < 1 or round (size ) == size , \
230232 "size must be a positive fraction of equity, or a positive whole number of units"
231- return self ._broker .new_order (- size , limit , stop , sl , tp )
233+ return self ._broker .new_order (- size , limit , stop , sl , tp , tag )
232234
233235 @property
234236 def equity (self ) -> float :
@@ -386,7 +388,8 @@ def __init__(self, broker: '_Broker',
386388 stop_price : Optional [float ] = None ,
387389 sl_price : Optional [float ] = None ,
388390 tp_price : Optional [float ] = None ,
389- parent_trade : Optional ['Trade' ] = None ):
391+ parent_trade : Optional ['Trade' ] = None ,
392+ tag : object = None ):
390393 self .__broker = broker
391394 assert size != 0
392395 self .__size = size
@@ -395,6 +398,7 @@ def __init__(self, broker: '_Broker',
395398 self .__sl_price = sl_price
396399 self .__tp_price = tp_price
397400 self .__parent_trade = parent_trade
401+ self .__tag = tag
398402
399403 def _replace (self , ** kwargs ):
400404 for k , v in kwargs .items ():
@@ -410,6 +414,7 @@ def __repr__(self):
410414 ('sl' , self .__sl_price ),
411415 ('tp' , self .__tp_price ),
412416 ('contingent' , self .is_contingent ),
417+ ('tag' , self .__tag ),
413418 ) if value is not None ))
414419
415420 def cancel (self ):
@@ -481,6 +486,14 @@ def tp(self) -> Optional[float]:
481486 def parent_trade (self ):
482487 return self .__parent_trade
483488
489+ @property
490+ def tag (self ):
491+ """
492+ Arbitrary value (such as a string) which, if set, enables tracking
493+ of this order and the associated `Trade` (see `Trade.tag`).
494+ """
495+ return self .__tag
496+
484497 __pdoc__ ['Order.parent_trade' ] = False
485498
486499 # Extra properties
@@ -515,7 +528,7 @@ class Trade:
515528 When an `Order` is filled, it results in an active `Trade`.
516529 Find active trades in `Strategy.trades` and closed, settled trades in `Strategy.closed_trades`.
517530 """
518- def __init__ (self , broker : '_Broker' , size : int , entry_price : float , entry_bar ):
531+ def __init__ (self , broker : '_Broker' , size : int , entry_price : float , entry_bar , tag ):
519532 self .__broker = broker
520533 self .__size = size
521534 self .__entry_price = entry_price
@@ -524,10 +537,12 @@ def __init__(self, broker: '_Broker', size: int, entry_price: float, entry_bar):
524537 self .__exit_bar : Optional [int ] = None
525538 self .__sl_order : Optional [Order ] = None
526539 self .__tp_order : Optional [Order ] = None
540+ self .__tag = tag
527541
528542 def __repr__ (self ):
529543 return f'<Trade size={ self .__size } time={ self .__entry_bar } -{ self .__exit_bar or "" } ' \
530- f'price={ self .__entry_price } -{ self .__exit_price or "" } pl={ self .pl :.0f} >'
544+ f'price={ self .__entry_price } -{ self .__exit_price or "" } pl={ self .pl :.0f} ' \
545+ f'{ " tag=" + str (self .__tag ) if self .__tag is not None else "" } >'
531546
532547 def _replace (self , ** kwargs ):
533548 for k , v in kwargs .items ():
@@ -541,7 +556,7 @@ def close(self, portion: float = 1.):
541556 """Place new `Order` to close `portion` of the trade at next market price."""
542557 assert 0 < portion <= 1 , "portion must be a fraction between 0 and 1"
543558 size = copysign (max (1 , round (abs (self .__size ) * portion )), - self .__size )
544- order = Order (self .__broker , size , parent_trade = self )
559+ order = Order (self .__broker , size , parent_trade = self , tag = self . __tag )
545560 self .__broker .orders .insert (0 , order )
546561
547562 # Fields getters
@@ -574,6 +589,19 @@ def exit_bar(self) -> Optional[int]:
574589 """
575590 return self .__exit_bar
576591
592+ @property
593+ def tag (self ):
594+ """
595+ A tag value inherited from the `Order` that opened
596+ this trade.
597+
598+ This can be used to track trades and apply conditional
599+ logic / subgroup analysis.
600+
601+ See also `Order.tag`.
602+ """
603+ return self .__tag
604+
577605 @property
578606 def _sl_order (self ):
579607 return self .__sl_order
@@ -665,7 +693,7 @@ def __set_contingent(self, type, price):
665693 order .cancel ()
666694 if price :
667695 kwargs = {'stop' : price } if type == 'sl' else {'limit' : price }
668- order = self .__broker .new_order (- self .size , trade = self , ** kwargs )
696+ order = self .__broker .new_order (- self .size , trade = self , tag = self . tag , ** kwargs )
669697 setattr (self , attr , order )
670698
671699
@@ -700,6 +728,7 @@ def new_order(self,
700728 stop : Optional [float ] = None ,
701729 sl : Optional [float ] = None ,
702730 tp : Optional [float ] = None ,
731+ tag : object = None ,
703732 * ,
704733 trade : Optional [Trade ] = None ):
705734 """
@@ -725,7 +754,7 @@ def new_order(self,
725754 "Short orders require: "
726755 f"TP ({ tp } ) < LIMIT ({ limit or stop or adjusted_price } ) < SL ({ sl } )" )
727756
728- order = Order (self , size , limit , stop , sl , tp , trade )
757+ order = Order (self , size , limit , stop , sl , tp , trade , tag )
729758 # Put the new order in the order queue,
730759 # inserting SL/TP/trade-closing orders in-front
731760 if trade :
@@ -905,7 +934,12 @@ def _process_orders(self):
905934
906935 # Open a new trade
907936 if need_size :
908- self ._open_trade (adjusted_price , need_size , order .sl , order .tp , time_index )
937+ self ._open_trade (adjusted_price ,
938+ need_size ,
939+ order .sl ,
940+ order .tp ,
941+ time_index ,
942+ order .tag )
909943
910944 # We need to reprocess the SL/TP orders newly added to the queue.
911945 # This allows e.g. SL hitting in the same bar the order was open.
@@ -964,8 +998,8 @@ def _close_trade(self, trade: Trade, price: float, time_index: int):
964998 self ._cash += trade .pl
965999
9661000 def _open_trade (self , price : float , size : int ,
967- sl : Optional [float ], tp : Optional [float ], time_index : int ):
968- trade = Trade (self , size , price , time_index )
1001+ sl : Optional [float ], tp : Optional [float ], time_index : int , tag ):
1002+ trade = Trade (self , size , price , time_index , tag )
9691003 self .trades .append (trade )
9701004 # Create SL/TP (bracket) orders.
9711005 # Make sure SL order is created first so it gets adversarially processed before TP order
0 commit comments