bbstrader 0.3.6__tar.gz → 0.3.7__tar.gz

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.

Files changed (70) hide show
  1. {bbstrader-0.3.6/bbstrader.egg-info → bbstrader-0.3.7}/PKG-INFO +3 -3
  2. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/__init__.py +1 -1
  3. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/__main__.py +2 -2
  4. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/apps/_copier.py +40 -37
  5. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/backtest.py +33 -28
  6. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/data.py +105 -81
  7. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/event.py +21 -22
  8. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/execution.py +51 -24
  9. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/performance.py +23 -12
  10. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/portfolio.py +40 -30
  11. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/scripts.py +13 -12
  12. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/strategy.py +288 -101
  13. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/compat.py +4 -3
  14. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/config.py +20 -36
  15. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/core/data.py +76 -48
  16. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/core/scripts.py +22 -21
  17. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/core/utils.py +13 -12
  18. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/trading/execution.py +6 -0
  19. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/tseries.py +55 -39
  20. {bbstrader-0.3.6 → bbstrader-0.3.7/bbstrader.egg-info}/PKG-INFO +3 -3
  21. {bbstrader-0.3.6 → bbstrader-0.3.7}/pyproject.toml +3 -3
  22. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/engine/test_portfolio.py +3 -2
  23. {bbstrader-0.3.6 → bbstrader-0.3.7}/LICENSE +0 -0
  24. {bbstrader-0.3.6 → bbstrader-0.3.7}/MANIFEST.in +0 -0
  25. {bbstrader-0.3.6 → bbstrader-0.3.7}/README.md +0 -0
  26. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/apps/__init__.py +0 -0
  27. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/btengine/__init__.py +0 -0
  28. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/core/__init__.py +0 -0
  29. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/ibkr/__init__.py +0 -0
  30. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/ibkr/utils.py +0 -0
  31. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/__init__.py +0 -0
  32. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/account.py +0 -0
  33. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/analysis.py +0 -0
  34. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/copier.py +0 -0
  35. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/rates.py +0 -0
  36. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/risk.py +0 -0
  37. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/scripts.py +0 -0
  38. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/trade.py +0 -0
  39. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/metatrader/utils.py +0 -0
  40. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/models/__init__.py +0 -0
  41. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/models/factors.py +0 -0
  42. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/models/ml.py +0 -0
  43. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/models/nlp.py +0 -0
  44. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/models/optimization.py +0 -0
  45. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/models/portfolio.py +0 -0
  46. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/models/risk.py +0 -0
  47. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/trading/__init__.py +0 -0
  48. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/trading/scripts.py +0 -0
  49. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/trading/strategies.py +0 -0
  50. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader/trading/utils.py +0 -0
  51. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader.egg-info/SOURCES.txt +0 -0
  52. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader.egg-info/dependency_links.txt +0 -0
  53. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader.egg-info/entry_points.txt +0 -0
  54. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader.egg-info/requires.txt +0 -0
  55. {bbstrader-0.3.6 → bbstrader-0.3.7}/bbstrader.egg-info/top_level.txt +0 -0
  56. {bbstrader-0.3.6 → bbstrader-0.3.7}/docs/conf.py +0 -0
  57. {bbstrader-0.3.6 → bbstrader-0.3.7}/requirements.txt +0 -0
  58. {bbstrader-0.3.6 → bbstrader-0.3.7}/setup.cfg +0 -0
  59. {bbstrader-0.3.6 → bbstrader-0.3.7}/setup.py +0 -0
  60. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/__init__.py +0 -0
  61. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/engine/__init__.py +0 -0
  62. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/engine/test_backtest.py +0 -0
  63. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/engine/test_data.py +0 -0
  64. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/engine/test_events.py +0 -0
  65. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/engine/test_execution.py +0 -0
  66. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/metatrader/__init__.py +0 -0
  67. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/metatrader/test_account.py +0 -0
  68. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/metatrader/test_rates.py +0 -0
  69. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/metatrader/test_risk_management.py +0 -0
  70. {bbstrader-0.3.6 → bbstrader-0.3.7}/tests/metatrader/test_trade.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bbstrader
3
- Version: 0.3.6
3
+ Version: 0.3.7
4
4
  Summary: Simplified Investment & Trading Toolkit
5
- Author-email: Bertin Balouki SIMYELI <bertin@bbstrader.com>
6
- Maintainer-email: Bertin Balouki SIMYELI <bertin@bbstrader.com>
5
+ Author-email: Bertin Balouki SIMYELI <bertin@bbs-trading.com>
6
+ Maintainer-email: Bertin Balouki SIMYELI <bertin@bbs-trading.com>
7
7
  License-Expression: MIT
