Skip to content

Commit 5e1accc

Browse files
committed
REF: Use Py3.6 f-strings
Fixes: #161 Closes: #166 Thanks @Alex-CodeLab Squashed commit of the following: commit 363641d Merge: aad278c 773a85f Author: Kernc <[email protected]> Date: Thu Nov 12 21:35:41 2020 +0100 Merge branch 'master' commit aad278c Author: Kernc <[email protected]> Date: Thu Nov 12 20:42:30 2020 +0100 add some more f-strings where useful commit 4fd6ac5 Author: Kernc <[email protected]> Date: Thu Nov 12 20:24:08 2020 +0100 address comments commit 5ab6cb3 Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 19:40:57 2020 +0100 Update _plotting.py commit 00bb39e Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 19:40:02 2020 +0100 Update backtesting.py commit 6c633bc Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 19:35:55 2020 +0100 Update backtesting.py commit 732068a Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 19:26:55 2020 +0100 Update backtesting.py commit 21fed39 Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 19:19:25 2020 +0100 Update backtesting.py commit e0b929e Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 18:49:30 2020 +0100 Update backtesting.py commit 696669a Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 18:02:10 2020 +0100 Update backtesting.py commit e38b25f Author: Alex ⚡ <[email protected]> Date: Sat Oct 31 17:42:43 2020 +0100 Update backtesting.py commit 6bd7492 Author: alex <[email protected]> Date: Sat Oct 31 16:29:38 2020 +0100 Refactor f-string formating.
1 parent 773a85f commit 5e1accc

File tree

3 files changed

+48
-48
lines changed

3 files changed

+48
-48
lines changed

backtesting/_plotting.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ def _maybe_resample_data(resample_rule, df, indicators, equity_data, trades):
101101
FREQS = ('1T', '5T', '10T', '15T', '30T', '1H', '2H', '4H', '8H', '1D', '1W', '1M')
102102
freq = next((f for f in FREQS[from_index:]
103103
if len(df.resample(f)) <= _MAX_CANDLES), FREQS[-1])
104-
warnings.warn("Data contains too many candlesticks to plot; downsampling to {!r}. "
105-
"See `Backtest.plot(resample=...)`".format(freq))
104+
warnings.warn(f"Data contains too many candlesticks to plot; downsampling to {freq!r}. "
105+
"See `Backtest.plot(resample=...)`")
106106

107107
from .lib import OHLCV_AGG, TRADES_AGG, _EQUITY_AGG
108108
df = df.resample(freq, label='right').agg(OHLCV_AGG).dropna()
@@ -354,7 +354,7 @@ def _plot_equity_section():
354354
dd_timedelta_label = df['datetime'].iloc[int(round(dd_end))] - df['datetime'].iloc[dd_start]
355355
fig.line([dd_start, dd_end], equity.iloc[dd_start],
356356
line_color='red', line_width=2,
357-
legend_label='Max Dd Dur. ({})'.format(dd_timedelta_label)
357+
legend_label=f'Max Dd Dur. ({dd_timedelta_label})'
358358
.replace(' 00:00:00', '')
359359
.replace('(0 days ', '('))
360360

@@ -424,8 +424,8 @@ def _plot_superimposed_ohlc():
424424
millisecond='S').get(time_resolution))
425425
if not resample_rule:
426426
warnings.warn(
427-
"'Can't superimpose OHLC data with rule '{}' (index datetime resolution: '{}'). "
428-
"Skipping.".format(resample_rule, time_resolution),
427+
f"'Can't superimpose OHLC data with rule '{resample_rule}'"
428+
f"(index datetime resolution: '{time_resolution}'). Skipping.",
429429
stacklevel=4)
430430
return
431431

@@ -469,7 +469,7 @@ def _plot_ohlc_trades():
469469
trade_source.add(trades[['EntryPrice', 'ExitPrice']].values.tolist(), 'position_lines_ys')
470470
fig_ohlc.multi_line(xs='position_lines_xs', ys='position_lines_ys',
471471
source=trade_source, line_color=trades_cmap,
472-
legend_label='Trades ({})'.format(len(trades)),
472+
legend_label=f'Trades ({len(trades)})',
473473
line_width=8, line_alpha=1, line_dash='dotted')
474474

