forexsmartbot 3.2.0__tar.gz → 3.2.1__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.
Files changed (140) hide show
  1. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/PKG-INFO +46 -13
  2. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/README.md +45 -12
  3. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/strategy.py +2 -9
  4. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/calculation_engine.py +15 -6
  5. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/__init__.py +15 -2
  6. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/multi_timeframe_strategy.py +38 -12
  7. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/sma_crossover.py +4 -18
  8. forexsmartbot-3.2.1/forexsmartbot/utils/native_indicators.py +200 -0
  9. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/PKG-INFO +46 -13
  10. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/SOURCES.txt +7 -1
  11. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/requires.txt +2 -0
  12. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/pyproject.toml +2 -2
  13. forexsmartbot-3.2.1/tests/test_config.py +48 -0
  14. forexsmartbot-3.2.1/tests/test_multi_timeframe_strategy.py +119 -0
  15. forexsmartbot-3.2.1/tests/test_native_indicators.py +109 -0
  16. forexsmartbot-3.2.1/tests/test_risk.py +121 -0
  17. forexsmartbot-3.2.1/tests/test_strategies.py +129 -0
  18. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/LICENSE +0 -0
  19. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/__init__.py +0 -0
  20. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/__init__.py +0 -0
  21. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/__init__.py +0 -0
  22. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/ib_tws_broker.py +0 -0
  23. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/mt4_broker.py +0 -0
  24. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/paper_broker.py +0 -0
  25. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/rest_broker.py +0 -0
  26. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/__init__.py +0 -0
  27. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/alpha_vantage_provider.py +0 -0
  28. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/config.py +0 -0
  29. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/csv_provider.py +0 -0
  30. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/dummy_provider.py +0 -0
  31. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/mt4_provider.py +0 -0
  32. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/multi_provider.py +0 -0
  33. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/oanda_provider.py +0 -0
  34. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/stooq_provider.py +0 -0
  35. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/twelve_data_provider.py +0 -0
  36. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/yfinance_provider.py +0 -0
  37. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/__init__.py +0 -0
  38. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/chart_patterns.py +0 -0
  39. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/correlation_matrix.py +0 -0
  40. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/economic_calendar.py +0 -0
  41. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/enhanced_chart.py +0 -0
  42. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/market_depth.py +0 -0
  43. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/performance_attribution.py +0 -0
  44. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/portfolio_analytics.py +0 -0
  45. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/risk_analytics.py +0 -0
  46. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/strategy_comparison.py +0 -0
  47. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/trade_journal.py +0 -0
  48. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/brokers/mt4_bridge.py +0 -0
  49. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/__init__.py +0 -0
  50. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/code_generator.py +0 -0
  51. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/strategy_builder.py +0 -0
  52. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/strategy_template.py +0 -0
  53. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/__init__.py +0 -0
  54. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/api_client.py +0 -0
  55. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/api_server.py +0 -0
  56. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/cloud_sync.py +0 -0
  57. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/error_handler.py +0 -0
  58. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/mobile_api.py +0 -0
  59. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/remote_monitor.py +0 -0
  60. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/security.py +0 -0
  61. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/backtester.py +0 -0
  62. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/config.py +0 -0
  63. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/interfaces.py +0 -0
  64. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/paper_broker.py +0 -0
  65. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/portfolio.py +0 -0
  66. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/risk.py +0 -0
  67. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/risk_engine.py +0 -0
  68. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/trade_manager.py +0 -0
  69. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/gui/main_window.py +0 -0
  70. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/gui/theme.py +0 -0
  71. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/marketplace/__init__.py +0 -0
  72. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/marketplace/marketplace.py +0 -0
  73. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/marketplace/strategy_rating.py +0 -0
  74. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/__init__.py +0 -0
  75. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/health_check.py +0 -0
  76. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/performance_tracker.py +0 -0
  77. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/strategy_monitor.py +0 -0
  78. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/__init__.py +0 -0
  79. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/adaptive_parameters.py +0 -0
  80. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/genetic_optimizer.py +0 -0
  81. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/hyperparameter_optimizer.py +0 -0
  82. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/monte_carlo.py +0 -0
  83. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/multi_objective_optimizer.py +0 -0
  84. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/parameter_sensitivity.py +0 -0
  85. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/walk_forward.py +0 -0
  86. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/__init__.py +0 -0
  87. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/backtest.py +0 -0
  88. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/controller.py +0 -0
  89. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/data_streaming.py +0 -0
  90. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/enhanced_backtest.py +0 -0
  91. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/fast_trading_controller.py +0 -0
  92. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/gpu_backtest.py +0 -0
  93. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/language_manager.py +0 -0
  94. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/notification_service.py +0 -0
  95. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/persistence.py +0 -0
  96. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/startup_manager.py +0 -0
  97. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/adaptive_trend_flow.py +0 -0
  98. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/breakout_atr.py +0 -0
  99. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/ensemble_ml_strategy.py +0 -0
  100. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/fear_index.py +0 -0
  101. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/lstm_strategy.py +0 -0
  102. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/mean_reversion.py +0 -0
  103. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/ml_adaptive_supertrend.py +0 -0
  104. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/momentum_breakout.py +0 -0
  105. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/news_trading.py +0 -0
  106. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/rl_strategy.py +0 -0
  107. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/rsi_reversion.py +0 -0
  108. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/scalping_ma.py +0 -0
  109. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/svm_strategy.py +0 -0
  110. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/transformer_strategy.py +0 -0
  111. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/__init__.py +0 -0
  112. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/ci_cd_pipeline.py +0 -0
  113. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/paper_account_tester.py +0 -0
  114. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/strategy_sandbox.py +0 -0
  115. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/__init__.py +0 -0
  116. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/analytics_dialog.py +0 -0
  117. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/analytics_widget.py +0 -0
  118. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/backtest_dialog.py +0 -0
  119. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/charts.py +0 -0
  120. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/cloud_dialog.py +0 -0
  121. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/enhanced_charts.py +0 -0
  122. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/enhanced_main_window.py +0 -0
  123. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/export_dialog.py +0 -0
  124. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/main_window.py +0 -0
  125. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/marketplace_dialog.py +0 -0
  126. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/monitoring_dialog.py +0 -0
  127. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/monitoring_widget.py +0 -0
  128. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/optimization_dialog.py +0 -0
  129. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/settings_dialog.py +0 -0
  130. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/simple_chart.py +0 -0
  131. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/strategy_builder_dialog.py +0 -0
  132. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/theme.py +0 -0
  133. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/tradingview_chart.py +0 -0
  134. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/utils/__init__.py +0 -0
  135. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/utils/gpu_numpy.py +0 -0
  136. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/utils/gpu_utils.py +0 -0
  137. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/dependency_links.txt +0 -0
  138. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/entry_points.txt +0 -0
  139. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/top_level.txt +0 -0
  140. {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forexsmartbot
3
- Version: 3.2.0
3
+ Version: 3.2.1
4
4
  Summary: Professional forex trading bot with advanced risk management and strategy plugins
5
5
  Author-email: VoxHash <contact@voxhash.dev>
6
6
  License: MIT
@@ -30,7 +30,7 @@ Requires-Dist: mplfinance>=0.12.10
30
30
  Requires-Dist: TA-Lib>=0.6.7
31
31
  Requires-Dist: requests>=2.31.0
32
32
  Requires-Dist: ib-insync>=0.9.86
33
- Requires-Dist: win10toast>=0.9
33
+ Requires-Dist: win10toast>=0.9; sys_platform == "win32"
34
34
  Provides-Extra: dev
35
35
  Requires-Dist: pytest>=7.0.0; extra == "dev"
36
36
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -42,13 +42,17 @@ Dynamic: license-file
42
42
 
43
43
  # ForexSmartBot
44
44
 
45
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
46
- [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
47
- [![PyQt6](https://img.shields.io/badge/PyQt6-6.7+-green.svg)](https://www.riverbankcomputing.com/software/pyqt/)
48
- [![Version](https://img.shields.io/badge/version-3.2.0-blue.svg)](https://github.com/VoxHash/ForexSmartBot/releases)
45
+ ![PyPI version](https://img.shields.io/pypi/v/forexsmartbot.svg)
46
+ ![Python Versions](https://img.shields.io/pypi/pyversions/forexsmartbot.svg)
47
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
48
+ ![PyQt6](https://img.shields.io/badge/PyQt6-6.7+-green.svg)
49
+
50
+
49
51
 
50
52
  > Professional-grade desktop application for automated forex trading with advanced risk management, multiple trading strategies, and real-time portfolio monitoring.
51
53
 
54
+
55
+
52
56
  ## ✨ Features
53
57
 
54
58
  - **Multi-Strategy Trading**: 17+ built-in strategies including 7 ML-based approaches
@@ -60,6 +64,8 @@ Dynamic: license-file
60
64
  - **Strategy Builder**: Visual strategy construction with code generation
61
65
  - **Strategy Marketplace**: Community-driven strategy sharing platform
62
66
 
67
+
68
+
63
69
  ## 🧭 Table of Contents
64
70
 
65
71
  - [Quick Start](#-quick-start)
@@ -70,6 +76,8 @@ Dynamic: license-file
70
76
  - [Contributing](#-contributing)
71
77
  - [License](#-license)
72
78
 
79
+
80
+
73
81
  ## 🚀 Quick Start
74
82
 
75
83
  ```bash
@@ -84,12 +92,18 @@ For detailed installation instructions, see [docs/installation.md](docs/Installa
84
92
 
85
93
  ## 💿 Installation
86
94
 
95
+
96
+
87
97
  ### From PyPI (Recommended)
98
+
88
99
  ```bash
89
100
  pip install forexsmartbot
90
101
  ```
91
102
 
103
+
104
+
92
105
  ### From Source
106
+
93
107
  ```bash
94
108
  git clone https://github.com/VoxHash/ForexSmartBot.git
95
109
  cd ForexSmartBot
@@ -97,7 +111,10 @@ pip install -r requirements.txt
97
111
  python app.py
98
112
  ```
99
113
 
114
+
115
+
100
116
  ### With GPU Acceleration (Optional)
117
+
101
118
  ```bash
102
119
  # For CUDA 12.x
103
120
  pip install cupy-cuda12x
@@ -108,8 +125,22 @@ pip install cupy-cuda11x
108
125
 
109
126
  See [docs/GPU_ACCELERATION.md](docs/GPU_ACCELERATION.md) for detailed setup instructions.
110
127
 
128
+ ### With Native Rust Indicators (Optional)
129
+
130
+ Lower-memory SMA/ATR/RSI and parallel batch SMA via PyO3 (`native/fsb_core`). Requires [Rust](https://rustup.rs/) and [maturin](https://www.maturin.rs/).
131
+
132
+ ```bash
133
+ # once: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
134
+ pip install maturin
135
+ cd native/fsb_core && maturin develop --release
136
+ ```
137
+
138
+ Without the extension, the same Python APIs use a NumPy fallback automatically. See [docs/PERFORMANCE_TUNING.md](docs/PERFORMANCE_TUNING.md).
139
+
111
140
  ## 🛠 Usage
112
141
 
142
+
143
+
113
144
  ### Basic Workflow
114
145
 
115
146
  1. Launch the application: `python app.py`
@@ -122,12 +153,14 @@ For advanced usage, see [docs/usage.md](docs/QUICK_START_V3.1.0.md) and [docs/cl
122
153
 
123
154
  ## ⚙️ Configuration
124
155
 
125
- | Variable | Description | Default |
126
- |---|---|---|
127
- | `INITIAL_BALANCE` | Starting account balance | 10000.0 |
128
- | `RISK_PER_TRADE` | Risk percentage per trade | 0.02 |
129
- | `MAX_LEVERAGE` | Maximum leverage | 1:100 |
130
- | `DATA_INTERVAL` | Data update interval | 1h |
156
+
157
+ | Variable | Description | Default |
158
+ | ----------------- | ------------------------- | ------- |
159
+ | `INITIAL_BALANCE` | Starting account balance | 10000.0 |
160
+ | `RISK_PER_TRADE` | Risk percentage per trade | 0.02 |
161
+ | `MAX_LEVERAGE` | Maximum leverage | 1:100 |
162
+ | `DATA_INTERVAL` | Data update interval | 1h |
163
+
131
164
 
132
165
  Full configuration reference: [docs/Configuration-Guide.md](docs/Configuration-Guide.md)
133
166
 
@@ -164,7 +197,7 @@ This project is licensed under the terms in [LICENSE](LICENSE).
164
197
  - **Documentation**: [docs/](docs/)
165
198
  - **FAQ**: [docs/FAQ.md](docs/FAQ.md)
166
199
  - **Issues**: [GitHub Issues](https://github.com/VoxHash/ForexSmartBot/issues)
167
- - **Email**: contact@voxhash.dev
200
+ - **Email**: [contact@voxhash.dev](mailto:contact@voxhash.dev)
168
201
 
169
202
  See [SUPPORT.md](SUPPORT.md) for more support options.
170
203
 
@@ -1,12 +1,16 @@
1
1
  # ForexSmartBot
2
2
 
3
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
- [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
- [![PyQt6](https://img.shields.io/badge/PyQt6-6.7+-green.svg)](https://www.riverbankcomputing.com/software/pyqt/)
6
- [![Version](https://img.shields.io/badge/version-3.2.0-blue.svg)](https://github.com/VoxHash/ForexSmartBot/releases)
3
+ ![PyPI version](https://img.shields.io/pypi/v/forexsmartbot.svg)
4
+ ![Python Versions](https://img.shields.io/pypi/pyversions/forexsmartbot.svg)
5
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
6
+ ![PyQt6](https://img.shields.io/badge/PyQt6-6.7+-green.svg)
7
+
8
+
7
9
 
8
10
  > Professional-grade desktop application for automated forex trading with advanced risk management, multiple trading strategies, and real-time portfolio monitoring.
9
11
 
12
+
13
+
10
14
  ## ✨ Features
11
15
 
12
16
  - **Multi-Strategy Trading**: 17+ built-in strategies including 7 ML-based approaches
@@ -18,6 +22,8 @@
18
22
  - **Strategy Builder**: Visual strategy construction with code generation
19
23
  - **Strategy Marketplace**: Community-driven strategy sharing platform
20
24
 
25
+
26
+
21
27
  ## 🧭 Table of Contents
22
28
 
23
29
  - [Quick Start](#-quick-start)
@@ -28,6 +34,8 @@
28
34
  - [Contributing](#-contributing)
29
35
  - [License](#-license)
30
36
 
37
+
38
+
31
39
  ## 🚀 Quick Start
32
40
 
33
41
  ```bash
@@ -42,12 +50,18 @@ For detailed installation instructions, see [docs/installation.md](docs/Installa
42
50
 
43
51
  ## 💿 Installation
44
52
 
53
+
54
+
45
55
  ### From PyPI (Recommended)
56
+
46
57
  ```bash
47
58
  pip install forexsmartbot
48
59
  ```
49
60
 
61
+
62
+
50
63
  ### From Source
64
+
51
65
  ```bash
52
66
  git clone https://github.com/VoxHash/ForexSmartBot.git
53
67
  cd ForexSmartBot
@@ -55,7 +69,10 @@ pip install -r requirements.txt
55
69
  python app.py
56
70
  ```
57
71
 
72
+
73
+
58
74
  ### With GPU Acceleration (Optional)
75
+
59
76
  ```bash
60
77
  # For CUDA 12.x
61
78
  pip install cupy-cuda12x
@@ -66,8 +83,22 @@ pip install cupy-cuda11x
66
83
 
67
84
  See [docs/GPU_ACCELERATION.md](docs/GPU_ACCELERATION.md) for detailed setup instructions.
68
85
 
86
+ ### With Native Rust Indicators (Optional)
87
+
88
+ Lower-memory SMA/ATR/RSI and parallel batch SMA via PyO3 (`native/fsb_core`). Requires [Rust](https://rustup.rs/) and [maturin](https://www.maturin.rs/).
89
+
90
+ ```bash
91
+ # once: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
92
+ pip install maturin
93
+ cd native/fsb_core && maturin develop --release
94
+ ```
95
+
96
+ Without the extension, the same Python APIs use a NumPy fallback automatically. See [docs/PERFORMANCE_TUNING.md](docs/PERFORMANCE_TUNING.md).
97
+
69
98
  ## 🛠 Usage
70
99
 
100
+
101
+
71
102
  ### Basic Workflow
72
103
 
73
104
  1. Launch the application: `python app.py`
@@ -80,12 +111,14 @@ For advanced usage, see [docs/usage.md](docs/QUICK_START_V3.1.0.md) and [docs/cl
80
111
 
81
112
  ## ⚙️ Configuration
82
113
 
83
- | Variable | Description | Default |
84
- |---|---|---|
85
- | `INITIAL_BALANCE` | Starting account balance | 10000.0 |
86
- | `RISK_PER_TRADE` | Risk percentage per trade | 0.02 |
87
- | `MAX_LEVERAGE` | Maximum leverage | 1:100 |
88
- | `DATA_INTERVAL` | Data update interval | 1h |
114
+
115
+ | Variable | Description | Default |
116
+ | ----------------- | ------------------------- | ------- |
117
+ | `INITIAL_BALANCE` | Starting account balance | 10000.0 |
118
+ | `RISK_PER_TRADE` | Risk percentage per trade | 0.02 |
119
+ | `MAX_LEVERAGE` | Maximum leverage | 1:100 |
120
+ | `DATA_INTERVAL` | Data update interval | 1h |
121
+
89
122
 
90
123
  Full configuration reference: [docs/Configuration-Guide.md](docs/Configuration-Guide.md)
91
124
 
@@ -122,7 +155,7 @@ This project is licensed under the terms in [LICENSE](LICENSE).
122
155
  - **Documentation**: [docs/](docs/)
123
156
  - **FAQ**: [docs/FAQ.md](docs/FAQ.md)
124
157
  - **Issues**: [GitHub Issues](https://github.com/VoxHash/ForexSmartBot/issues)
125
- - **Email**: contact@voxhash.dev
158
+ - **Email**: [contact@voxhash.dev](mailto:contact@voxhash.dev)
126
159
 
127
160
  See [SUPPORT.md](SUPPORT.md) for more support options.
128
161
 
@@ -132,4 +165,4 @@ This application is for educational and legitimate business purposes only. Users
132
165
 
133
166
  ---
134
167
 
135
- Made with ❤️ by [VoxHash Technologies](https://voxhash.dev)
168
+ Made with ❤️ by [VoxHash Technologies](https://voxhash.dev)
@@ -1,5 +1,4 @@
1
1
  import pandas as pd
2
- import numpy as np
3
2
 
4
3
  class Strategy:
5
4
  """Simple trend/volatility aware strategy:
@@ -13,14 +12,8 @@ class Strategy:
13
12
  self.atr_window = atr_window
14
13
 
15
14
  def indicators(self, df: pd.DataFrame) -> pd.DataFrame:
16
- out = df.copy()
17
- out['SMA_fast'] = out['Close'].rolling(self.fast).mean()
18
- out['SMA_slow'] = out['Close'].rolling(self.slow).mean()
19
- tr = np.maximum(out['High']-out['Low'],
20
- np.maximum(abs(out['High']-out['Close'].shift(1)),
21
- abs(out['Low']-out['Close'].shift(1))))
22
- out['ATR'] = tr.rolling(self.atr_window).mean()
23
- return out
15
+ from ..utils.native_indicators import apply_sma_atr_indicators
16
+ return apply_sma_atr_indicators(df, self.fast, self.slow, self.atr_window)
24
17
 
25
18
  def signal(self, df: pd.DataFrame) -> int:
26
19
  if len(df) < max(self.fast, self.slow) + 2:
@@ -68,27 +68,36 @@ class CalculationEngine:
68
68
  columns=returns_df.columns)
69
69
 
70
70
  @staticmethod
71
- @jit(nopython=True)
72
71
  def fast_rolling_mean(values: np.ndarray, window: int) -> np.ndarray:
72
+ """Fast rolling mean: prefer Rust ``fsb_core``, else Numba, else NumPy."""
73
+ from ..utils.native_indicators import native_available, sma
74
+
75
+ if native_available():
76
+ return sma(values, window)
77
+ return CalculationEngine._numba_rolling_mean(np.asarray(values, dtype=np.float64), window)
78
+
79
+ @staticmethod
80
+ @jit(nopython=True)
81
+ def _numba_rolling_mean(values: np.ndarray, window: int) -> np.ndarray:
73
82
  """Fast rolling mean calculation using numba."""
74
83
  n = len(values)
75
84
  result = np.full(n, np.nan)
76
-
85
+
77
86
  for i in range(window - 1, n):
78
87
  result[i] = np.mean(values[i - window + 1:i + 1])
79
-
88
+
80
89
  return result
81
-
90
+
82
91
  @staticmethod
83
92
  @jit(nopython=True)
84
93
  def fast_rolling_std(values: np.ndarray, window: int) -> np.ndarray:
85
94
  """Fast rolling standard deviation calculation."""
86
95
  n = len(values)
87
96
  result = np.full(n, np.nan)
88
-
97
+
89
98
  for i in range(window - 1, n):
90
99
  result[i] = np.std(values[i - window + 1:i + 1])
91
-
100
+
92
101
  return result
93
102
 
94
103
  @lru_cache(maxsize=128)
@@ -110,10 +110,23 @@ RISK_LEVELS = {
110
110
  }
111
111
 
112
112
  def get_strategy(name: str, **params):
113
- """Get strategy instance by name."""
113
+ """Get strategy instance by name.
114
+
115
+ ``Multi_Timeframe`` accepts an optional ``base_strategy`` (instance or registry
116
+ name). When omitted, it defaults to ``SMA_Crossover`` so UI / backtest call
117
+ sites can use ``get_strategy('Multi_Timeframe')`` with no extra kwargs.
118
+ """
114
119
  if name not in STRATEGIES:
115
120
  raise ValueError(f"Unknown strategy: {name}")
116
- return STRATEGIES[name](**params)
121
+ cls = STRATEGIES[name]
122
+ if name == 'Multi_Timeframe':
123
+ base = params.pop('base_strategy', None)
124
+ if isinstance(base, str):
125
+ if base == 'Multi_Timeframe':
126
+ raise ValueError("Multi_Timeframe cannot wrap itself as base_strategy")
127
+ base = get_strategy(base)
128
+ return cls(base_strategy=base, **params)
129
+ return cls(**params)
117
130
 
118
131
  def list_strategies():
119
132
  """List available strategies."""
@@ -2,30 +2,51 @@
2
2
 
3
3
  import pandas as pd
4
4
  import numpy as np
5
- from typing import Dict, Any, Optional, List
6
- from ..core.interfaces import IStrategy
7
- from ..adapters.data import IDataProvider
5
+ from typing import Dict, Any, Optional, List, Union
6
+ from ..core.interfaces import IStrategy, IDataProvider
8
7
 
9
8
 
10
9
  class MultiTimeframeStrategy(IStrategy):
11
10
  """Strategy that analyzes multiple timeframes simultaneously."""
12
11
 
13
- def __init__(self, base_strategy: IStrategy, timeframes: List[str] = ['1h', '4h', '1d'],
14
- timeframe_weights: Optional[Dict[str, float]] = None):
12
+ def __init__(
13
+ self,
14
+ base_strategy: Optional[Union[IStrategy, str]] = None,
15
+ timeframes: Optional[List[str]] = None,
16
+ timeframe_weights: Optional[Dict[str, float]] = None,
17
+ ):
15
18
  """
16
19
  Initialize multi-timeframe strategy.
17
20
 
18
21
  Args:
19
- base_strategy: Base strategy to apply on each timeframe
22
+ base_strategy: Base strategy instance, registry name, or None for SMA_Crossover
20
23
  timeframes: List of timeframes to analyze (e.g., ['1h', '4h', '1d'])
21
24
  timeframe_weights: Optional weights for each timeframe (default: equal weights)
22
25
  """
23
- self._base_strategy = base_strategy
24
- self._timeframes = timeframes
25
- self._timeframe_weights = timeframe_weights or {tf: 1.0/len(timeframes) for tf in timeframes}
26
- self._name = f"Multi-Timeframe {base_strategy.name}"
26
+ if timeframes is None:
27
+ timeframes = ['1h', '4h', '1d']
28
+ self._base_strategy = self._resolve_base_strategy(base_strategy)
29
+ self._timeframes = list(timeframes)
30
+ self._timeframe_weights = timeframe_weights or {
31
+ tf: 1.0 / len(self._timeframes) for tf in self._timeframes
32
+ }
33
+ self._name = f"Multi-Timeframe {self._base_strategy.name}"
27
34
  self._data_provider = None
28
35
  self._timeframe_data = {}
36
+
37
+ @staticmethod
38
+ def _resolve_base_strategy(base_strategy: Optional[Union[IStrategy, str]]) -> IStrategy:
39
+ """Resolve base strategy from instance, registry name, or default SMA crossover."""
40
+ if base_strategy is None:
41
+ from .sma_crossover import SMACrossover
42
+ return SMACrossover()
43
+ if isinstance(base_strategy, str):
44
+ if base_strategy == 'Multi_Timeframe':
45
+ raise ValueError("Multi_Timeframe cannot wrap itself as base_strategy")
46
+ # Lazy import to avoid circular registry import at module load
47
+ from . import get_strategy
48
+ return get_strategy(base_strategy)
49
+ return base_strategy
29
50
 
30
51
  @property
31
52
  def name(self) -> str:
@@ -104,8 +125,13 @@ class MultiTimeframeStrategy(IStrategy):
104
125
  if len(df) < 2:
105
126
  return 0.0
106
127
 
107
- # Use SMA crossover as trend indicator
108
- if 'SMA_20' in df.columns and 'SMA_50' in df.columns:
128
+ # Prefer SMA_fast/SMA_slow (SMACrossover); fall back to SMA_20/SMA_50 naming
129
+ if 'SMA_fast' in df.columns and 'SMA_slow' in df.columns:
130
+ sma_fast = df['SMA_fast'].iloc[-1]
131
+ sma_slow = df['SMA_slow'].iloc[-1]
132
+ if pd.notna(sma_fast) and pd.notna(sma_slow):
133
+ return 1.0 if sma_fast > sma_slow else -1.0
134
+ elif 'SMA_20' in df.columns and 'SMA_50' in df.columns:
109
135
  sma_fast = df['SMA_20'].iloc[-1]
110
136
  sma_slow = df['SMA_50'].iloc[-1]
111
137
  if pd.notna(sma_fast) and pd.notna(sma_slow):
@@ -1,7 +1,6 @@
1
1
  """SMA Crossover strategy."""
2
2
 
3
3
  import pandas as pd
4
- import numpy as np
5
4
  from typing import Dict, Any, Optional
6
5
  from ..core.interfaces import IStrategy
7
6
 
@@ -36,24 +35,11 @@ class SMACrossover(IStrategy):
36
35
  self._atr_period = int(kwargs['atr_period'])
37
36
 
38
37
  def indicators(self, df: pd.DataFrame) -> pd.DataFrame:
39
- """Calculate SMA and ATR indicators."""
40
- out = df.copy()
41
-
42
- # SMAs
43
- out['SMA_fast'] = out['Close'].rolling(self._fast_period).mean()
44
- out['SMA_slow'] = out['Close'].rolling(self._slow_period).mean()
45
-
46
- # ATR
47
- tr = np.maximum(
48
- out['High'] - out['Low'],
49
- np.maximum(
50
- abs(out['High'] - out['Close'].shift(1)),
51
- abs(out['Low'] - out['Close'].shift(1))
52
- )
38
+ """Calculate SMA and ATR indicators (Rust ``fsb_core`` when available)."""
39
+ from ..utils.native_indicators import apply_sma_atr_indicators
40
+ return apply_sma_atr_indicators(
41
+ df, self._fast_period, self._slow_period, self._atr_period
53
42
  )
54
- out['ATR'] = tr.rolling(self._atr_period).mean()
55
-
56
- return out
57
43
 
58
44
  def signal(self, df: pd.DataFrame) -> int:
59
45
  """Generate trading signal based on SMA crossover."""
@@ -0,0 +1,200 @@
1
+ """Native (Rust/PyO3) indicator helpers with pure-Python/NumPy fallback.
2
+
3
+ Build optional extension from ``native/fsb_core``::
4
+
5
+ cd native/fsb_core && maturin develop --release
6
+
7
+ When ``fsb_core`` is not installed, callers still get correct results via NumPy.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import List, Optional, Sequence
13
+
14
+ import numpy as np
15
+ import pandas as pd
16
+
17
+ _NATIVE = None
18
+ _NATIVE_ERR: Optional[str] = None
19
+
20
+ try:
21
+ import fsb_core as _NATIVE # type: ignore
22
+ except Exception as exc: # pragma: no cover - optional dependency
23
+ _NATIVE_ERR = str(exc)
24
+ _NATIVE = None
25
+
26
+
27
+ def native_available() -> bool:
28
+ """Return True if the Rust ``fsb_core`` extension is importable."""
29
+ return _NATIVE is not None
30
+
31
+
32
+ def native_status() -> str:
33
+ """Human-readable status for logs / settings."""
34
+ if _NATIVE is not None:
35
+ ver = getattr(_NATIVE, "__version__", "?")
36
+ return f"fsb_core {ver} (Rust)"
37
+ return f"unavailable ({_NATIVE_ERR or 'not built'})"
38
+
39
+
40
+ def _as_f64(values: Sequence[float] | np.ndarray | pd.Series) -> np.ndarray:
41
+ arr = np.asarray(values, dtype=np.float64)
42
+ if arr.ndim != 1:
43
+ raise ValueError("values must be 1-D")
44
+ return arr
45
+
46
+
47
+ def sma(values: Sequence[float] | np.ndarray | pd.Series, window: int) -> np.ndarray:
48
+ """Simple moving average; leading values are NaN until ``window`` fills."""
49
+ if window < 1:
50
+ raise ValueError("window must be >= 1")
51
+ arr = _as_f64(values)
52
+ if _NATIVE is not None:
53
+ return np.asarray(_NATIVE.sma(arr.tolist(), int(window)), dtype=np.float64)
54
+ out = np.full(arr.shape[0], np.nan, dtype=np.float64)
55
+ if arr.shape[0] < window:
56
+ return out
57
+ csum = np.cumsum(arr, dtype=np.float64)
58
+ out[window - 1 :] = (csum[window - 1 :] - np.concatenate(([0.0], csum[:-window]))) / window
59
+ return out
60
+
61
+
62
+ def atr(
63
+ high: Sequence[float] | np.ndarray | pd.Series,
64
+ low: Sequence[float] | np.ndarray | pd.Series,
65
+ close: Sequence[float] | np.ndarray | pd.Series,
66
+ period: int,
67
+ ) -> np.ndarray:
68
+ """Average True Range (simple mean of true range)."""
69
+ if period < 1:
70
+ raise ValueError("period must be >= 1")
71
+ h = _as_f64(high)
72
+ l = _as_f64(low)
73
+ c = _as_f64(close)
74
+ if not (h.shape == l.shape == c.shape):
75
+ raise ValueError("high, low, and close must have the same length")
76
+ if _NATIVE is not None:
77
+ return np.asarray(
78
+ _NATIVE.atr(h.tolist(), l.tolist(), c.tolist(), int(period)),
79
+ dtype=np.float64,
80
+ )
81
+ prev_close = np.roll(c, 1)
82
+ prev_close[0] = c[0]
83
+ tr = np.maximum(h - l, np.maximum(np.abs(h - prev_close), np.abs(l - prev_close)))
84
+ tr[0] = h[0] - l[0]
85
+ return sma(tr, period)
86
+
87
+
88
+ def rsi(values: Sequence[float] | np.ndarray | pd.Series, period: int) -> np.ndarray:
89
+ """Relative Strength Index using simple rolling means of gains/losses."""
90
+ if period < 1:
91
+ raise ValueError("period must be >= 1")
92
+ arr = _as_f64(values)
93
+ if _NATIVE is not None:
94
+ return np.asarray(_NATIVE.rsi(arr.tolist(), int(period)), dtype=np.float64)
95
+ n = arr.shape[0]
96
+ out = np.full(n, np.nan, dtype=np.float64)
97
+ if n < period + 1:
98
+ return out
99
+ delta = np.diff(arr, prepend=arr[0])
100
+ delta[0] = 0.0
101
+ gains = np.where(delta > 0, delta, 0.0)
102
+ losses = np.where(delta < 0, -delta, 0.0)
103
+ avg_gain = sma(gains, period)
104
+ avg_loss = sma(losses, period)
105
+ for i in range(n):
106
+ g, loss = avg_gain[i], avg_loss[i]
107
+ if np.isnan(g) or np.isnan(loss):
108
+ continue
109
+ if loss == 0.0:
110
+ out[i] = 50.0 if g == 0.0 else 100.0
111
+ else:
112
+ rs = g / loss
113
+ out[i] = 100.0 - (100.0 / (1.0 + rs))
114
+ return out
115
+
116
+
117
+ def batch_sma(
118
+ series_list: List[Sequence[float] | np.ndarray | pd.Series],
119
+ window: int,
120
+ ) -> List[np.ndarray]:
121
+ """SMA over many series; uses rayon inside ``fsb_core`` when available."""
122
+ if window < 1:
123
+ raise ValueError("window must be >= 1")
124
+ prepared = [_as_f64(s).tolist() for s in series_list]
125
+ if _NATIVE is not None:
126
+ return [np.asarray(row, dtype=np.float64) for row in _NATIVE.batch_sma(prepared, int(window))]
127
+ return [sma(s, window) for s in prepared]
128
+
129
+
130
+ def equity_sma_crossover(
131
+ close: Sequence[float] | np.ndarray | pd.Series,
132
+ sma_fast: Sequence[float] | np.ndarray | pd.Series,
133
+ sma_slow: Sequence[float] | np.ndarray | pd.Series,
134
+ initial_balance: float = 10_000.0,
135
+ position_fraction: float = 0.1,
136
+ ) -> np.ndarray:
137
+ """Compact equity curve for SMA crossover (native loop when available)."""
138
+ c = _as_f64(close)
139
+ f = _as_f64(sma_fast)
140
+ s = _as_f64(sma_slow)
141
+ if not (c.shape == f.shape == s.shape):
142
+ raise ValueError("close, sma_fast, and sma_slow lengths must match")
143
+ if _NATIVE is not None:
144
+ return np.asarray(
145
+ _NATIVE.equity_sma_crossover(
146
+ c.tolist(),
147
+ f.tolist(),
148
+ s.tolist(),
149
+ float(initial_balance),
150
+ float(position_fraction),
151
+ ),
152
+ dtype=np.float64,
153
+ )
154
+ n = c.shape[0]
155
+ balance = float(initial_balance)
156
+ side = 0
157
+ size = 0.0
158
+ entry = 0.0
159
+ equity = np.empty(n, dtype=np.float64)
160
+ for i in range(n):
161
+ px = float(c[i])
162
+ if i >= 1:
163
+ fi, si, pf, ps = float(f[i]), float(s[i]), float(f[i - 1]), float(s[i - 1])
164
+ if not any(map(np.isnan, (fi, si, pf, ps))):
165
+ cross_up = fi > si and pf <= ps
166
+ cross_dn = fi < si and pf >= ps
167
+ if cross_up and side <= 0:
168
+ if side < 0:
169
+ balance += -size * (px - entry)
170
+ side = 0
171
+ size = 0.0
172
+ size = (balance * position_fraction) / px
173
+ entry = px
174
+ side = 1
175
+ elif cross_dn and side >= 0:
176
+ if side > 0:
177
+ balance += size * (px - entry)
178
+ side = 0
179
+ size = 0.0
180
+ size = (balance * position_fraction) / px
181
+ entry = px
182
+ side = -1
183
+ mtm = side * size * (px - entry) if side else 0.0
184
+ equity[i] = balance + mtm
185
+ return equity
186
+
187
+
188
+ def apply_sma_atr_indicators(
189
+ df: pd.DataFrame,
190
+ fast_period: int,
191
+ slow_period: int,
192
+ atr_period: int,
193
+ ) -> pd.DataFrame:
194
+ """Fill ``SMA_fast``, ``SMA_slow``, and ``ATR`` columns (native when possible)."""
195
+ out = df.copy()
196
+ close = out["Close"]
197
+ out["SMA_fast"] = sma(close, fast_period)
198
+ out["SMA_slow"] = sma(close, slow_period)
199
+ out["ATR"] = atr(out["High"], out["Low"], close, atr_period)
200
+ return out