8
8
  Project-URL: Homepage, https://github.com/bbalouki/bbstrader
9
9
  Project-URL: Download, https://pypi.org/project/bbstrader/
@@ -5,7 +5,7 @@ Simplified Investment & Trading Toolkit
5
5
 
6
6
  __author__ = "Bertin Balouki SIMYELI"
7
7
  __copyright__ = "2023-2025 Bertin Balouki SIMYELI"
8
- __email__ = "bertin@bbstrader.com"
8
+ __email__ = "bertin@bbs-trading.com"
9
9
  __license__ = "MIT"
10
10
 
11
11
  from importlib.metadata import version, PackageNotFoundError
@@ -23,7 +23,7 @@ class _Module(Enum):
23
23
  FONT = pyfiglet.figlet_format("BBSTRADER", font="big")
24
24
 
25
25
 
26
- def main():
26
+ def main() -> None:
27
27
  DESCRIPTION = "BBSTRADER"
28
28
  USAGE_TEXT = """
29
29
  Usage:
@@ -51,7 +51,7 @@ def main():
51
51
  if ("-h" in sys.argv or "--help" in sys.argv) and args.run is None:
52
52
  print(Fore.WHITE + USAGE_TEXT)
53
53
  sys.exit(0)
54
- if ("-v" in sys.argv or "--version" in sys.argv):
54
+ if "-v" in sys.argv or "--version" in sys.argv:
55
55
  print(Fore.GREEN + f"bbstrader version {__version__}")
56
56
  print(Fore.WHITE + f"bbstrader maintained and supported by {__author__}")
57
57
  sys.exit(0)
@@ -12,10 +12,13 @@ from PIL import Image, ImageTk
12
12
  from bbstrader.metatrader.copier import copier_worker_process, get_symbols_from_string
13
13
 
14
14
 
15
- def resource_path(relative_path):
15
+ from typing import Any, Dict, Union
16
+
17
+
18
+ def resource_path(relative_path: str) -> Path:
16
19
  """Get absolute path to resource"""
17
20
  try:
18
- base_path = Path(sys._MEIPASS)
21
+ base_path = Path(sys._MEIPASS) # type: ignore
19
22
  except AttributeError:
20
23
  base_path = Path(__file__).resolve().parent.parent.parent
21
24
 
@@ -27,10 +30,10 @@ ICON_PATH = resource_path("assets/bbstrader.ico")
27
30
  LOGO_PATH = resource_path("assets/bbstrader.png")
28
31
 
29
32
 
30
- class TradeCopierApp(object):
33
+ class TradeCopierApp:
31
34
  copier_processes: List[multiprocessing.Process]
32
35
 
33
- def __init__(self, root: tk.Tk):
36
+ def __init__(self, root: tk.Tk) -> None:
34
37
  root.title(TITLE)
35
38
  root.geometry("1600x900")
36
39
  self.root = root
@@ -48,13 +51,13 @@ class TradeCopierApp(object):
48
51
  self.add_destination_accounts_frame(self.main_frame)
49
52
  self.add_copier_settings(self.main_frame)
50
53
 
51
- def set_style(self):
54
+ def set_style(self) -> None:
52
55
  self.style = ttk.Style()
53
56
  self.style.configure("Bold.TLabelframe.Label", font=("Segoe UI", 15, "bold"))
54
57
 
55
- def add_main_frame(self):
58
+ def add_main_frame(self) -> ttk.Frame:
56
59
  main_frame = ttk.Frame(self.root, padding="10")
57
- main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
60
+ main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # type: ignore
58
61
 
59
62
  # Configure the layout
60
63
  main_frame.columnconfigure(0, weight=2)
@@ -63,7 +66,7 @@ class TradeCopierApp(object):
63
66
 
64
67
  # --- visual/logo frame ---
65
68
  self.visual_frame = ttk.Frame(main_frame)
66
- self.visual_frame.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E))
69
+ self.visual_frame.grid(row=0, column=1, padx=5, pady=5, sticky=(tk.W, tk.E)) # type: ignore
67
70
 
68
71
  # --- opier settings---
69
72
  self.right_panel_frame = ttk.Frame(main_frame)
@@ -76,12 +79,12 @@ class TradeCopierApp(object):
76
79
 
77
80
  return main_frame
78
81
 
79
- def add_source_account_frame(self, main_frame):
82
+ def add_source_account_frame(self, main_frame: ttk.Frame) -> None:
80
83
  # --- Source Account ---
81
84
  source_frame = ttk.LabelFrame(
82
85
  main_frame, text="Source Account", style="Bold.TLabelframe"
83
86
  )
84
- source_frame.grid(row=0, column=0, padx=5, pady=5, sticky=(tk.W, tk.E, tk.N))
87
+ source_frame.grid(row=0, column=0, padx=5, pady=5, sticky=(tk.W, tk.E, tk.N)) # type: ignore
85
88
  source_frame.columnconfigure(1, weight=1)
86
89
  source_frame.columnconfigure(3, weight=1)
87
90
 
@@ -131,13 +134,13 @@ class TradeCopierApp(object):
131
134
  )
132
135
  self.allow_copy_check.grid(row=1, column=3, sticky=tk.W, padx=5, pady=2)
133
136
 
134
- def add_destination_accounts_frame(self, main_frame):
137
+ def add_destination_accounts_frame(self, main_frame: ttk.Frame) -> None:
135
138
  # --- Destination Accounts Scrollable Area ---
136
139
  self.destinations_outer_frame = ttk.LabelFrame(
137
140
  main_frame, text="Destination Accounts", style="Bold.TLabelframe"
138
141
  )
139
142
  self.destinations_outer_frame.grid(
140
- row=1, column=0, padx=5, pady=5, sticky=(tk.W, tk.E, tk.N, tk.S)
143
+ row=1, column=0, padx=5, pady=5, sticky=(tk.W, tk.E, tk.N, tk.S) # type: ignore
141
144
  )
142
145
  self.destinations_outer_frame.rowconfigure(0, weight=1)
143
146
  self.destinations_outer_frame.columnconfigure(0, weight=1)
@@ -159,10 +162,10 @@ class TradeCopierApp(object):
159
162
 
160
163
  self.scrollable_frame_for_destinations.columnconfigure(0, weight=1)
161
164
 
162
- def configure_scroll_region(event):
165
+ def configure_scroll_region(event: tk.Event) -> None:
163
166
  self.canvas.configure(scrollregion=self.canvas.bbox("all"))
164
167
 
165
- def configure_canvas_window(event):
168
+ def configure_canvas_window(event: tk.Event) -> None:
166
169
  self.canvas.itemconfig(self.canvas_window, width=event.width)
167
170
 
168
171
  self.scrollable_frame_for_destinations.bind(
@@ -170,7 +173,7 @@ class TradeCopierApp(object):
170
173
  )
171
174
  self.canvas.bind("<Configure>", configure_canvas_window)
172
175
 
173
- def _on_mousewheel(event):
176
+ def _on_mousewheel(event: tk.Event) -> None:
174
177
  scroll_val = -1 * (event.delta // 120)
175
178
  self.canvas.yview_scroll(scroll_val, "units")
176
179
 
@@ -189,12 +192,12 @@ class TradeCopierApp(object):
189
192
 
190
193
  self.add_destination_account()
191
194
 
192
- def add_copier_settings(self, main_frame):
195
+ def add_copier_settings(self, main_frame: ttk.Frame) -> None:
193
196
  # --- Copier Settings ---
194
197
  settings_frame = ttk.LabelFrame(
195
198
  self.right_panel_frame, text="Copier Settings", style="Bold.TLabelframe"
196
199
  )
197
- settings_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=5, pady=5)
200
+ settings_frame.grid(row=0, column=0, sticky=(tk.W, tk.E), padx=5, pady=5) # type: ignore
198
201
 
199
202
  ttk.Label(settings_frame, text="Sleep Time (s)").grid(
200
203
  row=0, column=0, sticky=tk.W, padx=5, pady=2
@@ -240,14 +243,14 @@ class TradeCopierApp(object):
240
243
  columnspan=2,
241
244
  padx=5,
242
245
  pady=5,
243
- sticky=(tk.W, tk.E, tk.N, tk.S),
246
+ sticky=(tk.W, tk.E, tk.N, tk.S), # type: ignore
244
247
  )
245
248
 
246
249
  self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=10)
247
250
  self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
248
251
  self.log_text.configure(state="disabled")
249
252
 
250
- def add_logo_and_description(self):
253
+ def add_logo_and_description(self) -> None:
251
254
  image = Image.open(LOGO_PATH)
252
255
  image = image.resize((120, 120))
253
256
  self.logo_img = ImageTk.PhotoImage(image)
@@ -262,9 +265,9 @@ class TradeCopierApp(object):
262
265
  font=("Segoe UI", 10, "bold"),
263
266
  ).pack()
264
267
 
265
- def add_destination_account(self):
268
+ def add_destination_account(self) -> None:
266
269
  dest_row = len(self.destination_widgets)
267
- dest_widgets = {}
270
+ dest_widgets: Dict[str, Union[ttk.Entry, ttk.Combobox]] = {}
268
271
 
269
272
  if dest_row > 0:
270
273
  sep = ttk.Separator(
@@ -385,13 +388,13 @@ class TradeCopierApp(object):
385
388
  self.add_dest_button.grid_forget()
386
389
  self.add_dest_button.grid(row=1, column=0, columnspan=2, pady=10, sticky=tk.EW)
387
390
 
388
- def log_message(self, message):
391
+ def log_message(self, message: str) -> None:
389
392
  self.log_text.configure(state="normal")
390
393
  self.log_text.insert(tk.END, message + "\n")
391
394
  self.log_text.configure(state="disabled")
392
395
  self.log_text.see(tk.END)
393
396
 
394
- def check_log_queue(self):
397
+ def check_log_queue(self) -> None:
395
398
  try:
396
399
  while True:
397
400
  message = self.log_queue.get_nowait()
@@ -413,7 +416,7 @@ class TradeCopierApp(object):
413
416
  else:
414
417
  return get_symbols_from_string(symbols)
415
418
 
416
- def _validate_inputs(self):
419
+ def _validate_inputs(self) -> bool:
417
420
  if (
418
421
  not self.source_login_entry.get()
419
422
  or not self.source_password_entry.get()
@@ -463,11 +466,11 @@ class TradeCopierApp(object):
463
466
  # Add more validation for time formats if needed
464
467
  return True
465
468
 
466
- def start_copier(self):
469
+ def start_copier(self) -> None:
467
470
  if not self._validate_inputs():
468
471
  return
469
472
 
470
- source_config = {
473
+ source_config: Dict[str, Any] = {
471
474
  "login": int(self.source_login_entry.get().strip()),
472
475
  "password": self.source_password_entry.get().strip(),
473
476
  "server": self.source_server_entry.get().strip(),
@@ -476,10 +479,10 @@ class TradeCopierApp(object):
476
479
  "unique": not self.allow_copy_var.get(),
477
480
  }
478
481
 
479
- destinations_config = []
482
+ destinations_config: List[Dict[str, Any]] = []
480
483
  for dest_widget_map in self.destination_widgets:
481
484
  symbols_str = dest_widget_map["symbols"].get().strip()
482
- dest = {
485
+ dest: Dict[str, Any] = {
483
486
  "login": int(dest_widget_map["login"].get().strip()),
484
487
  "password": dest_widget_map["password"].get().strip(),
485
488
  "server": dest_widget_map["server"].get().strip(),
@@ -510,7 +513,7 @@ class TradeCopierApp(object):
510
513
  try:
511
514
  # Create shared shutdown event and log queue
512
515
  self.shutdown_event = multiprocessing.Event()
513
- self.log_queue = multiprocessing.Queue()
516
+ self.log_queue: "multiprocessing.Queue[str]" = multiprocessing.Queue()
514
517
  self.copier_processes = []
515
518
 
516
519
  # Spawn one process for each destination
@@ -546,7 +549,7 @@ class TradeCopierApp(object):
546
549
  self.start_button.config(state=tk.NORMAL)
547
550
  self.stop_button.config(state=tk.DISABLED)
548
551
 
549
- def stop_copier(self):
552
+ def stop_copier(self) -> None:
550
553
  self.log_message("Attempting to stop all Trade Copier processes...")
551
554
 
552
555
  if not hasattr(self, "copier_processes") or not self.copier_processes:
@@ -581,12 +584,12 @@ class TradeCopierApp(object):
581
584
  # Cleanup references
582
585
  self.copier_processes = []
583
586
  self.shutdown_event = None
584
- self.log_queue = None
587
+ self.log_queue = None # type: ignore
585
588
 
586
589
  self.start_button.config(state=tk.NORMAL)
587
590
  self.stop_button.config(state=tk.DISABLED)
588
591
 
589
- def browse_path(self, path_entry_widget):
592
+ def browse_path(self, path_entry_widget: ttk.Entry) -> None:
590
593
  filetypes = (("Executable files", "*.exe"), ("All files", "*.*"))
591
594
  filepath = filedialog.askopenfilename(
592
595
  title="Select MetaTrader Terminal Executable", filetypes=filetypes
@@ -595,7 +598,7 @@ class TradeCopierApp(object):
595
598
  path_entry_widget.delete(0, tk.END)
596
599
  path_entry_widget.insert(0, filepath)
597
600
 
598
- def browse_symbols_file(self, symbols_entry_widget):
601
+ def browse_symbols_file(self, symbols_entry_widget: ttk.Entry) -> None:
599
602
  """