475475
def _plot_indicators():
@@ -478,7 +478,7 @@ def _plot_indicators():
478478
def _too_many_dims(value):
479479
assert value.ndim >= 2
480480
if value.ndim > 2:
481-
warnings.warn("Can't plot indicators with >2D ('{}')".format(value.name),
481+
warnings.warn(f"Can't plot indicators with >2D ('{value.name}')",
482482
stacklevel=5)
483483
return True
484484
return False
@@ -517,11 +517,11 @@ def __eq__(self, other):
517517
legend_label = LegendStr(value.name)
518518
for j, arr in enumerate(value, 1):
519519
color = next(colors)
520-
source_name = '{}_{}_{}'.format(legend_label, i, j)
520+
source_name = f'{legend_label}_{i}_{j}'
521521
if arr.dtype == bool:
522522
arr = arr.astype(int)
523523
source.add(arr, source_name)
524-
tooltips.append('@{{{}}}{{0,0.0[0000]}}'.format(source_name))
524+
tooltips.append(f'@{{{source_name}}}{{0,0.0[0000]}}')
525525
if is_overlay:
526526
ohlc_extreme_values[source_name] = arr
527527
if is_scatter:

backtesting/_util.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ def __getattr__(self, item):
111111
try:
112112
return self.__get_array(item)
113113
except KeyError:
114-
raise AttributeError("Column '{}' not in data".format(item)) from None
114+
raise AttributeError(f"Column '{item}' not in data") from None
115115

116116
def _set_length(self, i):
117117
self.__i = i
@@ -125,9 +125,9 @@ def _update(self):
125125

126126
def __repr__(self):
127127
i = min(self.__i, len(self.__df) - 1)
128-
return '<Data i={} ({}) {}>'.format(i, self.__arrays['__index'][i],
129-
', '.join('{}={}'.format(k, v)
130-
for k, v in self.__df.iloc[i].items()))
128+
index = self.__arrays['__index'][i]
129+
items = ', '.join(f'{k}={v}' for k, v in self.__df.iloc[i].items())
130+
return f'<Data i={i} ({index}) {items}>'
131131

132132
def __len__(self):
133133
return self.__i

backtesting/backtesting.py

+35-35
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,19 @@ def __repr__(self):
5757
return '<Strategy ' + str(self) + '>'
5858

5959
def __str__(self):
60-
params = ','.join('{}={}'.format(*p) for p in zip(self._params.keys(),
61-
map(_as_str, self._params.values())))
60+
params = ','.join(f'{i[0]}={i[1]}' for i in zip(self._params.keys(),
61+
map(_as_str, self._params.values())))
6262
if params:
6363
params = '(' + params + ')'
64-
return '{}{}'.format(self.__class__.__name__, params)
64+
return f'{self.__class__.__name__}{params}'
6565

6666
def _check_params(self, params):
6767
for k, v in params.items():
6868
if not hasattr(self, k):
6969
raise AttributeError(
70-
"Strategy '{}' is missing parameter '{}'. Strategy class "
71-
"should define parameters as class variables before they "
72-
"can be optimized or run with.".format(self.__class__.__name__, k))
70+
f"Strategy '{self.__class__.__name__}' is missing parameter '{k}'."
71+
"Strategy class should define parameters as class variables before they "
72+
"can be optimized or run with.")
7373
setattr(self, k, v)
7474
return params
7575

@@ -116,15 +116,15 @@ def init():
116116
if name is None:
117117
params = ','.join(filter(None, map(_as_str, chain(args, kwargs.values()))))
118118
func_name = _as_str(func)
119-
name = ('{}({})' if params else '{}').format(func_name, params)
119+
name = (f'{func_name}({params})' if params else f'{func_name}')
120120
else:
121121
name = name.format(*map(_as_str, args),
122122
**dict(zip(kwargs.keys(), map(_as_str, kwargs.values()))))
123123

