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.
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/PKG-INFO +46 -13
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/README.md +45 -12
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/strategy.py +2 -9
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/calculation_engine.py +15 -6
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/__init__.py +15 -2
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/multi_timeframe_strategy.py +38 -12
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/sma_crossover.py +4 -18
- forexsmartbot-3.2.1/forexsmartbot/utils/native_indicators.py +200 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/PKG-INFO +46 -13
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/SOURCES.txt +7 -1
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/requires.txt +2 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/pyproject.toml +2 -2
- forexsmartbot-3.2.1/tests/test_config.py +48 -0
- forexsmartbot-3.2.1/tests/test_multi_timeframe_strategy.py +119 -0
- forexsmartbot-3.2.1/tests/test_native_indicators.py +109 -0
- forexsmartbot-3.2.1/tests/test_risk.py +121 -0
- forexsmartbot-3.2.1/tests/test_strategies.py +129 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/LICENSE +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/ib_tws_broker.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/mt4_broker.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/paper_broker.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/brokers/rest_broker.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/alpha_vantage_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/config.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/csv_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/dummy_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/mt4_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/multi_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/oanda_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/stooq_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/twelve_data_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/adapters/data/yfinance_provider.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/chart_patterns.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/correlation_matrix.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/economic_calendar.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/enhanced_chart.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/market_depth.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/performance_attribution.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/portfolio_analytics.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/risk_analytics.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/strategy_comparison.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/analytics/trade_journal.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/brokers/mt4_bridge.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/code_generator.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/strategy_builder.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/builder/strategy_template.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/api_client.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/api_server.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/cloud_sync.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/error_handler.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/mobile_api.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/remote_monitor.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/cloud/security.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/backtester.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/config.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/interfaces.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/paper_broker.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/portfolio.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/risk.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/risk_engine.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/core/trade_manager.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/gui/main_window.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/gui/theme.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/marketplace/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/marketplace/marketplace.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/marketplace/strategy_rating.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/health_check.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/performance_tracker.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/monitoring/strategy_monitor.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/adaptive_parameters.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/genetic_optimizer.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/hyperparameter_optimizer.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/monte_carlo.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/multi_objective_optimizer.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/parameter_sensitivity.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/optimization/walk_forward.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/backtest.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/controller.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/data_streaming.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/enhanced_backtest.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/fast_trading_controller.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/gpu_backtest.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/language_manager.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/notification_service.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/persistence.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/services/startup_manager.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/adaptive_trend_flow.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/breakout_atr.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/ensemble_ml_strategy.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/fear_index.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/lstm_strategy.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/mean_reversion.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/ml_adaptive_supertrend.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/momentum_breakout.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/news_trading.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/rl_strategy.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/rsi_reversion.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/scalping_ma.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/svm_strategy.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/transformer_strategy.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/ci_cd_pipeline.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/paper_account_tester.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/testing/strategy_sandbox.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/analytics_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/analytics_widget.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/backtest_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/charts.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/cloud_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/enhanced_charts.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/enhanced_main_window.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/export_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/main_window.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/marketplace_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/monitoring_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/monitoring_widget.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/optimization_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/settings_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/simple_chart.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/strategy_builder_dialog.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/theme.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/ui/tradingview_chart.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/utils/__init__.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/utils/gpu_numpy.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/utils/gpu_utils.py +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/dependency_links.txt +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/entry_points.txt +0 -0
- {forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot.egg-info/top_level.txt +0 -0
- {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.
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+

|
|
46
|
+

|
|
47
|
+

|
|
48
|
+

|
|
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
|
-
|
|
126
|
-
|
|
127
|
-
|
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+

|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
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
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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."""
|
{forexsmartbot-3.2.0 → forexsmartbot-3.2.1}/forexsmartbot/strategies/multi_timeframe_strategy.py
RENAMED
|
@@ -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__(
|
|
14
|
-
|
|
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
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
self.
|
|
26
|
-
self.
|
|
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
|
-
#
|
|
108
|
-
if '
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|