600
603
  Opens a file dialog to select a .txt file, reads the content,
601
604
  and populates the given symbols_entry_widget with a comma-separated list.
@@ -623,7 +626,7 @@ class TradeCopierApp(object):
623
626
  self.log_message(f"Error loading symbols: {e}")
624
627
 
625
628
 
626
- def main():
629
+ def main() -> None:
627
630
  """
628
631
  Main function to initialize and run the Trade Copier GUI.
629
632
  """
@@ -632,12 +635,12 @@ def main():
632
635
  root.iconbitmap(os.path.abspath(ICON_PATH))
633
636
  app = TradeCopierApp(root)
634
637
 
635
- def on_closing():
638
+ def on_closing() -> None:
636
639
  try:
637
640
  if (
638
641
  hasattr(app, "copier_process")
639
- and app.copier_process
640
- and app.copier_process.is_alive()
642
+ and app.copier_process # type: ignore
643
+ and app.copier_process.is_alive() # type: ignore
641
644
  ):
642
645
  app.log_message("Window closed, stopping Trade Copier...")
643
646
  app.stop_copier()
@@ -1,11 +1,13 @@
1
1
  import queue
2
2
  import time
3
3
  from datetime import datetime
4
- from typing import List, Literal, Optional
4
+ from typing import Any, List, Literal, Optional, Type
5
5
 