124124
try:
125125
value = func(*args, **kwargs)
126126
except Exception as e:
127-
raise RuntimeError('Indicator "{}" errored with exception: {}'.format(name, e))
127+
raise RuntimeError(f'Indicator "{name}" errored with exception: {e}')
128128

129129
if isinstance(value, pd.DataFrame):
130130
value = value.values.T
@@ -140,8 +140,8 @@ def init():
140140
if not is_arraylike or not 1 <= value.ndim <= 2 or value.shape[-1] != len(self._data.Close):
141141
raise ValueError(
142142
'Indicators must return (optionally a tuple of) numpy.arrays of same '
143-
'length as `data` (data shape: {}; indicator "{}" shape: {}, returned value: {})'
144-
.format(self._data.Close.shape, name, getattr(value, 'shape', ''), value))
143+
f'length as `data` (data shape: {self._data.Close.shape}; indicator "{name}"'
144+
f'shape: {getattr(value, "shape" , "")}, returned value: {value})')
145145

146146
if plot and overlay is None and np.issubdtype(value.dtype, np.number):
147147
x = value / self._data.Close
@@ -288,10 +288,10 @@ def __getattr__(self, item):
288288
removed_attrs = ('entry', 'set_entry', 'is_long', 'is_short',
289289
'sl', 'tp', 'set_sl', 'set_tp')
290290
if item in removed_attrs:
291-
raise AttributeError('Strategy.orders.{} were removed in Backtesting 0.2.0. '
292-
'Use `Order` API instead. See docs.'
293-
.format('/.'.join(removed_attrs)))
294-
raise AttributeError("'tuple' object has no attribute {!r}".format(item))
291+
raise AttributeError(f'Strategy.orders.{"/.".join(removed_attrs)} were removed in'
292+
'Backtesting 0.2.0. '
293+
'Use `Order` API instead. See docs.')
294+
raise AttributeError(f"'tuple' object has no attribute {item!r}")
295295

296296

297297
class Position:
@@ -346,7 +346,7 @@ def close(self, portion: float = 1.):
346346
trade.close(portion)
347347

348348
def __repr__(self):
349-
return '<Position: {} ({} trades)>'.format(self.size, len(self.__broker.trades))
349+
return f'<Position: {self.size} ({len(self.__broker.trades)} trades)>'
350350

351351

