@@ -199,7 +199,8 @@ def buy(self, *,
199
199
limit : Optional [float ] = None ,
200
200
stop : Optional [float ] = None ,
201
201
sl : Optional [float ] = None ,
202
- tp : Optional [float ] = None ):
202
+ tp : Optional [float ] = None ,
203
+ tag : object = None ):
203
204
"""
204
205
Place a new long order. For explanation of parameters, see `Order` and its properties.
205
206
@@ -209,14 +210,15 @@ def buy(self, *,
209
210
"""
210
211
assert 0 < size < 1 or round (size ) == size , \
211
212
"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 )
213
214
214
215
def sell (self , * ,
215
216
size : float = _FULL_EQUITY ,
216
217
limit : Optional [float ] = None ,
217
218
stop : Optional [float ] = None ,
218
219
sl : Optional [float ] = None ,
219
- tp : Optional [float ] = None ):
220
+ tp : Optional [float ] = None ,
221
+ tag : object = None ):
220
222
"""
221
223
Place a new short order. For explanation of parameters, see `Order` and its properties.
222
224
@@ -228,7 +230,7 @@ def sell(self, *,
228
230
"""
229
231
assert 0 < size < 1 or round (size ) == size , \
230
232
"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 )
232
234
233
235
@property
234
236
def equity (self ) -> float :
@@ -386,7 +388,8 @@ def __init__(self, broker: '_Broker',
386
388
stop_price : Optional [float ] = None ,
387
389
sl_price : Optional [float ] = None ,
388
390
tp_price : Optional [float ] = None ,
389
- parent_trade : Optional ['Trade' ] = None ):
391
+ parent_trade : Optional ['Trade' ] = None ,
392
+ tag : object = None ):
390
393
self .__broker = broker
391
394
assert size != 0
392
395
self .__size = size
@@ -395,6 +398,7 @@ def __init__(self, broker: '_Broker',
395
398
self .__sl_price = sl_price
396
399
self .__tp_price = tp_price
397
400
self .__parent_trade = parent_trade
401
+ self .__tag = tag
398
402
399
403
def _replace (self , ** kwargs ):
400
404
for k , v in kwargs .items ():
@@ -410,6 +414,7 @@ def __repr__(self):
410
414
('sl' , self .__sl_price ),
411
415
('tp' , self .__tp_price ),
412
416
('contingent' , self .is_contingent ),
417
+ ('tag' , self .__tag ),
413
418
) if value is not None ))
414
419
415
420
def cancel (self ):
@@ -481,6 +486,14 @@ def tp(self) -> Optional[float]:
481
486
def parent_trade (self ):
482
487
return self .__parent_trade
483
488
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
+
484
497
__pdoc__ ['Order.parent_trade' ] = False
485
498
486
499
# Extra properties
@@ -515,7 +528,7 @@ class Trade:
515
528
When an `Order` is filled, it results in an active `Trade`.
516
529
Find active trades in `Strategy.trades` and closed, settled trades in `Strategy.closed_trades`.
517
530
"""
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 ):
519
532
self .__broker = broker
520
533
self .__size = size
521
534
self .__entry_price = entry_price
@@ -524,10 +537,12 @@ def __init__(self, broker: '_Broker', size: int, entry_price: float, entry_bar):
524
537
self .__exit_bar : Optional [int ] = None
525
538
self .__sl_order : Optional [Order ] = None
526
539
self .__tp_order : Optional [Order ] = None
540
+ self .__tag = tag
527
541
528
542
def __repr__ (self ):
529
543
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 "" } >'
531
546
532
547
def _replace (self , ** kwargs ):
533
548
for k , v in kwargs .items ():
@@ -541,7 +556,7 @@ def close(self, portion: float = 1.):
541
556
"""Place new `Order` to close `portion` of the trade at next market price."""
542
557
assert 0 < portion <= 1 , "portion must be a fraction between 0 and 1"
543
558
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 )
545
560
self .__broker .orders .insert (0 , order )
546
561
547
562
# Fields getters
@@ -574,6 +589,19 @@ def exit_bar(self) -> Optional[int]:
574
589
"""
575
590
return self .__exit_bar
576
591
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
+
577
605
@property
578
606
def _sl_order (self ):
579
607
return self .__sl_order
@@ -665,7 +693,7 @@ def __set_contingent(self, type, price):
665
693
order .cancel ()
666
694
if price :
667
695
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 )
669
697
setattr (self , attr , order )
670
698
671
699
@@ -700,6 +728,7 @@ def new_order(self,
700
728
stop : Optional [float ] = None ,
701
729
sl : Optional [float ] = None ,
702
730
tp : Optional [float ] = None ,
731
+ tag : object = None ,
703
732
* ,
704
733
trade : Optional [Trade ] = None ):
705
734
"""
@@ -725,7 +754,7 @@ def new_order(self,
725
754
"Short orders require: "
726
755
f"TP ({ tp } ) < LIMIT ({ limit or stop or adjusted_price } ) < SL ({ sl } )" )
727
756
728
- order = Order (self , size , limit , stop , sl , tp , trade )
757
+ order = Order (self , size , limit , stop , sl , tp , trade , tag )
729
758
# Put the new order in the order queue,
730
759
# inserting SL/TP/trade-closing orders in-front
731
760
if trade :
@@ -905,7 +934,12 @@ def _process_orders(self):
905
934
906
935
# Open a new trade
907
936
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 )
909
943
910
944
# We need to reprocess the SL/TP orders newly added to the queue.
911
945
# 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):
964
998
self ._cash += trade .pl
965
999
966
1000
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 )
969
1003
self .trades .append (trade )
970
1004
# Create SL/TP (bracket) orders.
971
1005
# Make sure SL order is created first so it gets adversarially processed before TP order
0 commit comments