deltafq 0.1.0__tar.gz → 0.1.2__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 deltafq might be problematic. Click here for more details.
- {deltafq-0.1.0 → deltafq-0.1.2}/LICENSE +21 -22
- deltafq-0.1.2/PKG-INFO +89 -0
- deltafq-0.1.2/README.md +50 -0
- deltafq-0.1.2/deltafq/__init__.py +30 -0
- deltafq-0.1.2/deltafq/backtest/__init__.py +17 -0
- deltafq-0.1.2/deltafq/backtest/engine.py +99 -0
- deltafq-0.1.2/deltafq/backtest/metrics.py +113 -0
- deltafq-0.1.2/deltafq/backtest/performance.py +81 -0
- deltafq-0.1.2/deltafq/backtest/reporter.py +91 -0
- deltafq-0.1.2/deltafq/core/__init__.py +19 -0
- deltafq-0.1.2/deltafq/core/base.py +37 -0
- deltafq-0.1.2/deltafq/core/config.py +63 -0
- deltafq-0.1.2/deltafq/core/exceptions.py +35 -0
- deltafq-0.1.2/deltafq/core/logger.py +46 -0
- deltafq-0.1.2/deltafq/data/__init__.py +17 -0
- deltafq-0.1.2/deltafq/data/cleaner.py +41 -0
- deltafq-0.1.2/deltafq/data/fetcher.py +52 -0
- deltafq-0.1.2/deltafq/data/storage.py +56 -0
- deltafq-0.1.2/deltafq/data/validator.py +52 -0
- deltafq-0.1.2/deltafq/indicators/__init__.py +17 -0
- deltafq-0.1.2/deltafq/indicators/momentum.py +56 -0
- deltafq-0.1.2/deltafq/indicators/technical.py +59 -0
- deltafq-0.1.2/deltafq/indicators/trend.py +129 -0
- deltafq-0.1.2/deltafq/indicators/volatility.py +67 -0
- deltafq-0.1.2/deltafq/live/__init__.py +17 -0
- deltafq-0.1.2/deltafq/live/connection.py +235 -0
- deltafq-0.1.2/deltafq/live/data_feed.py +159 -0
- deltafq-0.1.2/deltafq/live/monitoring.py +192 -0
- deltafq-0.1.2/deltafq/live/risk_control.py +193 -0
- deltafq-0.1.2/deltafq/strategy/__init__.py +17 -0
- deltafq-0.1.2/deltafq/strategy/base_strategy.py +53 -0
- deltafq-0.1.2/deltafq/strategy/portfolio.py +82 -0
- deltafq-0.1.2/deltafq/strategy/risk_manager.py +64 -0
- deltafq-0.1.2/deltafq/strategy/signal_generator.py +52 -0
- deltafq-0.1.2/deltafq/trading/__init__.py +19 -0
- deltafq-0.1.2/deltafq/trading/broker.py +119 -0
- deltafq-0.1.2/deltafq/trading/execution.py +176 -0
- deltafq-0.1.2/deltafq/trading/order_manager.py +111 -0
- deltafq-0.1.2/deltafq/trading/position_manager.py +157 -0
- deltafq-0.1.2/deltafq/trading/simulator.py +150 -0
- deltafq-0.1.2/deltafq.egg-info/PKG-INFO +89 -0
- deltafq-0.1.2/deltafq.egg-info/SOURCES.txt +48 -0
- deltafq-0.1.2/deltafq.egg-info/entry_points.txt +2 -0
- deltafq-0.1.2/deltafq.egg-info/requires.txt +15 -0
- deltafq-0.1.2/pyproject.toml +104 -0
- deltafq-0.1.2/setup.py +10 -0
- deltafq-0.1.2/test/test_basic.py +169 -0
- deltafq-0.1.0/MANIFEST.in +0 -7
- deltafq-0.1.0/PKG-INFO +0 -195
- deltafq-0.1.0/README.md +0 -156
- deltafq-0.1.0/deltafq/__init__.py +0 -31
- deltafq-0.1.0/deltafq/backtest/__init__.py +0 -7
- deltafq-0.1.0/deltafq/backtest/engine.py +0 -52
- deltafq-0.1.0/deltafq/backtest/result.py +0 -45
- deltafq-0.1.0/deltafq/data/__init__.py +0 -7
- deltafq-0.1.0/deltafq/data/base.py +0 -30
- deltafq-0.1.0/deltafq/data/loader.py +0 -63
- deltafq-0.1.0/deltafq/indicators/__init__.py +0 -8
- deltafq-0.1.0/deltafq/indicators/momentum.py +0 -23
- deltafq-0.1.0/deltafq/indicators/trend.py +0 -61
- deltafq-0.1.0/deltafq/indicators/volatility.py +0 -27
- deltafq-0.1.0/deltafq/optimization/__init__.py +0 -6
- deltafq-0.1.0/deltafq/optimization/grid_search.py +0 -41
- deltafq-0.1.0/deltafq/performance/__init__.py +0 -6
- deltafq-0.1.0/deltafq/performance/metrics.py +0 -37
- deltafq-0.1.0/deltafq/risk/__init__.py +0 -7
- deltafq-0.1.0/deltafq/risk/metrics.py +0 -33
- deltafq-0.1.0/deltafq/risk/position.py +0 -39
- deltafq-0.1.0/deltafq/strategy/__init__.py +0 -6
- deltafq-0.1.0/deltafq/strategy/base.py +0 -44
- deltafq-0.1.0/deltafq/trade/__init__.py +0 -6
- deltafq-0.1.0/deltafq/trade/broker.py +0 -40
- deltafq-0.1.0/deltafq/utils/__init__.py +0 -6
- deltafq-0.1.0/deltafq/utils/time.py +0 -32
- deltafq-0.1.0/deltafq.egg-info/PKG-INFO +0 -195
- deltafq-0.1.0/deltafq.egg-info/SOURCES.txt +0 -39
- deltafq-0.1.0/deltafq.egg-info/requires.txt +0 -15
- deltafq-0.1.0/pyproject.toml +0 -74
- deltafq-0.1.0/setup.py +0 -12
- deltafq-0.1.0/tests/test_backtest.py +0 -54
- deltafq-0.1.0/tests/test_data.py +0 -27
- deltafq-0.1.0/tests/test_full_workflow.py +0 -158
- deltafq-0.1.0/tests/test_indicators.py +0 -55
- deltafq-0.1.0/tests/test_strategy.py +0 -49
- {deltafq-0.1.0 → deltafq-0.1.2}/deltafq.egg-info/dependency_links.txt +0 -0
- {deltafq-0.1.0 → deltafq-0.1.2}/deltafq.egg-info/top_level.txt +0 -0
- {deltafq-0.1.0 → deltafq-0.1.2}/setup.cfg +0 -0
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
22
|
-
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 DeltaF
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
deltafq-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: deltafq
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: A comprehensive Python quantitative finance library
|
|
5
|
+
Author-email: DeltaF <leek_li@outlook.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Delta-F/deltafq
|
|
8
|
+
Project-URL: Repository, https://github.com/Delta-F/deltafq
|
|
9
|
+
Project-URL: Issues, https://github.com/Delta-F/deltafq/issues
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: numpy>=1.21.0
|
|
25
|
+
Requires-Dist: pandas>=1.3.0
|
|
26
|
+
Requires-Dist: scipy>=1.7.0
|
|
27
|
+
Requires-Dist: matplotlib>=3.4.0
|
|
28
|
+
Requires-Dist: seaborn>=0.11.0
|
|
29
|
+
Requires-Dist: plotly>=5.0.0
|
|
30
|
+
Requires-Dist: yfinance>=0.1.70
|
|
31
|
+
Requires-Dist: requests>=2.25.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=6.2.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=2.12.0; extra == "dev"
|
|
35
|
+
Requires-Dist: black>=21.0.0; extra == "dev"
|
|
36
|
+
Requires-Dist: flake8>=3.9.0; extra == "dev"
|
|
37
|
+
Requires-Dist: sphinx>=4.0.0; extra == "dev"
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
|
|
40
|
+
# DeltaFQ
|
|
41
|
+
|
|
42
|
+
A comprehensive Python quantitative finance library for strategy development, backtesting, and live trading.
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
- **Data Management**: Efficient data fetching, cleaning, and storage
|
|
47
|
+
- **Strategy Framework**: Flexible strategy development framework
|
|
48
|
+
- **Backtesting**: High-performance historical data backtesting
|
|
49
|
+
- **Paper Trading**: Risk-free strategy testing with simulated trading
|
|
50
|
+
- **Live Trading**: Real-time trading with broker integration
|
|
51
|
+
- **Technical Indicators**: Rich library of technical analysis indicators
|
|
52
|
+
- **Risk Management**: Built-in risk control modules
|
|
53
|
+
|
|
54
|
+
## Installation
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install deltafq
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Quick Start
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
import deltafq as dfq
|
|
64
|
+
|
|
65
|
+
# Fetch market data
|
|
66
|
+
data = dfq.data.fetch_stock_data('AAPL', start='2023-01-01')
|
|
67
|
+
|
|
68
|
+
# Create and test a strategy
|
|
69
|
+
strategy = dfq.strategy.MovingAverageStrategy(fast_period=10, slow_period=20)
|
|
70
|
+
results = dfq.backtest.run_backtest(strategy, data)
|
|
71
|
+
|
|
72
|
+
# Run paper trading
|
|
73
|
+
simulator = dfq.trading.PaperTradingSimulator(initial_capital=100000)
|
|
74
|
+
simulator.run_strategy(strategy, data)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Documentation
|
|
78
|
+
|
|
79
|
+
- [API Reference](docs/api_reference/)
|
|
80
|
+
- [Tutorials](docs/tutorials/)
|
|
81
|
+
- [Examples](examples/)
|
|
82
|
+
|
|
83
|
+
## Contributing
|
|
84
|
+
|
|
85
|
+
Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
deltafq-0.1.2/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# DeltaFQ
|
|
2
|
+
|
|
3
|
+
A comprehensive Python quantitative finance library for strategy development, backtesting, and live trading.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Data Management**: Efficient data fetching, cleaning, and storage
|
|
8
|
+
- **Strategy Framework**: Flexible strategy development framework
|
|
9
|
+
- **Backtesting**: High-performance historical data backtesting
|
|
10
|
+
- **Paper Trading**: Risk-free strategy testing with simulated trading
|
|
11
|
+
- **Live Trading**: Real-time trading with broker integration
|
|
12
|
+
- **Technical Indicators**: Rich library of technical analysis indicators
|
|
13
|
+
- **Risk Management**: Built-in risk control modules
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
pip install deltafq
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
import deltafq as dfq
|
|
25
|
+
|
|
26
|
+
# Fetch market data
|
|
27
|
+
data = dfq.data.fetch_stock_data('AAPL', start='2023-01-01')
|
|
28
|
+
|
|
29
|
+
# Create and test a strategy
|
|
30
|
+
strategy = dfq.strategy.MovingAverageStrategy(fast_period=10, slow_period=20)
|
|
31
|
+
results = dfq.backtest.run_backtest(strategy, data)
|
|
32
|
+
|
|
33
|
+
# Run paper trading
|
|
34
|
+
simulator = dfq.trading.PaperTradingSimulator(initial_capital=100000)
|
|
35
|
+
simulator.run_strategy(strategy, data)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Documentation
|
|
39
|
+
|
|
40
|
+
- [API Reference](docs/api_reference/)
|
|
41
|
+
- [Tutorials](docs/tutorials/)
|
|
42
|
+
- [Examples](examples/)
|
|
43
|
+
|
|
44
|
+
## Contributing
|
|
45
|
+
|
|
46
|
+
Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
DeltaFQ - A comprehensive Python quantitative finance library.
|
|
3
|
+
|
|
4
|
+
This library provides tools for strategy development, backtesting,
|
|
5
|
+
paper trading, and live trading.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "0.1.0"
|
|
9
|
+
__author__ = "DeltaF"
|
|
10
|
+
|
|
11
|
+
# Import core modules
|
|
12
|
+
from . import core
|
|
13
|
+
from . import data
|
|
14
|
+
from . import strategy
|
|
15
|
+
from . import backtest
|
|
16
|
+
from . import indicators
|
|
17
|
+
from . import trading
|
|
18
|
+
from . import live
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"core",
|
|
22
|
+
"data",
|
|
23
|
+
"strategy",
|
|
24
|
+
"backtest",
|
|
25
|
+
"indicators",
|
|
26
|
+
"trading",
|
|
27
|
+
"live"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backtesting module for DeltaFQ.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .engine import BacktestEngine
|
|
6
|
+
from .performance import PerformanceAnalyzer
|
|
7
|
+
from .metrics import MetricsCalculator
|
|
8
|
+
from .reporter import BacktestReporter
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"BacktestEngine",
|
|
12
|
+
"PerformanceAnalyzer",
|
|
13
|
+
"MetricsCalculator",
|
|
14
|
+
"BacktestReporter"
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backtesting engine for DeltaFQ.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from typing import Dict, Any, Optional
|
|
7
|
+
from ..core.base import BaseComponent
|
|
8
|
+
from ..core.exceptions import BacktestError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BacktestEngine(BaseComponent):
|
|
12
|
+
"""Backtesting engine for strategy testing."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, initial_capital: float = 100000, commission: float = 0.001, **kwargs):
|
|
15
|
+
"""Initialize backtest engine."""
|
|
16
|
+
super().__init__(**kwargs)
|
|
17
|
+
self.initial_capital = initial_capital
|
|
18
|
+
self.commission = commission
|
|
19
|
+
self.results = None
|
|
20
|
+
|
|
21
|
+
def initialize(self) -> bool:
|
|
22
|
+
"""Initialize backtest engine."""
|
|
23
|
+
self.logger.info(f"Initializing backtest engine with capital: {self.initial_capital}")
|
|
24
|
+
return True
|
|
25
|
+
|
|
26
|
+
def run_backtest(self, strategy, data: pd.DataFrame) -> Dict[str, Any]:
|
|
27
|
+
"""Run backtest for given strategy and data."""
|
|
28
|
+
try:
|
|
29
|
+
self.logger.info("Starting backtest")
|
|
30
|
+
|
|
31
|
+
# Initialize portfolio
|
|
32
|
+
cash = self.initial_capital
|
|
33
|
+
positions = {}
|
|
34
|
+
trades = []
|
|
35
|
+
|
|
36
|
+
# Run strategy on data
|
|
37
|
+
for i, (date, row) in enumerate(data.iterrows()):
|
|
38
|
+
# Get signals from strategy
|
|
39
|
+
signals = strategy.generate_signals(data.iloc[:i+1])
|
|
40
|
+
|
|
41
|
+
if not signals.empty and i > 0:
|
|
42
|
+
signal = signals.iloc[-1]
|
|
43
|
+
|
|
44
|
+
# Execute trades based on signals
|
|
45
|
+
if signal != 0: # Buy or sell signal
|
|
46
|
+
symbol = 'STOCK' # Simplified for single asset
|
|
47
|
+
price = row['close']
|
|
48
|
+
quantity = int(signal * cash * 0.1 / price) # Use 10% of cash
|
|
49
|
+
|
|
50
|
+
if quantity > 0 and quantity * price <= cash:
|
|
51
|
+
# Buy
|
|
52
|
+
cost = quantity * price * (1 + self.commission)
|
|
53
|
+
cash -= cost
|
|
54
|
+
positions[symbol] = positions.get(symbol, 0) + quantity
|
|
55
|
+
trades.append({
|
|
56
|
+
'date': date,
|
|
57
|
+
'symbol': symbol,
|
|
58
|
+
'quantity': quantity,
|
|
59
|
+
'price': price,
|
|
60
|
+
'type': 'buy'
|
|
61
|
+
})
|
|
62
|
+
elif quantity < 0 and positions.get(symbol, 0) >= abs(quantity):
|
|
63
|
+
# Sell
|
|
64
|
+
quantity = abs(quantity)
|
|
65
|
+
proceeds = quantity * price * (1 - self.commission)
|
|
66
|
+
cash += proceeds
|
|
67
|
+
positions[symbol] -= quantity
|
|
68
|
+
trades.append({
|
|
69
|
+
'date': date,
|
|
70
|
+
'symbol': symbol,
|
|
71
|
+
'quantity': -quantity,
|
|
72
|
+
'price': price,
|
|
73
|
+
'type': 'sell'
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
# Calculate final portfolio value
|
|
77
|
+
final_price = data['close'].iloc[-1]
|
|
78
|
+
final_value = cash + sum(positions.values()) * final_price
|
|
79
|
+
|
|
80
|
+
self.results = {
|
|
81
|
+
'initial_capital': self.initial_capital,
|
|
82
|
+
'final_value': final_value,
|
|
83
|
+
'total_return': (final_value - self.initial_capital) / self.initial_capital,
|
|
84
|
+
'trades': trades,
|
|
85
|
+
'final_positions': positions,
|
|
86
|
+
'final_cash': cash
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
self.logger.info(f"Backtest completed. Total return: {self.results['total_return']:.2%}")
|
|
90
|
+
return self.results
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
raise BacktestError(f"Backtest failed: {str(e)}")
|
|
94
|
+
|
|
95
|
+
def get_results(self) -> Optional[Dict[str, Any]]:
|
|
96
|
+
"""Get backtest results."""
|
|
97
|
+
return self.results
|
|
98
|
+
|
|
99
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backtest metrics calculation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import numpy as np
|
|
7
|
+
from typing import Dict, Any
|
|
8
|
+
from ..core.base import BaseComponent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MetricsCalculator(BaseComponent):
|
|
12
|
+
"""Calculate various backtest metrics."""
|
|
13
|
+
|
|
14
|
+
def initialize(self) -> bool:
|
|
15
|
+
"""Initialize metrics calculator."""
|
|
16
|
+
self.logger.info("Initializing metrics calculator")
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
def calculate_trade_metrics(self, trades: list) -> Dict[str, Any]:
|
|
20
|
+
"""Calculate trade-based metrics."""
|
|
21
|
+
if not trades:
|
|
22
|
+
return {}
|
|
23
|
+
|
|
24
|
+
trade_df = pd.DataFrame(trades)
|
|
25
|
+
|
|
26
|
+
# Separate buy and sell trades
|
|
27
|
+
buy_trades = trade_df[trade_df['type'] == 'buy']
|
|
28
|
+
sell_trades = trade_df[trade_df['type'] == 'sell']
|
|
29
|
+
|
|
30
|
+
# Calculate P&L for each trade (simplified)
|
|
31
|
+
pnl = []
|
|
32
|
+
for _, sell_trade in sell_trades.iterrows():
|
|
33
|
+
# Find corresponding buy trade
|
|
34
|
+
buy_trade = buy_trades[buy_trades['symbol'] == sell_trade['symbol']].iloc[0]
|
|
35
|
+
trade_pnl = (sell_trade['price'] - buy_trade['price']) * sell_trade['quantity']
|
|
36
|
+
pnl.append(trade_pnl)
|
|
37
|
+
|
|
38
|
+
if pnl:
|
|
39
|
+
pnl_series = pd.Series(pnl)
|
|
40
|
+
return {
|
|
41
|
+
'total_trades': len(pnl),
|
|
42
|
+
'winning_trades': (pnl_series > 0).sum(),
|
|
43
|
+
'losing_trades': (pnl_series < 0).sum(),
|
|
44
|
+
'win_rate': (pnl_series > 0).mean(),
|
|
45
|
+
'avg_win': pnl_series[pnl_series > 0].mean() if (pnl_series > 0).any() else 0,
|
|
46
|
+
'avg_loss': pnl_series[pnl_series < 0].mean() if (pnl_series < 0).any() else 0,
|
|
47
|
+
'total_pnl': pnl_series.sum()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {}
|
|
51
|
+
|
|
52
|
+
def calculate_risk_metrics(self, returns: pd.Series) -> Dict[str, float]:
|
|
53
|
+
"""Calculate risk-related metrics."""
|
|
54
|
+
if len(returns) == 0:
|
|
55
|
+
return {}
|
|
56
|
+
|
|
57
|
+
# Value at Risk (VaR)
|
|
58
|
+
var_95 = np.percentile(returns, 5)
|
|
59
|
+
var_99 = np.percentile(returns, 1)
|
|
60
|
+
|
|
61
|
+
# Conditional Value at Risk (CVaR)
|
|
62
|
+
cvar_95 = returns[returns <= var_95].mean()
|
|
63
|
+
cvar_99 = returns[returns <= var_99].mean()
|
|
64
|
+
|
|
65
|
+
# Skewness and Kurtosis
|
|
66
|
+
skewness = returns.skew()
|
|
67
|
+
kurtosis = returns.kurtosis()
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
'var_95': var_95,
|
|
71
|
+
'var_99': var_99,
|
|
72
|
+
'cvar_95': cvar_95,
|
|
73
|
+
'cvar_99': cvar_99,
|
|
74
|
+
'skewness': skewness,
|
|
75
|
+
'kurtosis': kurtosis
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def calculate_portfolio_metrics(self, portfolio_values: pd.Series) -> Dict[str, float]:
|
|
79
|
+
"""Calculate portfolio-level metrics."""
|
|
80
|
+
if len(portfolio_values) < 2:
|
|
81
|
+
return {}
|
|
82
|
+
|
|
83
|
+
returns = portfolio_values.pct_change().dropna()
|
|
84
|
+
|
|
85
|
+
# Rolling metrics
|
|
86
|
+
rolling_returns = returns.rolling(window=252) # Annual window
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
'total_return': (portfolio_values.iloc[-1] / portfolio_values.iloc[0]) - 1,
|
|
90
|
+
'annualized_return': (1 + returns.mean()) ** 252 - 1,
|
|
91
|
+
'volatility': returns.std() * np.sqrt(252),
|
|
92
|
+
'sharpe_ratio': returns.mean() / returns.std() * np.sqrt(252) if returns.std() > 0 else 0,
|
|
93
|
+
'max_drawdown': self._calculate_max_drawdown(portfolio_values),
|
|
94
|
+
'calmar_ratio': self._calculate_calmar_ratio(returns)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
def _calculate_max_drawdown(self, values: pd.Series) -> float:
|
|
98
|
+
"""Calculate maximum drawdown."""
|
|
99
|
+
peak = values.expanding().max()
|
|
100
|
+
drawdown = (values - peak) / peak
|
|
101
|
+
return drawdown.min()
|
|
102
|
+
|
|
103
|
+
def _calculate_calmar_ratio(self, returns: pd.Series) -> float:
|
|
104
|
+
"""Calculate Calmar ratio."""
|
|
105
|
+
annual_return = (1 + returns.mean()) ** 252 - 1
|
|
106
|
+
max_dd = abs(self._calculate_max_drawdown((1 + returns).cumprod()))
|
|
107
|
+
|
|
108
|
+
if max_dd == 0:
|
|
109
|
+
return float('inf') if annual_return > 0 else 0.0
|
|
110
|
+
|
|
111
|
+
return annual_return / max_dd
|
|
112
|
+
|
|
113
|
+
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Performance analysis for backtests.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import numpy as np
|
|
7
|
+
from typing import Dict, List, Any
|
|
8
|
+
from ..core.base import BaseComponent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PerformanceAnalyzer(BaseComponent):
|
|
12
|
+
"""Analyze backtest performance."""
|
|
13
|
+
|
|
14
|
+
def initialize(self) -> bool:
|
|
15
|
+
"""Initialize performance analyzer."""
|
|
16
|
+
self.logger.info("Initializing performance analyzer")
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
def calculate_returns(self, prices: pd.Series) -> pd.Series:
|
|
20
|
+
"""Calculate returns from price series."""
|
|
21
|
+
return prices.pct_change().dropna()
|
|
22
|
+
|
|
23
|
+
def calculate_sharpe_ratio(self, returns: pd.Series, risk_free_rate: float = 0.02) -> float:
|
|
24
|
+
"""Calculate Sharpe ratio."""
|
|
25
|
+
if len(returns) == 0 or returns.std() == 0:
|
|
26
|
+
return 0.0
|
|
27
|
+
|
|
28
|
+
excess_returns = returns - risk_free_rate / 252 # Daily risk-free rate
|
|
29
|
+
return excess_returns.mean() / returns.std() * np.sqrt(252)
|
|
30
|
+
|
|
31
|
+
def calculate_max_drawdown(self, returns: pd.Series) -> float:
|
|
32
|
+
"""Calculate maximum drawdown."""
|
|
33
|
+
cumulative = (1 + returns).cumprod()
|
|
34
|
+
running_max = cumulative.expanding().max()
|
|
35
|
+
drawdown = (cumulative - running_max) / running_max
|
|
36
|
+
return drawdown.min()
|
|
37
|
+
|
|
38
|
+
def calculate_volatility(self, returns: pd.Series) -> float:
|
|
39
|
+
"""Calculate annualized volatility."""
|
|
40
|
+
return returns.std() * np.sqrt(252)
|
|
41
|
+
|
|
42
|
+
def analyze_performance(self, returns: pd.Series, benchmark_returns: pd.Series = None) -> Dict[str, float]:
|
|
43
|
+
"""Comprehensive performance analysis."""
|
|
44
|
+
analysis = {
|
|
45
|
+
'total_return': (1 + returns).prod() - 1,
|
|
46
|
+
'annualized_return': (1 + returns).prod() ** (252 / len(returns)) - 1,
|
|
47
|
+
'volatility': self.calculate_volatility(returns),
|
|
48
|
+
'sharpe_ratio': self.calculate_sharpe_ratio(returns),
|
|
49
|
+
'max_drawdown': self.calculate_max_drawdown(returns),
|
|
50
|
+
'win_rate': (returns > 0).mean(),
|
|
51
|
+
'profit_factor': self._calculate_profit_factor(returns)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if benchmark_returns is not None:
|
|
55
|
+
analysis['alpha'] = self._calculate_alpha(returns, benchmark_returns)
|
|
56
|
+
analysis['beta'] = self._calculate_beta(returns, benchmark_returns)
|
|
57
|
+
|
|
58
|
+
return analysis
|
|
59
|
+
|
|
60
|
+
def _calculate_profit_factor(self, returns: pd.Series) -> float:
|
|
61
|
+
"""Calculate profit factor."""
|
|
62
|
+
positive_returns = returns[returns > 0].sum()
|
|
63
|
+
negative_returns = abs(returns[returns < 0].sum())
|
|
64
|
+
|
|
65
|
+
if negative_returns == 0:
|
|
66
|
+
return float('inf') if positive_returns > 0 else 0.0
|
|
67
|
+
|
|
68
|
+
return positive_returns / negative_returns
|
|
69
|
+
|
|
70
|
+
def _calculate_alpha(self, returns: pd.Series, benchmark_returns: pd.Series) -> float:
|
|
71
|
+
"""Calculate alpha."""
|
|
72
|
+
# Simplified calculation
|
|
73
|
+
return returns.mean() - benchmark_returns.mean()
|
|
74
|
+
|
|
75
|
+
def _calculate_beta(self, returns: pd.Series, benchmark_returns: pd.Series) -> float:
|
|
76
|
+
"""Calculate beta."""
|
|
77
|
+
if benchmark_returns.var() == 0:
|
|
78
|
+
return 0.0
|
|
79
|
+
return returns.cov(benchmark_returns) / benchmark_returns.var()
|
|
80
|
+
|
|
81
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Backtest report generation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
from typing import Dict, Any, Optional
|
|
7
|
+
from ..core.base import BaseComponent
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class BacktestReporter(BaseComponent):
|
|
11
|
+
"""Generate backtest reports."""
|
|
12
|
+
|
|
13
|
+
def initialize(self) -> bool:
|
|
14
|
+
"""Initialize backtest reporter."""
|
|
15
|
+
self.logger.info("Initializing backtest reporter")
|
|
16
|
+
return True
|
|
17
|
+
|
|
18
|
+
def generate_summary_report(self, results: Dict[str, Any]) -> str:
|
|
19
|
+
"""Generate a summary report."""
|
|
20
|
+
report = f"""
|
|
21
|
+
=== Backtest Summary Report ===
|
|
22
|
+
|
|
23
|
+
Initial Capital: ${results.get('initial_capital', 0):,.2f}
|
|
24
|
+
Final Value: ${results.get('final_value', 0):,.2f}
|
|
25
|
+
Total Return: {results.get('total_return', 0):.2%}
|
|
26
|
+
|
|
27
|
+
Trades:
|
|
28
|
+
- Total Trades: {len(results.get('trades', []))}
|
|
29
|
+
- Final Cash: ${results.get('final_cash', 0):,.2f}
|
|
30
|
+
- Final Positions: {results.get('final_positions', {})}
|
|
31
|
+
|
|
32
|
+
=== End of Report ===
|
|
33
|
+
"""
|
|
34
|
+
return report
|
|
35
|
+
|
|
36
|
+
def generate_detailed_report(self, results: Dict[str, Any], performance_metrics: Dict[str, float]) -> str:
|
|
37
|
+
"""Generate detailed report with performance metrics."""
|
|
38
|
+
report = f"""
|
|
39
|
+
=== Detailed Backtest Report ===
|
|
40
|
+
|
|
41
|
+
PORTFOLIO PERFORMANCE
|
|
42
|
+
====================
|
|
43
|
+
Initial Capital: ${results.get('initial_capital', 0):,.2f}
|
|
44
|
+
Final Value: ${results.get('final_value', 0):,.2f}
|
|
45
|
+
Total Return: {results.get('total_return', 0):.2%}
|
|
46
|
+
|
|
47
|
+
PERFORMANCE METRICS
|
|
48
|
+
==================
|
|
49
|
+
Annualized Return: {performance_metrics.get('annualized_return', 0):.2%}
|
|
50
|
+
Volatility: {performance_metrics.get('volatility', 0):.2%}
|
|
51
|
+
Sharpe Ratio: {performance_metrics.get('sharpe_ratio', 0):.2f}
|
|
52
|
+
Maximum Drawdown: {performance_metrics.get('max_drawdown', 0):.2%}
|
|
53
|
+
Calmar Ratio: {performance_metrics.get('calmar_ratio', 0):.2f}
|
|
54
|
+
|
|
55
|
+
TRADING METRICS
|
|
56
|
+
===============
|
|
57
|
+
Total Trades: {len(results.get('trades', []))}
|
|
58
|
+
Final Cash: ${results.get('final_cash', 0):,.2f}
|
|
59
|
+
Final Positions: {results.get('final_positions', {})}
|
|
60
|
+
|
|
61
|
+
=== End of Detailed Report ===
|
|
62
|
+
"""
|
|
63
|
+
return report
|
|
64
|
+
|
|
65
|
+
def save_report(self, report: str, filename: str) -> bool:
|
|
66
|
+
"""Save report to file."""
|
|
67
|
+
try:
|
|
68
|
+
with open(filename, 'w') as f:
|
|
69
|
+
f.write(report)
|
|
70
|
+
self.logger.info(f"Report saved to: {filename}")
|
|
71
|
+
return True
|
|
72
|
+
except Exception as e:
|
|
73
|
+
self.logger.error(f"Failed to save report: {str(e)}")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
def export_trades_to_csv(self, trades: list, filename: str) -> bool:
|
|
77
|
+
"""Export trades to CSV file."""
|
|
78
|
+
try:
|
|
79
|
+
if trades:
|
|
80
|
+
trade_df = pd.DataFrame(trades)
|
|
81
|
+
trade_df.to_csv(filename, index=False)
|
|
82
|
+
self.logger.info(f"Trades exported to: {filename}")
|
|
83
|
+
return True
|
|
84
|
+
else:
|
|
85
|
+
self.logger.warning("No trades to export")
|
|
86
|
+
return False
|
|
87
|
+
except Exception as e:
|
|
88
|
+
self.logger.error(f"Failed to export trades: {str(e)}")
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Core functionality for DeltaFQ.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .config import Config
|
|
6
|
+
from .logger import Logger
|
|
7
|
+
from .exceptions import DeltaFQError, DataError, TradingError
|
|
8
|
+
from .base import BaseComponent
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"Config",
|
|
12
|
+
"Logger",
|
|
13
|
+
"DeltaFQError",
|
|
14
|
+
"DataError",
|
|
15
|
+
"TradingError",
|
|
16
|
+
"BaseComponent"
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base classes for DeltaFQ components.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Any, Dict
|
|
7
|
+
from .logger import Logger
|
|
8
|
+
from .config import Config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseComponent(ABC):
|
|
12
|
+
"""Base class for all DeltaFQ components."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, name: str = None, config: Config = None):
|
|
15
|
+
"""Initialize base component."""
|
|
16
|
+
self.name = name or self.__class__.__name__
|
|
17
|
+
self.config = config or Config()
|
|
18
|
+
self.logger = Logger(self.name)
|
|
19
|
+
|
|
20
|
+
@abstractmethod
|
|
21
|
+
def initialize(self) -> bool:
|
|
22
|
+
"""Initialize the component."""
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def cleanup(self):
|
|
26
|
+
"""Cleanup resources."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
def get_info(self) -> Dict[str, Any]:
|
|
30
|
+
"""Get component information."""
|
|
31
|
+
return {
|
|
32
|
+
"name": self.name,
|
|
33
|
+
"class": self.__class__.__name__,
|
|
34
|
+
"config": self.config.config
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|