deltafq 0.1.0__py3-none-any.whl → 0.1.2__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (60) hide show
  1. deltafq/__init__.py +30 -31
  2. deltafq/backtest/__init__.py +17 -7
  3. deltafq/backtest/engine.py +99 -52
  4. deltafq/backtest/metrics.py +113 -0
  5. deltafq/backtest/performance.py +81 -0
  6. deltafq/backtest/reporter.py +91 -0
  7. deltafq/core/__init__.py +19 -0
  8. deltafq/core/base.py +37 -0
  9. deltafq/core/config.py +63 -0
  10. deltafq/core/exceptions.py +35 -0
  11. deltafq/core/logger.py +46 -0
  12. deltafq/data/__init__.py +17 -7
  13. deltafq/data/cleaner.py +41 -0
  14. deltafq/data/fetcher.py +52 -0
  15. deltafq/data/storage.py +56 -0
  16. deltafq/data/validator.py +52 -0
  17. deltafq/indicators/__init__.py +17 -8
  18. deltafq/indicators/momentum.py +56 -23
  19. deltafq/indicators/technical.py +59 -0
  20. deltafq/indicators/trend.py +129 -61
  21. deltafq/indicators/volatility.py +67 -27
  22. deltafq/live/__init__.py +17 -0
  23. deltafq/live/connection.py +235 -0
  24. deltafq/live/data_feed.py +159 -0
  25. deltafq/live/monitoring.py +192 -0
  26. deltafq/live/risk_control.py +193 -0
  27. deltafq/strategy/__init__.py +17 -6
  28. deltafq/strategy/base_strategy.py +53 -0
  29. deltafq/strategy/portfolio.py +82 -0
  30. deltafq/strategy/risk_manager.py +64 -0
  31. deltafq/strategy/signal_generator.py +52 -0
  32. deltafq/trading/__init__.py +19 -0
  33. deltafq/trading/broker.py +119 -0
  34. deltafq/trading/execution.py +176 -0
  35. deltafq/trading/order_manager.py +111 -0
  36. deltafq/trading/position_manager.py +157 -0
  37. deltafq/trading/simulator.py +150 -0
  38. deltafq-0.1.2.dist-info/METADATA +110 -0
  39. deltafq-0.1.2.dist-info/RECORD +43 -0
  40. deltafq-0.1.2.dist-info/entry_points.txt +2 -0
  41. {deltafq-0.1.0.dist-info → deltafq-0.1.2.dist-info}/licenses/LICENSE +21 -22
  42. deltafq/backtest/result.py +0 -45
  43. deltafq/data/base.py +0 -30
  44. deltafq/data/loader.py +0 -63
  45. deltafq/optimization/__init__.py +0 -6
  46. deltafq/optimization/grid_search.py +0 -41
  47. deltafq/performance/__init__.py +0 -6
  48. deltafq/performance/metrics.py +0 -37
  49. deltafq/risk/__init__.py +0 -7
  50. deltafq/risk/metrics.py +0 -33
  51. deltafq/risk/position.py +0 -39
  52. deltafq/strategy/base.py +0 -44
  53. deltafq/trade/__init__.py +0 -6
  54. deltafq/trade/broker.py +0 -40
  55. deltafq/utils/__init__.py +0 -6
  56. deltafq/utils/time.py +0 -32
  57. deltafq-0.1.0.dist-info/METADATA +0 -195
  58. deltafq-0.1.0.dist-info/RECORD +0 -29
  59. {deltafq-0.1.0.dist-info → deltafq-0.1.2.dist-info}/WHEEL +0 -0
  60. {deltafq-0.1.0.dist-info → deltafq-0.1.2.dist-info}/top_level.txt +0 -0