352352
class _OutOfMoneyError(Exception):
@@ -386,11 +386,11 @@ def __init__(self, broker: '_Broker',
386386

387387
def _replace(self, **kwargs):
388388
for k, v in kwargs.items():
389-
setattr(self, '_{}__{}'.format(self.__class__.__qualname__, k), v)
389+
setattr(self, f'_{self.__class__.__qualname__}__{k}', v)
390390
return self
391391

392392
def __repr__(self):
393-
return '<Order {}>'.format(', '.join('{}={}'.format(param, round(value, 5))
393+
return '<Order {}>'.format(', '.join(f'{param}={round(value, 5)}'
394394
for param, value in (
395395
('size', self.__size),
396396
('limit', self.__limit_price),
@@ -513,13 +513,12 @@ def __init__(self, broker: '_Broker', size: int, entry_price: float, entry_bar):
513513
self.__tp_order: Optional[Order] = None
514514

515515
def __repr__(self):
516-
return '<Trade size={} time={}-{} price={}-{} pl={:.0f}>'.format(
517-
self.__size, self.__entry_bar, self.__exit_bar or '',
518-
self.__entry_price, self.__exit_price or '', self.pl)
516+
return f'<Trade size={self.__size} time={self.__entry_bar}-{self.__exit_bar or ""} ' \
517+
f'price={self.__entry_price}-{self.__exit_price or ""} pl={self.pl:.0f}>'
519518

520519
def _replace(self, **kwargs):
521520
for k, v in kwargs.items():
522-
setattr(self, '_{}__{}'.format(self.__class__.__qualname__, k), v)
521+
setattr(self, f'_{self.__class__.__qualname__}__{k}', v)
523522
return self
524523

525524
def _copy(self, **kwargs):
@@ -647,8 +646,8 @@ def tp(self, price: float):
647646
def __set_contingent(self, type, price):
648647
assert type in ('sl', 'tp')
649648
assert price is None or 0 < price < np.inf
650-
attr = '_{}__{}_order'.format(self.__class__.__qualname__, type)
651-
order: Order = getattr(self, attr)
649+
attr = f'_{self.__class__.__qualname__}__{type}_order'
650+
order: Order = getattr(self, attr) # type: Order
652651
if order:
653652
order.cancel()
654653
if price:
@@ -660,9 +659,9 @@ def __set_contingent(self, type, price):
660659
class _Broker:
661660
def __init__(self, *, data, cash, commission, margin,
662661
trade_on_close, hedging, exclusive_orders, index):
663-
assert 0 < cash, "cash shosuld be >0, is {}".format(cash)
664-
assert 0 <= commission < .1, "commission should be between 0-10%, is {}".format(commission)
665-
assert 0 < margin <= 1, "margin should be between 0 and 1, is {}".format(margin)
662+
assert 0 < cash, f"cash should be >0, is {cash}"
663+
assert 0 <= commission < .1, f"commission should be between 0-10%, is {commission}"
664+
assert 0 < margin <= 1, f"margin should be between 0 and 1, is {margin}"
666665
self._data: _Data = data
667666
self._cash = cash
668667
self._commission = commission
@@ -678,8 +677,7 @@ def __init__(self, *, data, cash, commission, margin,
678677
self.closed_trades: List[Trade] = []
679678

680679
def __repr__(self):
681-
return '<Broker: {:.0f}{:+.1f} ({} trades)>'.format(
682-
self._cash, self.position.pl, len(self.trades))
680+
return f'<Broker: {self._cash:.0f}{self.position.pl:+.1f} ({len(self.trades)} trades)>'
683681

684682
def new_order(self,
685683
size: float,
@@ -703,12 +701,14 @@ def new_order(self,
703701

704702
if is_long:
705703
if not (sl or -np.inf) < (limit or stop or adjusted_price) < (tp or np.inf):
706-
raise ValueError("Long orders require: SL ({}) < LIMIT ({}) < TP ({})".format(
707-
sl, limit or stop or adjusted_price, tp))
704+
raise ValueError(
705+
"Long orders require: "
706+
f"SL ({sl}) < LIMIT ({limit or stop or adjusted_price}) < TP ({tp})")
708707
else:
709708
if not (tp or -np.inf) < (limit or stop or adjusted_price) < (sl or np.inf):
710-
raise ValueError("Short orders require: TP ({}) < LIMIT ({}) < SL ({})".format(
711-
tp, limit or stop or adjusted_price, sl))
709+
raise ValueError(
710+
"Short orders require: "
711+
f"TP ({tp}) < LIMIT ({limit or stop or adjusted_price}) < SL ({sl})")
712712

713713
order = Order(self, size, limit, stop, sl, tp, trade)
714714
# Put the new order in the order queue,
@@ -1243,8 +1243,8 @@ def _tuple(x):
12431243

12441244
for k, v in kwargs.items():
12451245
if len(_tuple(v)) == 0:
1246-
raise ValueError("Optimization variable '{0}' is passed no "
1247-
"optimization values: {0}={1}".format(k, v))
1246+
raise ValueError(f"Optimization variable '{k}' is passed no "
1247+
f"optimization values: {k}={v}")
12481248

12491249
class AttrDict(dict):
12501250
def __getattr__(self, item):
@@ -1259,7 +1259,7 @@ def __getattr__(self, item):
12591259
raise ValueError('No admissible parameter combinations to test')
12601260

12611261
if len(param_combos) > 300:
1262-
warnings.warn('Searching for best of {} configurations.'.format(len(param_combos)),
1262+
warnings.warn(f'Searching for best of {len(param_combos)} configurations.',
12631263
stacklevel=2)
12641264

12651265
heatmap = pd.Series(np.nan,

0 commit comments

Comments
 (0)