6
+ import pandas as pd
6
7
  from tabulate import tabulate
7
- from bbstrader.btengine.event import Events
8
+
8
9
  from bbstrader.btengine.data import DataHandler
10
+ from bbstrader.btengine.event import Events
9
11
  from bbstrader.btengine.execution import ExecutionHandler, SimExecutionHandler
10
12
  from bbstrader.btengine.portfolio import Portfolio
11
13
  from bbstrader.btengine.strategy import Strategy
@@ -13,7 +15,7 @@ from bbstrader.btengine.strategy import Strategy
13
15
  __all__ = ["Backtest", "BacktestEngine", "run_backtest"]
14
16
 
15
17
 
16
- class Backtest(object):
18
+ class Backtest:
17
19
  """
18
20
  The `Backtest()` object encapsulates the event-handling logic and essentially
19
21
  ties together all of the other classes.
@@ -63,12 +65,12 @@ class BacktestEngine(Backtest):
63
65
  initial_capital: float,
64
66
  heartbeat: float,
65
67
  start_date: datetime,
66
- data_handler: DataHandler,
67
- execution_handler: ExecutionHandler,
68
- strategy: Strategy,
68
+ data_handler: Type[DataHandler],
69
+ execution_handler: Type[ExecutionHandler],
70
+ strategy: Type[Strategy],
69
71
  /,
70
- **kwargs,
71
- ):
72
+ **kwargs: Any,
73
+ ) -> None:
72
74
  """
