1212import warnings
1313from abc import ABCMeta , abstractmethod
1414from copy import copy
15+ from difflib import get_close_matches
1516from functools import cached_property , lru_cache , partial
1617from itertools import chain , product , repeat
1718from math import copysign
@@ -64,10 +65,12 @@ def __str__(self):
6465 def _check_params (self , params ):
6566 for k , v in params .items ():
6667 if not hasattr (self , k ):
68+ suggestions = get_close_matches (k , (attr for attr in dir (self ) if not attr .startswith ('_' )))
69+ hint = f" Did you mean: { ', ' .join (suggestions )} ?" if suggestions else ""
6770 raise AttributeError (
68- f"Strategy '{ self .__class__ .__name__ } ' is missing parameter '{ k } '."
71+ f"Strategy '{ self .__class__ .__name__ } ' is missing parameter '{ k } '. "
6972 "Strategy class should define parameters as class variables before they "
70- "can be optimized or run with." )
73+ "can be optimized or run with." + hint )
7174 setattr (self , k , v )
7275 return params
7376
@@ -309,7 +312,7 @@ def position(self) -> 'Position':
309312 @property
310313 def orders (self ) -> 'Tuple[Order, ...]' :
311314 """List of orders (see `Order`) waiting for execution."""
312- return _Orders (self ._broker .orders )
315+ return tuple (self ._broker .orders )
313316
314317 @property
315318 def trades (self ) -> 'Tuple[Trade, ...]' :
@@ -322,27 +325,6 @@ def closed_trades(self) -> 'Tuple[Trade, ...]':
322325 return tuple (self ._broker .closed_trades )
323326
324327
325- class _Orders (tuple ):
326- """
327- TODO: remove this class. Only for deprecation.
328- """
329- def cancel (self ):
330- """Cancel all non-contingent (i.e. SL/TP) orders."""
331- for order in self :
332- if not order .is_contingent :
333- order .cancel ()
334-
335- def __getattr__ (self , item ):
336- # TODO: Warn on deprecations from the previous version. Remove in the next.
337- removed_attrs = ('entry' , 'set_entry' , 'is_long' , 'is_short' ,
338- 'sl' , 'tp' , 'set_sl' , 'set_tp' )
339- if item in removed_attrs :
340- raise AttributeError (f'Strategy.orders.{ "/." .join (removed_attrs )} were removed in'
341- 'Backtesting 0.2.0. '
342- 'Use `Order` API instead. See docs.' )
343- raise AttributeError (f"'tuple' object has no attribute { item !r} " )
344-
345-
346328class Position :
347329 """
348330 Currently held asset position, available as
@@ -998,8 +980,9 @@ def _process_orders(self):
998980 # Not enough cash/margin even for a single unit
999981 if not size :
1000982 warnings .warn (
1001- f'time={ self ._i } : Broker canceled the relative-sized '
1002- f'order due to insufficient margin.' , category = UserWarning )
983+ f'time={ self ._i } : Broker canceled the relative-sized order due to insufficient margin '
984+ f'(equity={ self .equity :.2f} , margin_available={ self .margin_available :.2f} ).' ,
985+ category = UserWarning )
1003986 # XXX: The order is canceled by the broker?
1004987 self .orders .remove (order )
1005988 continue
@@ -1032,6 +1015,10 @@ def _process_orders(self):
10321015 # If we don't have enough liquidity to cover for the order, the broker CANCELS it
10331016 if abs (need_size ) * adjusted_price_plus_commission > \
10341017 self .margin_available * self ._leverage :
1018+ warnings .warn (
1019+ f'time={ self ._i } : Broker canceled the order due to insufficient margin '
1020+ f'(equity={ self .equity :.2f} , margin_available={ self .margin_available :.2f} ).' ,
1021+ category = UserWarning )
10351022 self .orders .remove (order )
10361023 continue
10371024
@@ -1174,7 +1161,7 @@ class Backtest:
11741161
11751162 `cash` is the initial cash to start with.
11761163
1177- `spread` is the the constant bid-ask spread rate (relative to the price).
1164+ `spread` is the constant bid-ask spread rate (relative to the price).
11781165 E.g. set it to `0.0002` for commission-less forex
11791166 trading where the average spread is roughly 0.2β° of the asking price.
11801167
@@ -1204,7 +1191,7 @@ class Backtest:
12041191
12051192 `margin` is the required margin (ratio) of a leveraged account.
12061193 No difference is made between initial and maintenance margins.
1207- To run the backtest using e.g. 50:1 leverge that your broker allows,
1194+ To run the backtest using e.g. 50:1 leverage that your broker allows,
12081195 set margin to `0.02` (1 / leverage).
12091196
12101197 If `trade_on_close` is `True`, market orders will be filled
0 commit comments