Skip to content

Commit 65b28f4

Browse files
mutt0-dsYonsh Lin
mutt0-ds
authored and
Yonsh Lin
committed
added fixed commision to backtesting as suggested in kernc#623
1 parent 1c12381 commit 65b28f4

File tree

1 file changed

+32
-20
lines changed

1 file changed

+32
-20
lines changed

backtesting/backtesting.py

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -698,12 +698,15 @@ def __set_contingent(self, type, price):
698698

699699

700700
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):
703703
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}")
707710
assert 0 < margin <= 1, f"margin should be between 0 and 1, is {margin}"
708711
self._data: _Data = data
709712
self._cash = cash
@@ -712,6 +715,7 @@ def __init__(self, *, data, cash, commission, margin,
712715
self._trade_on_close = trade_on_close
713716
self._hedging = hedging
714717
self._exclusive_orders = exclusive_orders
718+
self._use_fixed_commission = use_fixed_commission
715719

716720
self._equity = np.tile(np.nan, len(index))
717721
self.orders: List[Order] = []
@@ -779,11 +783,12 @@ def last_price(self) -> float:
779783
return self._data.Close[-1]
780784

781785
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)))
787792

788793
@property
789794
def equity(self) -> float:
@@ -1030,7 +1035,8 @@ def __init__(self,
10301035
margin: float = 1.,
10311036
trade_on_close=False,
10321037
hedging=False,
1033-
exclusive_orders=False
1038+
exclusive_orders=False,
1039+
use_fixed_commission=False
10341040
):
10351041
"""
10361042
Initialize a backtest. Requires data and a strategy to test.
@@ -1052,11 +1058,13 @@ def __init__(self,
10521058
10531059
`cash` is the initial cash to start with.
10541060
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`.
10601068
10611069
`margin` is the required margin (ratio) of a leveraged account.
10621070
No difference is made between initial and maintenance margins.
@@ -1075,6 +1083,9 @@ def __init__(self,
10751083
trade/position, making at most a single trade (long or short) in effect
10761084
at each time.
10771085
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+
10781089
[FIFO]: https://www.investopedia.com/terms/n/nfa-compliance-rule-2-43b.asp
10791090
"""
10801091

@@ -1083,8 +1094,9 @@ def __init__(self,
10831094
if not isinstance(data, pd.DataFrame):
10841095
raise TypeError("`data` must be a pandas.DataFrame with columns")
10851096
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')
10881100

10891101
data = data.copy(deep=False)
10901102

@@ -1128,8 +1140,8 @@ def __init__(self,
11281140
self._data: pd.DataFrame = data
11291141
self._broker = partial(
11301142
_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
11331145
)
11341146
self._strategy = strategy
11351147
self._results: Optional[pd.Series] = None

0 commit comments

Comments
 (0)