bbstrader 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of bbstrader might be problematic. Click here for more details.

bbstrader/__ini__.py CHANGED
@@ -4,15 +4,15 @@ Simplified Investment & Trading Toolkit
4
4
 
5
5
  """
6
6
  __author__ = "Bertin Balouki SIMYELI"
7
- __copyright__ = "2023-2024 Bertin Balouki SIMYELI"
7
+ __copyright__ = "2023-2025 Bertin Balouki SIMYELI"
8
8
  __email__ = "bertin@bbstrader.com"
9
9
  __license__ = "MIT"
10
10
  __version__ = '0.2.0'
11
11
 
12
12
 
13
- from bbstrader import btengine
14
- from bbstrader import metatrader
15
- from bbstrader import models
16
- from bbstrader import trading
17
- from bbstrader import tseries
18
- from .config import config_logger
13
+ from bbstrader import btengine # noqa: F401
14
+ from bbstrader import metatrader # noqa: F401
15
+ from bbstrader import models # noqa: F401
16
+ from bbstrader import trading # noqa: F401
17
+ from bbstrader import tseries # noqa: F401
18
+ from .config import config_logger # noqa: F401
@@ -45,10 +45,10 @@ Notes
45
45
 
46
46
  See `bbstrader.btengine.backtest.run_backtest` for more details on the backtesting process and its parameters.
47
47
  """
48
- from bbstrader.btengine.data import *
49
- from bbstrader.btengine.event import *
50
- from bbstrader.btengine.execution import *
51
- from bbstrader.btengine.performance import *
52
- from bbstrader.btengine.backtest import *
53
- from bbstrader.btengine.strategy import *
54
- from bbstrader.btengine.portfolio import *
48
+ from bbstrader.btengine.backtest import * # noqa: F403
49
+ from bbstrader.btengine.data import * # noqa: F403
50
+ from bbstrader.btengine.event import * # noqa: F403
51
+ from bbstrader.btengine.execution import * # noqa: F403
52
+ from bbstrader.btengine.performance import * # noqa: F403
53
+ from bbstrader.btengine.portfolio import * # noqa: F403
54
+ from bbstrader.btengine.strategy import * # noqa: F403
@@ -1,16 +1,14 @@
1
- import pprint
2
1
  import queue
3
2
  import time
4
- import yfinance as yf
5
- from queue import Queue
6
3
  from datetime import datetime
7
- from bbstrader.btengine.data import *
8
- from bbstrader.btengine.execution import *
4
+ from typing import List, Literal, Optional
5
+
6
+ from tabulate import tabulate
7
+
8
+ from bbstrader.btengine.data import DataHandler
9
+ from bbstrader.btengine.execution import ExecutionHandler, SimExecutionHandler
9
10
  from bbstrader.btengine.portfolio import Portfolio
10
- from bbstrader.btengine.event import SignalEvent
11
11
  from bbstrader.btengine.strategy import Strategy
12
- from typing import Literal, Optional, List
13
- from tabulate import tabulate
14
12
 