73
75
  Initialises the backtest.
74
76
 
@@ -95,7 +97,7 @@ class BacktestEngine(Backtest):
95
97
  self.strategy_cls = strategy
96
98
  self.kwargs = kwargs
97
99
 
98
- self.events = queue.Queue()
100
+ self.events: "queue.Queue[Events]" = queue.Queue()
99
101
 
100
102
  self.signals = 0
101
103
  self.orders = 0
@@ -105,7 +107,7 @@ class BacktestEngine(Backtest):
105
107
  self.show_equity = kwargs.get("show_equity", False)
106
108
  self.stats_file = kwargs.get("stats_file", None)
107
109
 
108
- def _generate_trading_instances(self):
110
+ def _generate_trading_instances(self) -> None:
109
111
  """
110
112
  Generates the trading instance objects from
111
113
  their class types.
@@ -121,7 +123,7 @@ class BacktestEngine(Backtest):
121
123
  self.strategy: Strategy = self.strategy_cls(
122
124
  bars=self.data_handler, events=self.events, **self.kwargs
123
125
  )
124
- self.portfolio = Portfolio(
126
+ self.portfolio: Portfolio = Portfolio(
125
127
  self.data_handler,
126
128
  self.events,
127
129
  self.start_date,
@@ -132,7 +134,7 @@ class BacktestEngine(Backtest):
132
134
  self.events, self.data_handler, **self.kwargs
133
135
  )
134
136
 
135
- def _run_backtest(self):
137
+ def _run_backtest(self) -> None:
136
138
  """
