@@ -698,12 +698,15 @@ def __set_contingent(self, type, price):
698
698
699
699
700
700
class _Broker :
701
- def __init__ (self , * , data , cash , commission , margin ,
702
- trade_on_close , hedging , exclusive_orders , index ):
701
+ def __init__ (self , * , data , cash , commission , margin , trade_on_close ,
702
+ hedging , exclusive_orders , use_fixed_commission , index ):
703
703
assert 0 < cash , f"cash should be >0, is { cash } "
704
- assert - .1 <= commission < .1 , \
705
- ("commission should be between -10% "
706
- f"(e.g. market-maker's rebates) and 10% (fees), is { commission } " )
704
+ if use_fixed_commission :
705
+ assert commission >= 0 , f"fixed commission should be be >0, is { commission } "
706
+ else :
707
+ assert - .1 <= commission < .1 , \
708
+ ("commission should be between -10% "
709
+ f"(e.g. market-maker's rebates) and 10% (fees), is { commission } " )
707
710
assert 0 < margin <= 1 , f"margin should be between 0 and 1, is { margin } "
708
711
self ._data : _Data = data
709
712
self ._cash = cash
@@ -712,6 +715,7 @@ def __init__(self, *, data, cash, commission, margin,
712
715
self ._trade_on_close = trade_on_close
713
716
self ._hedging = hedging
714
717
self ._exclusive_orders = exclusive_orders
718
+ self ._use_fixed_commission = use_fixed_commission
715
719
716
720
self ._equity = np .tile (np .nan , len (index ))
717
721
self .orders : List [Order ] = []
@@ -779,11 +783,12 @@ def last_price(self) -> float:
779
783
return self ._data .Close [- 1 ]
780
784
781
785
def _adjusted_price (self , size = None , price = None ) -> float :
782
- """
783
- Long/short `price`, adjusted for commisions.
784
- In long positions, the adjusted price is a fraction higher, and vice versa.
785
- """
786
- return (price or self .last_price ) * (1 + copysign (self ._commission , size ))
786
+ """Long/shortprice, adjusted for commissions (fixed or percentage). In long positions,
787
+ the adjusted price is a fraction higher, and vice versa."""
788
+ if self ._use_fixed_commission :
789
+ return ((price or self .last_price ) + copysign (self ._commission , size ))
790
+ else :
791
+ return ((price or self .last_price ) * (1 + copysign (self ._commission , size )))
787
792
788
793
@property
789
794
def equity (self ) -> float :
@@ -1030,7 +1035,8 @@ def __init__(self,
1030
1035
margin : float = 1. ,
1031
1036
trade_on_close = False ,
1032
1037
hedging = False ,
1033
- exclusive_orders = False
1038
+ exclusive_orders = False ,
1039
+ use_fixed_commission = False
1034
1040
):
1035
1041
"""
1036
1042
Initialize a backtest. Requires data and a strategy to test.
@@ -1052,11 +1058,13 @@ def __init__(self,
1052
1058
1053
1059
`cash` is the initial cash to start with.
1054
1060
1055
- `commission` is the commission ratio. E.g. if your broker's commission
1056
- is 1% of trade value, set commission to `0.01`. Note, if you wish to
1057
- account for bid-ask spread, you can approximate doing so by increasing
1058
- the commission, e.g. set it to `0.0002` for commission-less forex
1059
- trading where the average spread is roughly 0.2‰ of asking price.
1061
+ `commission` is the commission ratio, a percentage by default.E.g.
1062
+ if your broker's commission is 1% of trade value, set commission to `0.01`.
1063
+ Note, if you wish to account for bid-ask spread, you can approximate
1064
+ doing so by increasing the commission, e.g. set it to `0.0002` for
1065
+ commission-less forex trading where the average spread is roughly
1066
+ 0.2‰ of asking price. If you want to use a fixed commission in dollar
1067
+ value, set the `use_fixed_commission` parameter as `True`.
1060
1068
1061
1069
`margin` is the required margin (ratio) of a leveraged account.
1062
1070
No difference is made between initial and maintenance margins.
@@ -1075,6 +1083,9 @@ def __init__(self,
1075
1083
trade/position, making at most a single trade (long or short) in effect
1076
1084
at each time.
1077
1085
1086
+ If `use_fixed_commission` is `True`, the commission won't be considered
1087
+ as a percentage, but as a flat fee on every order.
1088
+
1078
1089
[FIFO]: https://www.investopedia.com/terms/n/nfa-compliance-rule-2-43b.asp
1079
1090
"""
1080
1091
@@ -1083,8 +1094,9 @@ def __init__(self,
1083
1094
if not isinstance (data , pd .DataFrame ):
1084
1095
raise TypeError ("`data` must be a pandas.DataFrame with columns" )
1085
1096
if not isinstance (commission , Number ):
1086
- raise TypeError ('`commission` must be a float value, percent of '
1087
- 'entry order price' )
1097
+ raise TypeError ('`commission` must be a number, percent of '
1098
+ 'entry order price. Set `use_fixed_commission` to `True` '
1099
+ 'if you want to use a flat fee instead' )
1088
1100
1089
1101
data = data .copy (deep = False )
1090
1102
@@ -1128,8 +1140,8 @@ def __init__(self,
1128
1140
self ._data : pd .DataFrame = data
1129
1141
self ._broker = partial (
1130
1142
_Broker , cash = cash , commission = commission , margin = margin ,
1131
- trade_on_close = trade_on_close , hedging = hedging ,
1132
- exclusive_orders = exclusive_orders , index = data .index ,
1143
+ trade_on_close = trade_on_close , hedging = hedging , exclusive_orders = exclusive_orders ,
1144
+ use_fixed_commission = use_fixed_commission , index = data .index
1133
1145
)
1134
1146
self ._strategy = strategy
1135
1147
self ._results : Optional [pd .Series ] = None
0 commit comments