15
13
  __all__ = [
16
14
  "Backtest",
@@ -18,6 +16,7 @@ __all__ = [
18
16
  "run_backtest"
19
17
  ]
20
18
 
19
+
21
20
  class Backtest(object):
22
21
  """
23
22
  The `Backtest()` object encapsulates the event-handling logic and essentially
@@ -60,6 +59,7 @@ class Backtest(object):
60
59
 
61
60
  class BacktestEngine(Backtest):
62
61
  __doc__ = Backtest.__doc__
62
+
63
63
  def __init__(
64
64
  self,
65
65
  symbol_list: List[str],
@@ -141,7 +141,7 @@ class BacktestEngine(Backtest):
141
141
  while True:
142
142
  i += 1
143
143
  value = self.portfolio.all_holdings[-1]['Total']
144
- if self.data_handler.continue_backtest == True:
144
+ if self.data_handler.continue_backtest is True:
145
145
  # Update the market bars
146
146
  self.data_handler.update_bars()
147
147
  self.strategy.check_pending_orders()
@@ -152,7 +152,8 @@ class BacktestEngine(Backtest):
152
152
  self.strategy.cash = value
153
153
  else:
154
154
  print("\n[======= BACKTEST COMPLETED =======]")
155
- print(f"END DATE: {self.data_handler.get_latest_bar_datetime(self.symbol_list[0])}")
155
+ print(
156
+ f"END DATE: {self.data_handler.get_latest_bar_datetime(self.symbol_list[0])}")
156
157
  print(f"TOTAL BARS: {i} ")
157
158
  print(f"PORFOLIO VALUE: {round(value, 2)}")
158
159
  break
@@ -198,7 +199,8 @@ class BacktestEngine(Backtest):
198
199
  stat2['Orders'] = self.orders
199
200
  stat2['Fills'] = self.fills
200
201
  stats.extend(stat2.items())
201
- tab_stats = tabulate(stats, headers=["Metric", "Value"], tablefmt="outline")
202
+ tab_stats = tabulate(
203
+ stats, headers=["Metric", "Value"], tablefmt="outline")
202
204
  print(tab_stats, "\n")
203
205
  if self.stats_file:
204
206
  with open(self.stats_file, 'a') as f:
@@ -211,16 +213,16 @@ class BacktestEngine(Backtest):
211
213
  print("\n[======= PORTFOLIO SUMMARY =======]")
212
214
  print(
213
215
  tabulate(
214
- self.portfolio.equity_curve.tail(10),
215
- headers="keys",
216
- tablefmt="outline"),
217
- "\n"
218
- )
219
-
216
+ self.portfolio.equity_curve.tail(10),
217
+ headers="keys",
218
+ tablefmt="outline"),
219
+ "\n"
220
+ )
221
+
220
222
  def simulate_trading(self):
221
223
  """
222
224
  Simulates the backtest and outputs portfolio performance.
223
-
225
+
224
226
  Returns:
225
227
  pd.DataFrame: The portfilio values over time (capital, equity, returns etc.)
226
228
  """
@@ -244,9 +246,9 @@ def run_backtest(
244
246
 
245
247
  Args:
246
248
  symbol_list (List[str]): List of symbol strings for the assets to be backtested.
247
-
249
+
248
250
  start_date (datetime): Start date of the backtest.
249
-
251
+
250
252
  data_handler (DataHandler): An instance of the `DataHandler` class, responsible for managing
251
253
  and processing market data. Available options include `CSVDataHandler`,
252
254
  `MT5DataHandler`, and `YFDataHandler`. Ensure that the `DataHandler`
@@ -275,7 +277,7 @@ def run_backtest(
275
277
 
276
278
  **kwargs: Additional parameters passed to the `Backtest` instance, which may include strategy-specific,
277
279
  data handler, portfolio, or execution handler options.
278
-
280
+
279
281
  Returns:
280
282
  pd.DataFrame: The portfolio values over time (capital, equities, returns etc.).
281
283
 
@@ -326,10 +328,12 @@ def run_backtest(
326
328
  return portfolio
327
329
 
328
330
 
329
- class CerebroEngine:...
331
+ class CerebroEngine:
332
+ ...
330
333
 
331
334
 
332
- class ZiplineEngine:...
335
+ class ZiplineEngine:
336
+ ...
333
337
 
334
338
 
335
339
  def run_backtest_with(engine: Literal["bbstrader", "cerebro", "zipline"], **kwargs):
@@ -347,8 +351,8 @@ def run_backtest_with(engine: Literal["bbstrader", "cerebro", "zipline"], **kwar
347
351
  **kwargs
348
352
  )
349
353
  elif engine == "cerebro":
350
- #TODO:
354
+ # TODO:
351
355
  pass
352
356
  elif engine == "zipline":
353
- #TODO:
354
- pass
357
+ # TODO:
358
+ pass
@@ -1,19 +1,19 @@
1
1
  import os.path
2
+ from abc import ABCMeta, abstractmethod
3
+ from datetime import datetime
4
+ from queue import Queue
5
+ from typing import Dict, List
6
+
2
7
  import numpy as np
3
8
  import pandas as pd
4
9
  import yfinance as yf
5
- from pathlib import Path
6
- from typing import List, Dict
7
- from queue import Queue
8
- from abc import ABCMeta, abstractmethod
9
- from bbstrader.metatrader.rates import download_historical_data
10
- from bbstrader.btengine.event import MarketEvent
11
- from bbstrader.config import BBSTRADER_DIR
12
- from datetime import datetime
13
10
  from eodhd import APIClient
14
11
  from financetoolkit import Toolkit
15
12
  from pytz import timezone
16
13
 
14
+ from bbstrader.btengine.event import MarketEvent
15
+ from bbstrader.config import BBSTRADER_DIR
16
+ from bbstrader.metatrader.rates import download_historical_data
17
17
 
18
18
  __all__ = [
19
19
  "DataHandler",
@@ -47,12 +47,15 @@ class DataHandler(metaclass=ABCMeta):
47
47
  @property
48
48
  def symbols(self) -> List[str]:
49
49
  pass
50
+
50
51
  @property
51
52
  def data(self) -> Dict[str, pd.DataFrame]:
52
53
  pass
54
+
53
55
  @property
54
56
  def labels(self) -> List[str]:
55
57
  pass
58
+
56
59
  @property
57
60
  def index(self) -> str | List[str]:
58
61
  pass
@@ -122,16 +125,15 @@ class BaseCSVDataHandler(DataHandler):
122
125
 
123
126
  """
124
127
 
125
- def __init__(self, events: Queue,
126
- symbol_list: List[str],
127
- csv_dir: str,
128
- columns: List[str]=None,
128
+ def __init__(self, events: Queue,
129
+ symbol_list: List[str],
130
+ csv_dir: str,
131
+ columns: List[str] = None,
129
132
  index_col: str | int | List[str] | List[int] = 0):
130
-
131
133
  """
132
134
  Initialises the data handler by requesting the location of the CSV files
133
135
  and a list of symbols.
134
-
136
+
135
137
  Args:
136
138
  events : The Event Queue.
137
139
  symbol_list : A list of symbol strings.
@@ -151,35 +153,36 @@ class BaseCSVDataHandler(DataHandler):
151
153
  self._load_and_process_data()
152
154
 
153
155
  @property
154
- def symbols(self)-> List[str]:
156
+ def symbols(self) -> List[str]:
155
157
  return self.symbol_list
156
-
158
+
157
159
  @property
158
- def data(self)-> Dict[str, pd.DataFrame]:
160
+ def data(self) -> Dict[str, pd.DataFrame]:
159
161
  return self.symbol_data
160
-
162
+
161
163
  @property
162
- def datadir(self)-> str:
164
+ def datadir(self) -> str:
163
165
  return self.csv_dir
164
-
166
+
165
167
  @property
166
- def labels(self)-> List[str]:
168
+ def labels(self) -> List[str]:
167
169
  return self.columns
168
-
170
+
169
171
  @property
170
- def index(self)-> str | List[str]:
172
+ def index(self) -> str | List[str]:
171
173
  return self._index
172
-
174
+
173
175
  def _load_and_process_data(self):
174
176
  """
175
177
  Opens the CSV files from the data directory, converting
176
178
  them into pandas DataFrames within a symbol dictionary.
177
179
  """
178
180
  default_names = pd.read_csv(
179
- os.path.join(self.csv_dir, f'{self.symbol_list[0]}.csv')
180
- ).columns.to_list()
181
+ os.path.join(self.csv_dir, f'{self.symbol_list[0]}.csv')
182
+ ).columns.to_list()
181
183
  new_names = self.columns or default_names
182
- new_names = [name.strip().lower().replace(' ', '_') for name in new_names]
184
+ new_names = [name.strip().lower().replace(' ', '_')
185
+ for name in new_names]
183
186
  self.columns = new_names
184
187
  assert 'adj_close' in new_names or 'close' in new_names, \
185
188
  "Column names must contain 'Adj Close' and 'Close' or adj_close and close"
@@ -288,7 +291,8 @@ class BaseCSVDataHandler(DataHandler):
288
291
  try:
289
292
  return getattr(bars_list[-1][1], val_type)
290
293
  except AttributeError:
291
- print(f"Value type {val_type} not available in the historical data set.")
294
+ print(
295
+ f"Value type {val_type} not available in the historical data set.")
292
296
  raise
293
297
 
294
298
  def get_latest_bars_values(self, symbol: str, val_type: str, N=1) -> np.ndarray:
@@ -305,7 +309,8 @@ class BaseCSVDataHandler(DataHandler):
305
309
  try:
306
310
  return np.array([getattr(b[1], val_type) for b in bars_list])
307
311
  except AttributeError:
308
- print(f"Value type {val_type} not available in the historical data set.")
312
+ print(
313
+ f"Value type {val_type} not available in the historical data set.")
309
314
  raise
310
315
 
311
316
  def update_bars(self):
@@ -346,18 +351,18 @@ class CSVDataHandler(BaseCSVDataHandler):
346
351
  events (Queue): The Event Queue.
347
352
  symbol_list (List[str]): A list of symbol strings.
348
353
  csv_dir (str): Absolute directory path to the CSV files.
349
-
354
+
350
355
  NOTE:
351
356
  All csv fille can be strored in 'Home/.bbstrader/csv_data'
352
357
 
353
358
  """
354
359
  csv_dir = kwargs.get("csv_dir")
355
- csv_dir = csv_dir or BBSTRADER_DIR / 'csv_data'
360
+ csv_dir = csv_dir or BBSTRADER_DIR / 'csv_data'
356
361
  super().__init__(
357
- events,
358
- symbol_list,
362
+ events,
363
+ symbol_list,
359
364
  csv_dir,
360
- columns =kwargs.get('columns'),
365
+ columns=kwargs.get('columns'),
361
366
  index_col=kwargs.get('index_col', 0)
362
367
  )
363
368
 
@@ -391,35 +396,35 @@ class MT5DataHandler(BaseCSVDataHandler):
391
396
  See `bbstrader.metatrader.rates.Rates` for other arguments.
392
397
  See `bbstrader.btengine.data.BaseCSVDataHandler` for other arguments.
393
398
  """
394
- self.tf = kwargs.get('time_frame', 'D1')
395
- self.start = kwargs.get('mt5_start', datetime(2000, 1, 1))
396
- self.end = kwargs.get('mt5_end', datetime.now())
397
- self.use_utc = kwargs.get('use_utc', False)
398
- self.filer = kwargs.get('filter', False)
399
- self.fill_na = kwargs.get('fill_na', False)
399
+ self.tf = kwargs.get('time_frame', 'D1')
400
+ self.start = kwargs.get('mt5_start', datetime(2000, 1, 1))
401
+ self.end = kwargs.get('mt5_end', datetime.now())
402
+ self.use_utc = kwargs.get('use_utc', False)
403
+ self.filer = kwargs.get('filter', False)
404
+ self.fill_na = kwargs.get('fill_na', False)
400
405
  self.lower_cols = kwargs.get('lower_cols', True)
401
- self.data_dir = kwargs.get('data_dir')
406
+ self.data_dir = kwargs.get('data_dir')
402
407
  self.symbol_list = symbol_list
403
408
  self.kwargs = kwargs
404
-
409
+
405
410
  csv_dir = self._download_and_cache_data(self.data_dir)
406
411
  super().__init__(
407
412
  events,
408
413
  symbol_list,
409
414
  csv_dir,
410
- columns =kwargs.get('columns'),
415
+ columns=kwargs.get('columns'),
411
416
  index_col=kwargs.get('index_col', 0)
412
417
  )
413
418
 
414
419
  def _download_and_cache_data(self, cache_dir: str):
415
- data_dir = cache_dir or BBSTRADER_DIR / 'mt5' / self.tf
420
+ data_dir = cache_dir or BBSTRADER_DIR / 'mt5' / self.tf
416
421
  data_dir.mkdir(parents=True, exist_ok=True)
417
422
  for symbol in self.symbol_list:
418
423
  try:
419
424
  data = download_historical_data(
420
425
  symbol=symbol,
421
426
  timeframe=self.tf,
422
- date_from=self.start,
427
+ date_from=self.start,
423
428
  date_to=self.end,
424
429
  utc=self.use_utc,
425
430
  filter=self.filer,
@@ -460,17 +465,17 @@ class YFDataHandler(BaseCSVDataHandler):
460
465
  See `bbstrader.btengine.data.BaseCSVDataHandler` for other arguments.
461
466
  """
462
467
  self.symbol_list = symbol_list
463
- self.start_date = kwargs.get('yf_start')
464
- self.end_date = kwargs.get('yf_end', datetime.now())
465
- self.cache_dir = kwargs.get('data_dir')
466
-
468
+ self.start_date = kwargs.get('yf_start')
469
+ self.end_date = kwargs.get('yf_end', datetime.now())
470
+ self.cache_dir = kwargs.get('data_dir')
471
+
467
472
  csv_dir = self._download_and_cache_data(self.cache_dir)
468
-
473
+
469
474
  super().__init__(
470
475
  events,
471
476
  symbol_list,
472
477
  csv_dir,
473
- columns =kwargs.get('columns'),
478
+ columns=kwargs.get('columns'),
474
479
  index_col=kwargs.get('index_col', 0)
475
480
  )
476
481
 
@@ -482,7 +487,7 @@ class YFDataHandler(BaseCSVDataHandler):
482
487
  filepath = os.path.join(cache_dir, f"{symbol}.csv")
483
488
  try:
484
489
  data = yf.download(
485
- symbol, start=self.start_date, end=self.end_date,
490
+ symbol, start=self.start_date, end=self.end_date,
486
491
  multi_level_index=False, progress=False)
487
492
  if data.empty:
488
493
  raise ValueError(f"No data found for {symbol}")
@@ -500,6 +505,7 @@ class EODHDataHandler(BaseCSVDataHandler):
500
505
  To use this class, you need to sign up for an API key at
501
506
  https://eodhistoricaldata.com/ and provide the key as an argument.
502
507
  """
508
+
503
509
  def __init__(self, events: Queue, symbol_list: List[str], **kwargs):
504
510
  """
505
511
  Args:
@@ -515,19 +521,20 @@ class EODHDataHandler(BaseCSVDataHandler):
515
521
  See `bbstrader.btengine.data.BaseCSVDataHandler` for other arguments.
516
522
  """
517
523
  self.symbol_list = symbol_list
518
- self.start_date = kwargs.get('eodhd_start')
519
- self.end_date = kwargs.get('eodhd_end', datetime.now().strftime('%Y-%m-%d'))
520
- self.period = kwargs.get('eodhd_period', 'd')
521
- self.cache_dir = kwargs.get('data_dir')
522
- self.__api_key = kwargs.get('eodhd_api_key', 'demo')
524
+ self.start_date = kwargs.get('eodhd_start')
525
+ self.end_date = kwargs.get(
526
+ 'eodhd_end', datetime.now().strftime('%Y-%m-%d'))
527
+ self.period = kwargs.get('eodhd_period', 'd')
528
+ self.cache_dir = kwargs.get('data_dir')
529
+ self.__api_key = kwargs.get('eodhd_api_key', 'demo')
523
530
 
524
531
  csv_dir = self._download_and_cache_data(self.cache_dir)
525
-
532
+
526
533
  super().__init__(
527
- events,
534
+ events,
528
535
  symbol_list,
529
536
  csv_dir,
530
- columns =kwargs.get('columns'),
537
+ columns=kwargs.get('columns'),
531
538
  index_col=kwargs.get('index_col', 0)
532
539
  )
533
540
 
@@ -538,7 +545,7 @@ class EODHDataHandler(BaseCSVDataHandler):
538
545
  if period in ['d', 'w', 'm']:
539
546
  return client.get_historical_data(
540
547
  symbol=symbol,
541
- interval=period,
548
+ interval=period,
542
549
  iso8601_start=self.start_date,
543
550
  iso8601_end=self.end_date,
544
551
  )
@@ -553,11 +560,11 @@ class EODHDataHandler(BaseCSVDataHandler):
553
560
  unix_end = int(enddt.timestamp())
554
561
  return client.get_intraday_historical_data(
555
562
  symbol=symbol,
556
- interval=period,
563
+ interval=period,
557
564
  from_unix_time=unix_start,
558
565
  to_unix_time=unix_end,
559
566
  )
560
-
567
+
561
568
  def _forma_data(self, data: List[Dict] | pd.DataFrame) -> pd.DataFrame:
562
569
  if isinstance(data, pd.DataFrame):
563
570
  if data.empty or len(data) == 0:
@@ -565,7 +572,7 @@ class EODHDataHandler(BaseCSVDataHandler):
565
572
  df = data.drop(labels=['symbol', 'interval'], axis=1)
566
573
  df = df.rename(columns={'adjusted_close': 'adj_close'})
567
574
  return df
568
-
575
+
569
576
  elif isinstance(data, list):
570
577
  if not data or len(data) == 0:
571
578
  raise ValueError("No data found.")
@@ -573,11 +580,12 @@ class EODHDataHandler(BaseCSVDataHandler):
573
580
  df = df.drop(columns=['timestamp', 'gmtoffset'], axis=1)
574
581
  df = df.rename(columns={'datetime': 'date'})
575
582
  df['adj_close'] = df['close']
576
- df = df[['date', 'open', 'high', 'low', 'close', 'adj_close', 'volume']]
583
+ df = df[['date', 'open', 'high', 'low',
584
+ 'close', 'adj_close', 'volume']]
577
585
  df.date = pd.to_datetime(df.date)
578
586
  df = df.set_index('date')
579
587
  return df
580
-
588
+
581
589
  def _download_and_cache_data(self, cache_dir: str):
582
590
  """Downloads and caches historical data as CSV files."""
583
591
  cache_dir = cache_dir or BBSTRADER_DIR / 'eodhd' / self.period
@@ -603,6 +611,7 @@ class FMPDataHandler(BaseCSVDataHandler):
603
611
  provide the key as an argument.
604
612
 
605
613
  """
614
+
606
615
  def __init__(self, events: Queue, symbol_list: List[str], **kwargs):
607
616
  """
608
617
  Args:
@@ -619,19 +628,20 @@ class FMPDataHandler(BaseCSVDataHandler):
619
628
  See `bbstrader.btengine.data.BaseCSVDataHandler` for other arguments.
620
629
  """
621
630
  self.symbol_list = symbol_list
622
- self.start_date = kwargs.get('fmp_start')
623
- self.end_date = kwargs.get('fmp_end', datetime.now().strftime('%Y-%m-%d'))
624
- self.period = kwargs.get('fmp_period', 'daily')
625
- self.cache_dir = kwargs.get('data_dir')
626
- self.__api_key = kwargs.get('fmp_api_key')
631
+ self.start_date = kwargs.get('fmp_start')
632
+ self.end_date = kwargs.get(
633
+ 'fmp_end', datetime.now().strftime('%Y-%m-%d'))
634
+ self.period = kwargs.get('fmp_period', 'daily')
635
+ self.cache_dir = kwargs.get('data_dir')
636
+ self.__api_key = kwargs.get('fmp_api_key')
627
637
 
628
638
  csv_dir = self._download_and_cache_data(self.cache_dir)
629
-
639
+
630
640
  super().__init__(
631
641
  events,
632
642
  symbol_list,
633
643
  csv_dir,
634
- columns =kwargs.get('columns'),
644
+ columns=kwargs.get('columns'),
635
645
  index_col=kwargs.get('index_col', 0)
636
646
  )
637
647
 
@@ -641,7 +651,7 @@ class FMPDataHandler(BaseCSVDataHandler):
641
651
  toolkit = Toolkit(
642
652
  symbol,
643
653
  api_key=self.__api_key,
644
- start_date=self.start_date,
654
+ start_date=self.start_date,
645
655
  end_date=self.end_date,
646
656
  benchmark_ticker=None,
647
657
  progress_bar=False
@@ -650,16 +660,17 @@ class FMPDataHandler(BaseCSVDataHandler):
650
660
  return toolkit.get_historical_data(period=period, progress_bar=False)
651
661
  elif period in ['1min', '5min', '15min', '30min', '1hour']:
652
662
  return toolkit.get_intraday_data(period=period, progress_bar=False)
653
-
663
+
654
664
  def _format_data(self, data: pd.DataFrame, period: str) -> pd.DataFrame:
655
665
  if data.empty or len(data) == 0:
656
666
  raise ValueError("No data found.")
657
667
  if period[0].isnumeric():
658
- data = data.drop(columns=['Return', 'Volatility', 'Cumulative Return'], axis=1)
668
+ data = data.drop(
669
+ columns=['Return', 'Volatility', 'Cumulative Return'], axis=1)
659
670
  else:
660
- data = data.drop(columns=['Dividends', 'Return', 'Volatility',
661
- 'Excess Return', 'Excess Volatility',
662
- 'Cumulative Return'], axis=1)
671
+ data = data.drop(columns=['Dividends', 'Return', 'Volatility',
672
+ 'Excess Return', 'Excess Volatility',
673
+ 'Cumulative Return'], axis=1)
663
674
  data = data.reset_index()
664
675
  if 'Adj Close' not in data.columns:
665
676
  data['Adj Close'] = data['Close']
@@ -667,7 +678,7 @@ class FMPDataHandler(BaseCSVDataHandler):
667
678
  data['date'] = pd.to_datetime(data['date'])
668
679
  data.set_index('date', inplace=True)
669
680
  return data
670
-
681
+
671
682
  def _download_and_cache_data(self, cache_dir: str):
672
683
  """Downloads and caches historical data as CSV files."""
673
684
  cache_dir = cache_dir or BBSTRADER_DIR / 'fmp' / self.period
@@ -685,4 +696,4 @@ class FMPDataHandler(BaseCSVDataHandler):
685
696
 
686
697
  # TODO Add data Handlers for Interactive Brokers
687
698
  class TWSDataHandler(BaseCSVDataHandler):
688
- ...
699
+ ...
@@ -135,7 +135,8 @@ class OrderEvent(Event):
135
135
  """
136
136
  print(
137
137
  "Order: Symbol=%s, Type=%s, Quantity=%s, Direction=%s, Price=%s" %
138
- (self.symbol, self.order_type, self.quantity, self.direction, self.price)
138
+ (self.symbol, self.order_type,
139
+ self.quantity, self.direction, self.price)
139
140
  )
140
141
 
141
142