137
139
  Executes the backtest.
138
140
  """
@@ -150,9 +152,9 @@ class BacktestEngine(Backtest):
150
152
  self.strategy.cash = value
151
153
  else:
152
154
  print("\n[======= BACKTEST COMPLETED =======]")
153
- print(
154
- f"END DATE: {self.data_handler.get_latest_bar_datetime(self.symbol_list[0])}"
155
- )
155
+ dt = self.data_handler.get_latest_bar_datetime(self.symbol_list[0])
156
+ if dt:
157
+ print(f"END DATE: {dt}")
156
158
  print(f"TOTAL BARS: {i} ")
157
159
  print(f"PORFOLIO VALUE: {round(value, 2)}")
158
160
  break
@@ -184,7 +186,7 @@ class BacktestEngine(Backtest):
184
186
 
185
187
  time.sleep(self.heartbeat)
186
188
 
187
- def _output_performance(self):
189
+ def _output_performance(self) -> None:
188
190
  """
189
191
  Outputs the strategy performance from the backtest.
190
192
  """
@@ -218,7 +220,7 @@ class BacktestEngine(Backtest):
218
220
  "\n",
219
221
  )
220
222
 
221
- def simulate_trading(self):
223
+ def simulate_trading(self) -> pd.DataFrame:
222
224
  """
223
225
  Simulates the backtest and outputs portfolio performance.
224
226
 
@@ -233,13 +235,13 @@ class BacktestEngine(Backtest):
233
235
  def run_backtest(
234
236
  symbol_list: List[str],
235
237
  start_date: datetime,
236
- data_handler: DataHandler,
237
- strategy: Strategy,
238
- exc_handler: Optional[ExecutionHandler] = None,
238
+ data_handler: Type[DataHandler],
239
+ strategy: Type[Strategy],
240
+ exc_handler: Optional[Type[ExecutionHandler]] = None,
239
241
  initial_capital: float = 100000.0,
240
242
  heartbeat: float = 0.0,
241
- **kwargs,
242
- ):
243
+ **kwargs: Any,
244
+ ) -> pd.DataFrame:
243
245
  """
244
246
  Runs a backtest simulation based on a `DataHandler`, `Strategy`, and `ExecutionHandler`.
245
247
 
@@ -315,7 +317,7 @@ def run_backtest(
315
317
  ... )
316
318
  """
317
319
  if exc_handler is None:
318
- execution_handler = SimExecutionHandler
320
+ execution_handler: Type[ExecutionHandler] = SimExecutionHandler
319
321
  else:
320
322
  execution_handler = exc_handler
321
323
  engine = BacktestEngine(
@@ -338,14 +340,16 @@ class CerebroEngine: ...
338
340
  class ZiplineEngine: ...
339
341
 
340
342
 
341
- def run_backtest_with(engine: Literal["bbstrader", "cerebro", "zipline"], **kwargs):
343
+ def run_backtest_with(
344
+ engine: Literal["bbstrader", "cerebro", "zipline"], **kwargs: Any
345
+ ) -> Optional[pd.DataFrame]:
342
346
  """ """
343
347
  if engine == "bbstrader":
344
348
  return run_backtest(
345
- symbol_list=kwargs.get("symbol_list"),
346
- start_date=kwargs.get("start_date"),
347
- data_handler=kwargs.get("data_handler"),
348
- strategy=kwargs.get("strategy"),
349
+ symbol_list=kwargs.get("symbol_list"), # type: ignore
350
+ start_date=kwargs.get("start_date"), # type: ignore
351
+ data_handler=kwargs.get("data_handler"), # type: ignore
352
+ strategy=kwargs.get("strategy"), # type: ignore
349
353
  exc_handler=kwargs.get("exc_handler"),
350
354
  initial_capital=kwargs.get("initial_capital", 100000.0),
351
355
  heartbeat=kwargs.get("heartbeat", 0.0),
@@ -357,3 +361,4 @@ def run_backtest_with(engine: Literal["bbstrader", "cerebro", "zipline"], **kwar
357
361
  elif engine == "zipline":
358
362
  # TODO:
359
363
  raise NotImplementedError("zipline engine is not supported yet")
364
+ return None