@@ -1,27 +1,67 @@
1
- """波动率类指标"""
2
-
3
- import pandas as pd
4
-
5
-
6
- def BOLL(data: pd.Series, period: int = 20, std_dev: float = 2.0) -> pd.DataFrame:
7
- """布林带
8
-
9
- Args:
10
- data: 价格序列
11
- period: 周期
12
- std_dev: 标准差倍数
13
-
14
- Returns:
15
- 包含upper、middle、lower的DataFrame
16
- """
17
- middle = data.rolling(window=period).mean()
18
- std = data.rolling(window=period).std()
19
- upper = middle + std_dev * std
20
- lower = middle - std_dev * std
21
-
22
- return pd.DataFrame({
23
- 'upper': upper,
24
- 'middle': middle,
25
- 'lower': lower
26
- })
27
-
1
+ """
2
+ Volatility indicators for DeltaFQ.
3
+ """
4
+
5
+ import pandas as pd
6
+ import numpy as np
7
+ from ..core.base import BaseComponent
8
+
9
+
10
+ class VolatilityIndicators(BaseComponent):
11
+ """Volatility-based technical indicators."""
12
+
13
+ def initialize(self) -> bool:
14
+ """Initialize volatility indicators."""
15
+ self.logger.info("Initializing volatility indicators")
16
+ return True
17
+
18
+ def atr(self, high: pd.Series, low: pd.Series, close: pd.Series, period: int = 14) -> pd.Series:
19
+ """Average True Range."""
20
+ # True Range
21
+ tr1 = high - low
22
+ tr2 = abs(high - close.shift())
23
+ tr3 = abs(low - close.shift())
24
+ tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
25
+
26
+ # Average True Range
27
+ return tr.rolling(window=period).mean()
28
+
29
+ def bollinger_bandwidth(self, data: pd.Series, period: int = 20, std_dev: float = 2) -> pd.Series:
30
+ """Bollinger Band Width."""
31
+ sma = data.rolling(window=period).mean()
32
+ std = data.rolling(window=period).std()
33
+
34
+ upper_band = sma + (std * std_dev)
35
+ lower_band = sma - (std * std_dev)
36
+
37
+ return (upper_band - lower_band) / sma
38
+
39
+ def keltner_channels(self, high: pd.Series, low: pd.Series, close: pd.Series,
40
+ period: int = 20, multiplier: float = 2) -> pd.DataFrame:
41
+ """Keltner Channels."""
42
+ typical_price = (high + low + close) / 3
43
+ middle_line = typical_price.rolling(window=period).mean()
44
+ atr = self.atr(high, low, close, period)
45
+
46
+ return pd.DataFrame({
47
+ 'upper': middle_line + (multiplier * atr),
48
+ 'middle': middle_line,
49
+ 'lower': middle_line - (multiplier * atr)
50
+ })
51
+
52
+ def donchian_channels(self, high: pd.Series, low: pd.Series, period: int = 20) -> pd.DataFrame:
53
+ """Donchian Channels."""
54
+ return pd.DataFrame({
55
+ 'upper': high.rolling(window=period).max(),
56
+ 'lower': low.rolling(window=period).min(),
57
+ 'middle': (high.rolling(window=period).max() + low.rolling(window=period).min()) / 2
58
+ })
59
+
60
+ def volatility_ratio(self, data: pd.Series, short_period: int = 10, long_period: int = 30) -> pd.Series:
61
+ """Volatility Ratio."""
62
+ short_vol = data.rolling(window=short_period).std()
63
+ long_vol = data.rolling(window=long_period).std()
64
+
65
+ return short_vol / long_vol
66
+
67
+
@@ -0,0 +1,17 @@
1
+ """
2
+ Live trading module for DeltaFQ.
3
+ """
4
+
5
+ from .data_feed import LiveDataFeed
6
+ from .risk_control import LiveRiskControl
7
+ from .monitoring import TradingMonitor
8
+ from .connection import ConnectionManager
9
+
10
+ __all__ = [
11
+ "LiveDataFeed",
12
+ "LiveRiskControl",
13
+ "TradingMonitor",
14
+ "ConnectionManager"
15
+ ]
16
+
17
+
@@ -0,0 +1,235 @@
1
+ """
2
+ Connection management for live trading.
3
+ """
4
+
5
+ from typing import Dict, Any, Optional, List
6
+ from datetime import datetime, timedelta
7
+ from ..core.base import BaseComponent
8
+ from ..core.exceptions import TradingError
9
+
10
+
11
+ class ConnectionManager(BaseComponent):
12
+ """Manage connections to trading systems."""
13
+
14
+ def __init__(self, **kwargs):
15
+ """Initialize connection manager."""
16
+ super().__init__(**kwargs)
17
+ self.connections = {}
18
+ self.connection_configs = {}
19
+ self.health_check_interval = 60 # seconds
20
+ self.last_health_check = {}
21
+
22
+ def initialize(self) -> bool:
23
+ """Initialize connection manager."""
24
+ self.logger.info("Initializing connection manager")
25
+ return True
26
+
27
+ def add_connection(self, name: str, connection_type: str, config: Dict[str, Any]) -> bool:
28
+ """Add a connection configuration."""
29
+ try:
30
+ self.connection_configs[name] = {
31
+ 'type': connection_type,
32
+ 'config': config,
33
+ 'created_at': datetime.now(),
34
+ 'last_used': None,
35
+ 'status': 'disconnected'
36
+ }
37
+
38
+ self.logger.info(f"Added connection: {name} ({connection_type})")
39
+ return True
40
+
41
+ except Exception as e:
42
+ self.logger.error(f"Failed to add connection: {str(e)}")
43
+ return False
44
+
45
+ def connect(self, name: str) -> bool:
46
+ """Establish connection."""
47
+ try:
48
+ if name not in self.connection_configs:
49
+ raise TradingError(f"Connection {name} not found")
50
+
51
+ config = self.connection_configs[name]
52
+ connection_type = config['type']
53
+
54
+ # Create connection based on type
55
+ if connection_type == 'broker':
56
+ connection = self._create_broker_connection(config['config'])
57
+ elif connection == 'data_feed':
58
+ connection = self._create_data_feed_connection(config['config'])
59
+ else:
60
+ raise TradingError(f"Unknown connection type: {connection_type}")
61
+
62
+ # Test connection
63
+ if self._test_connection(connection):
64
+ self.connections[name] = connection
65
+ self.connection_configs[name]['status'] = 'connected'
66
+ self.connection_configs[name]['last_used'] = datetime.now()
67
+ self.last_health_check[name] = datetime.now()
68
+
69
+ self.logger.info(f"Connected: {name}")
70
+ return True
71
+ else:
72
+ self.logger.error(f"Connection test failed: {name}")
73
+ return False
74
+
75
+ except Exception as e:
76
+ self.logger.error(f"Failed to connect {name}: {str(e)}")
77
+ return False
78
+
79
+ def disconnect(self, name: str) -> bool:
80
+ """Disconnect from service."""
81
+ try:
82
+ if name in self.connections:
83
+ # Close connection
84
+ connection = self.connections[name]
85
+ if hasattr(connection, 'close'):
86
+ connection.close()
87
+
88
+ del self.connections[name]
89
+ self.connection_configs[name]['status'] = 'disconnected'
90
+
91
+ self.logger.info(f"Disconnected: {name}")
92
+ return True
93
+
94
+ return False
95
+
96
+ except Exception as e:
97
+ self.logger.error(f"Failed to disconnect {name}: {str(e)}")
98
+ return False
99
+
100
+ def get_connection(self, name: str):
101
+ """Get connection object."""
102
+ return self.connections.get(name)
103
+
104
+ def is_connected(self, name: str) -> bool:
105
+ """Check if connection is active."""
106
+ return name in self.connections and self.connection_configs.get(name, {}).get('status') == 'connected'
107
+
108
+ def health_check(self, name: str) -> bool:
109
+ """Perform health check on connection."""
110
+ try:
111
+ if name not in self.connections:
112
+ return False
113
+
114
+ connection = self.connections[name]
115
+
116
+ # Perform health check based on connection type
117
+ if hasattr(connection, 'ping'):
118
+ result = connection.ping()
119
+ elif hasattr(connection, 'is_connected'):
120
+ result = connection.is_connected()
121
+ else:
122
+ # Default health check
123
+ result = True
124
+
125
+ # Update last health check time
126
+ self.last_health_check[name] = datetime.now()
127
+
128
+ if not result:
129
+ self.logger.warning(f"Health check failed: {name}")
130
+ self.connection_configs[name]['status'] = 'unhealthy'
131
+ else:
132
+ self.connection_configs[name]['status'] = 'connected'
133
+
134
+ return result
135
+
136
+ except Exception as e:
137
+ self.logger.error(f"Health check error for {name}: {str(e)}")
138
+ self.connection_configs[name]['status'] = 'error'
139
+ return False
140
+
141
+ def health_check_all(self) -> Dict[str, bool]:
142
+ """Perform health check on all connections."""
143
+ results = {}
144
+
145
+ for name in self.connections:
146
+ results[name] = self.health_check(name)
147
+
148
+ return results
149
+
150
+ def auto_reconnect(self, name: str, max_attempts: int = 3) -> bool:
151
+ """Attempt to automatically reconnect."""
152
+ try:
153
+ if name not in self.connection_configs:
154
+ return False
155
+
156
+ for attempt in range(max_attempts):
157
+ self.logger.info(f"Reconnection attempt {attempt + 1} for {name}")
158
+
159
+ if self.connect(name):
160
+ self.logger.info(f"Successfully reconnected: {name}")
161
+ return True
162
+
163
+ # Wait before next attempt
164
+ import time
165
+ time.sleep(5)
166
+
167
+ self.logger.error(f"Failed to reconnect {name} after {max_attempts} attempts")
168
+ return False
169
+
170
+ except Exception as e:
171
+ self.logger.error(f"Auto-reconnect failed for {name}: {str(e)}")
172
+ return False
173
+
174
+ def _create_broker_connection(self, config: Dict[str, Any]):
175
+ """Create broker connection."""
176
+ # Placeholder for broker connection creation
177
+ return MockConnection('broker', config)
178
+
179
+ def _create_data_feed_connection(self, config: Dict[str, Any]):
180
+ """Create data feed connection."""
181
+ # Placeholder for data feed connection creation
182
+ return MockConnection('data_feed', config)
183
+
184
+ def _test_connection(self, connection) -> bool:
185
+ """Test connection."""
186
+ try:
187
+ # Simple connection test
188
+ if hasattr(connection, 'test'):
189
+ return connection.test()
190
+ return True
191
+
192
+ except Exception as e:
193
+ self.logger.error(f"Connection test failed: {str(e)}")
194
+ return False
195
+
196
+ def get_connection_status(self) -> Dict[str, Any]:
197
+ """Get status of all connections."""
198
+ return {
199
+ 'total_connections': len(self.connection_configs),
200
+ 'active_connections': len(self.connections),
201
+ 'connections': {
202
+ name: {
203
+ 'type': config['type'],
204
+ 'status': config['status'],
205
+ 'last_used': config['last_used'],
206
+ 'last_health_check': self.last_health_check.get(name)
207
+ }
208
+ for name, config in self.connection_configs.items()
209
+ }
210
+ }
211
+
212
+
213
+ class MockConnection:
214
+ """Mock connection for testing."""
215
+
216
+ def __init__(self, connection_type: str, config: Dict[str, Any]):
217
+ self.connection_type = connection_type
218
+ self.config = config
219
+ self.connected = True
220
+
221
+ def test(self) -> bool:
222
+ """Test connection."""
223
+ return True
224
+
225
+ def ping(self) -> bool:
226
+ """Ping connection."""
227
+ return True
228
+
229
+ def is_connected(self) -> bool:
230
+ """Check if connected."""
231
+ return self.connected
232
+
233
+ def close(self):
234
+ """Close connection."""
235
+ self.connected = False
@@ -0,0 +1,159 @@
1
+ """
2
+ Live data feed management for real-time trading.
3
+ """
4
+
5
+ import pandas as pd
6
+ from typing import Dict, List, Callable, Optional, Any
7
+ from datetime import datetime
8
+ from ..core.base import BaseComponent
9
+ from ..core.exceptions import TradingError
10
+
11
+
12
+ class LiveDataFeed(BaseComponent):
13
+ """Manages real-time market data feeds."""
14
+
15
+ def __init__(self, **kwargs):
16
+ """Initialize live data feed."""
17
+ super().__init__(**kwargs)
18
+ self.subscribers = {}
19
+ self.data_callbacks = []
20
+ self.is_running = False
21
+ self.last_prices = {}
22
+
23
+ def initialize(self) -> bool:
24
+ """Initialize live data feed."""
25
+ self.logger.info("Initializing live data feed")
26
+ return True
27
+
28
+ def subscribe(self, symbols: List[str], callback: Optional[Callable] = None) -> bool:
29
+ """Subscribe to live data for given symbols."""
30
+ try:
31
+ for symbol in symbols:
32
+ if symbol not in self.subscribers:
33
+ self.subscribers[symbol] = []
34
+
35
+ if callback:
36
+ self.subscribers[symbol].append(callback)
37
+
38
+ self.logger.info(f"Subscribed to {symbol}")
39
+
40
+ return True
41
+
42
+ except Exception as e:
43
+ raise TradingError(f"Failed to subscribe to symbols: {str(e)}")
44
+
45
+ def unsubscribe(self, symbols: List[str]) -> bool:
46
+ """Unsubscribe from live data."""
47
+ try:
48
+ for symbol in symbols:
49
+ if symbol in self.subscribers:
50
+ del self.subscribers[symbol]
51
+ self.logger.info(f"Unsubscribed from {symbol}")
52
+
53
+ return True
54
+
55
+ except Exception as e:
56
+ self.logger.error(f"Failed to unsubscribe: {str(e)}")
57
+ return False
58
+
59
+ def add_data_callback(self, callback: Callable) -> bool:
60
+ """Add a general data callback."""
61
+ self.data_callbacks.append(callback)
62
+ return True
63
+
64
+ def start_feed(self) -> bool:
65
+ """Start the live data feed."""
66
+ try:
67
+ self.is_running = True
68
+ self.logger.info("Live data feed started")
69
+
70
+ # In a real implementation, this would start the actual data feed
71
+ # For now, we'll simulate with periodic updates
72
+ self._simulate_data_feed()
73
+
74
+ return True
75
+
76
+ except Exception as e:
77
+ self.is_running = False
78
+ raise TradingError(f"Failed to start data feed: {str(e)}")
79
+
80
+ def stop_feed(self) -> bool:
81
+ """Stop the live data feed."""
82
+ try:
83
+ self.is_running = False
84
+ self.logger.info("Live data feed stopped")
85
+ return True
86
+
87
+ except Exception as e:
88
+ self.logger.error(f"Failed to stop data feed: {str(e)}")
89
+ return False
90
+
91
+ def get_latest_price(self, symbol: str) -> Optional[float]:
92
+ """Get latest price for a symbol."""
93
+ return self.last_prices.get(symbol)
94
+
95
+ def get_latest_prices(self, symbols: List[str]) -> Dict[str, float]:
96
+ """Get latest prices for multiple symbols."""
97
+ return {symbol: self.last_prices.get(symbol) for symbol in symbols if symbol in self.last_prices}
98
+
99
+ def _simulate_data_feed(self):
100
+ """Simulate live data feed for testing."""
101
+ import time
102
+ import random
103
+
104
+ base_prices = {
105
+ 'AAPL': 150.0,
106
+ 'GOOGL': 2500.0,
107
+ 'MSFT': 300.0,
108
+ 'TSLA': 200.0
109
+ }
110
+
111
+ while self.is_running:
112
+ for symbol in self.subscribers.keys():
113
+ if symbol in base_prices:
114
+ # Simulate price movement
115
+ current_price = self.last_prices.get(symbol, base_prices[symbol])
116
+ change = random.uniform(-0.02, 0.02) # ±2% change
117
+ new_price = current_price * (1 + change)
118
+
119
+ self.last_prices[symbol] = new_price
120
+
121
+ # Create data point
122
+ data_point = {
123
+ 'symbol': symbol,
124
+ 'price': new_price,
125
+ 'timestamp': datetime.now(),
126
+ 'volume': random.randint(1000, 10000)
127
+ }
128
+
129
+ # Notify subscribers
130
+ self._notify_subscribers(symbol, data_point)
131
+
132
+ time.sleep(1) # Update every second
133
+
134
+ def _notify_subscribers(self, symbol: str, data_point: Dict[str, Any]):
135
+ """Notify subscribers of new data."""
136
+ try:
137
+ # Notify symbol-specific subscribers
138
+ if symbol in self.subscribers:
139
+ for callback in self.subscribers[symbol]:
140
+ callback(data_point)
141
+
142
+ # Notify general data callbacks
143
+ for callback in self.data_callbacks:
144
+ callback(data_point)
145
+
146
+ except Exception as e:
147
+ self.logger.error(f"Error notifying subscribers: {str(e)}")
148
+
149
+ def get_feed_status(self) -> Dict[str, Any]:
150
+ """Get feed status information."""
151
+ return {
152
+ 'is_running': self.is_running,
153
+ 'subscribed_symbols': list(self.subscribers.keys()),
154
+ 'total_subscribers': sum(len(callbacks) for callbacks in self.subscribers.values()),
155
+ 'data_callbacks': len(self.data_callbacks),
156
+ 'latest_prices': dict(self.last_prices)
157
+ }
158
+
159
+