@@ -356,17 +356,14 @@ class Order:
356
356
Place new orders through `Strategy.buy()` and `Strategy.sell()`.
357
357
Query existing orders through `Strategy.orders`.
358
358
359
- When an order is executed or [filled], it normally results in a `Trade`, except when an
360
- existing opposite-facing trade can be sufficiently
361
- reduced or closed in an [NFA compliant FIFO] manner.
359
+ When an order is executed or [filled], it results in a `Trade`.
362
360
363
361
If you wish to modify aspects of a placed but not yet filled order,
364
362
cancel it and place a new one instead.
365
363
366
364
All placed orders are [Good 'Til Canceled].
367
365
368
366
[filled]: https://www.investopedia.com/terms/f/fill.asp
369
- [NFA compliant FIFO]: https://www.investopedia.com/terms/n/nfa-compliance-rule-2-43b.asp
370
367
[Good 'Til Canceled]: https://www.investopedia.com/terms/g/gtc.asp
371
368
"""
372
369
def __init__ (self , broker : '_Broker' ,
@@ -646,7 +643,7 @@ def __set_contingent(self, type, price):
646
643
647
644
648
645
class _Broker :
649
- def __init__ (self , * , data , cash , commission , margin , trade_on_close , index ):
646
+ def __init__ (self , * , data , cash , commission , margin , trade_on_close , hedging , index ):
650
647
assert 0 < cash , "cash shosuld be >0, is {}" .format (cash )
651
648
assert 0 <= commission < .1 , "commission should be between 0-10%, is {}" .format (commission )
652
649
assert 0 < margin <= 1 , "margin should be between 0 and 1, is {}" .format (margin )
@@ -655,6 +652,7 @@ def __init__(self, *, data, cash, commission, margin, trade_on_close, index):
655
652
self ._commission = commission
656
653
self ._leverage = 1 / margin
657
654
self ._trade_on_close = trade_on_close
655
+ self ._hedging = hedging
658
656
659
657
self ._equity = np .tile (np .nan , len (index ))
660
658
self .orders = [] # type: List[Order]
@@ -825,25 +823,26 @@ def _process_orders(self):
825
823
assert size == round (size )
826
824
need_size = int (size )
827
825
828
- # Fill position by FIFO closing/reducing existing opposite-facing trades.
829
- # Existing trades are closed at unadjusted price, because the adjustment
830
- # was already made when buying.
831
- for trade in list (self .trades ):
832
- if trade .is_long == order .is_long :
833
- continue
834
- assert np .sign (trade .size ) + np .sign (order .size ) == 0
835
-
836
- # Order size greater than this opposite-directed existing trade,
837
- # so it will be closed completely
838
- if abs (need_size ) >= abs (trade .size ):
839
- self ._close_trade (trade , price , time_index )
840
- need_size += trade .size
841
- else :
842
- # The existing trade is larger than the new order,
843
- # so it will only be closed partially
844
- self ._reduce_trade (trade , price , need_size , time_index )
845
- need_size = 0
846
- break
826
+ if not self ._hedging :
827
+ # Fill position by FIFO closing/reducing existing opposite-facing trades.
828
+ # Existing trades are closed at unadjusted price, because the adjustment
829
+ # was already made when buying.
830
+ for trade in list (self .trades ):
831
+ if trade .is_long == order .is_long :
832
+ continue
833
+ assert np .sign (trade .size ) + np .sign (order .size ) == 0
834
+
835
+ # Order size greater than this opposite-directed existing trade,
836
+ # so it will be closed completely
837
+ if abs (need_size ) >= abs (trade .size ):
838
+ self ._close_trade (trade , price , time_index )
839
+ need_size += trade .size
840
+ else :
841
+ # The existing trade is larger than the new order,
842
+ # so it will only be closed partially
843
+ self ._reduce_trade (trade , price , need_size , time_index )
844
+ need_size = 0
845
+ break
847
846
848
847
# If we don't have enough liquidity to cover for the order, cancel it
849
848
if abs (need_size ) * adjusted_price > self .margin_available * self ._leverage :
@@ -918,7 +917,8 @@ def __init__(self,
918
917
cash : float = 10000 ,
919
918
commission : float = .0 ,
920
919
margin : float = 1. ,
921
- trade_on_close = False
920
+ trade_on_close = False ,
921
+ hedging = False ,
922
922
):
923
923
"""
924
924
Initialize a backtest. Requires data and a strategy to test.
@@ -952,6 +952,12 @@ def __init__(self,
952
952
If `trade_on_close` is `True`, market orders will be executed
953
953
with respect to the current bar's closing price instead of the
954
954
next bar's open.
955
+
956
+ If `hedging` is `True`, allow trades in both directions simultaneously.
957
+ If `False`, the opposite-facing orders first close existing trades in
958
+ a [FIFO] manner.
959
+
960
+ [FIFO]: https://www.investopedia.com/terms/n/nfa-compliance-rule-2-43b.asp
955
961
"""
956
962
957
963
if not (isinstance (strategy , type ) and issubclass (strategy , Strategy )):
@@ -999,7 +1005,8 @@ def __init__(self,
999
1005
self ._data = data # type: pd.DataFrame
1000
1006
self ._broker = partial (
1001
1007
_Broker , cash = cash , commission = commission , margin = margin ,
1002
- trade_on_close = trade_on_close , index = data .index
1008
+ trade_on_close = trade_on_close , hedging = hedging ,
1009
+ index = data .index ,
1003
1010
)
1004
1011
self ._strategy = strategy
1005
1012
self ._results = None
0 commit comments