deltafq 0.4.0__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.
Files changed (42) hide show
  1. deltafq/__init__.py +29 -0
  2. deltafq/backtest/__init__.py +32 -0
  3. deltafq/backtest/engine.py +145 -0
  4. deltafq/backtest/metrics.py +74 -0
  5. deltafq/backtest/performance.py +350 -0
  6. deltafq/charts/__init__.py +14 -0
  7. deltafq/charts/performance.py +319 -0
  8. deltafq/charts/price.py +64 -0
  9. deltafq/charts/signals.py +181 -0
  10. deltafq/core/__init__.py +18 -0
  11. deltafq/core/base.py +21 -0
  12. deltafq/core/config.py +62 -0
  13. deltafq/core/exceptions.py +34 -0
  14. deltafq/core/logger.py +44 -0
  15. deltafq/data/__init__.py +16 -0
  16. deltafq/data/cleaner.py +39 -0
  17. deltafq/data/fetcher.py +58 -0
  18. deltafq/data/storage.py +264 -0
  19. deltafq/data/validator.py +51 -0
  20. deltafq/indicators/__init__.py +14 -0
  21. deltafq/indicators/fundamental.py +28 -0
  22. deltafq/indicators/talib_indicators.py +67 -0
  23. deltafq/indicators/technical.py +251 -0
  24. deltafq/live/__init__.py +16 -0
  25. deltafq/live/connection.py +235 -0
  26. deltafq/live/data_feed.py +158 -0
  27. deltafq/live/monitoring.py +191 -0
  28. deltafq/live/risk_control.py +192 -0
  29. deltafq/strategy/__init__.py +12 -0
  30. deltafq/strategy/base.py +34 -0
  31. deltafq/strategy/signals.py +193 -0
  32. deltafq/trader/__init__.py +16 -0
  33. deltafq/trader/broker.py +119 -0
  34. deltafq/trader/engine.py +174 -0
  35. deltafq/trader/order_manager.py +110 -0
  36. deltafq/trader/position_manager.py +92 -0
  37. deltafq-0.4.0.dist-info/METADATA +115 -0
  38. deltafq-0.4.0.dist-info/RECORD +42 -0
  39. deltafq-0.4.0.dist-info/WHEEL +5 -0
  40. deltafq-0.4.0.dist-info/entry_points.txt +2 -0
  41. deltafq-0.4.0.dist-info/licenses/LICENSE +21 -0
  42. deltafq-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,119 @@
1
+ """
2
+ Broker interface for live trading.
3
+ """
4
+
5
+ import pandas as pd
6
+ from abc import ABC, abstractmethod
7
+ from typing import Dict, List, Optional, Any
8
+ from datetime import datetime
9
+ from ..core.base import BaseComponent
10
+ from ..core.exceptions import TradingError
11
+
12
+
13
+ class Broker(BaseComponent, ABC):
14
+ """Abstract broker interface for live trading."""
15
+
16
+ def initialize(self) -> bool:
17
+ """Initialize broker connection."""
18
+ self.logger.info("Initializing broker connection")
19
+ return self._connect()
20
+
21
+ @abstractmethod
22
+ def _connect(self) -> bool:
23
+ """Connect to broker."""
24
+ pass
25
+
26
+ @abstractmethod
27
+ def place_order(self, symbol: str, quantity: int, order_type: str,
28
+ price: Optional[float] = None) -> str:
29
+ """Place an order with the broker."""
30
+ pass
31
+
32
+ @abstractmethod
33
+ def cancel_order(self, order_id: str) -> bool:
34
+ """Cancel an order."""
35
+ pass
36
+
37
+ @abstractmethod
38
+ def get_order_status(self, order_id: str) -> Dict[str, Any]:
39
+ """Get order status."""
40
+ pass
41
+
42
+ @abstractmethod
43
+ def get_account_info(self) -> Dict[str, Any]:
44
+ """Get account information."""
45
+ pass
46
+
47
+ @abstractmethod
48
+ def get_positions(self) -> Dict[str, Dict[str, Any]]:
49
+ """Get current positions."""
50
+ pass
51
+
52
+ @abstractmethod
53
+ def get_current_price(self, symbol: str) -> float:
54
+ """Get current price for symbol."""
55
+ pass
56
+
57
+
58
+ class MockBroker(Broker):
59
+ """Mock broker for testing purposes."""
60
+
61
+ def __init__(self, **kwargs):
62
+ """Initialize mock broker."""
63
+ super().__init__(**kwargs)
64
+ self.orders = {}
65
+ self.positions = {}
66
+ self.account_balance = 100000
67
+ self.order_counter = 0
68
+
69
+ def _connect(self) -> bool:
70
+ """Mock connection."""
71
+ self.logger.info("Connected to mock broker")
72
+ return True
73
+
74
+ def place_order(self, symbol: str, quantity: int, order_type: str,
75
+ price: Optional[float] = None) -> str:
76
+ """Place a mock order."""
77
+ self.order_counter += 1
78
+ order_id = f"MOCK_{self.order_counter}"
79
+
80
+ self.orders[order_id] = {
81
+ 'symbol': symbol,
82
+ 'quantity': quantity,
83
+ 'order_type': order_type,
84
+ 'price': price,
85
+ 'status': 'pending',
86
+ 'timestamp': datetime.now()
87
+ }
88
+
89
+ self.logger.info(f"Mock order placed: {order_id}")
90
+ return order_id
91
+
92
+ def cancel_order(self, order_id: str) -> bool:
93
+ """Cancel a mock order."""
94
+ if order_id in self.orders:
95
+ self.orders[order_id]['status'] = 'cancelled'
96
+ self.logger.info(f"Mock order cancelled: {order_id}")
97
+ return True
98
+ return False
99
+
100
+ def get_order_status(self, order_id: str) -> Dict[str, Any]:
101
+ """Get mock order status."""
102
+ return self.orders.get(order_id, {})
103
+
104
+ def get_account_info(self) -> Dict[str, Any]:
105
+ """Get mock account info."""
106
+ return {
107
+ 'balance': self.account_balance,
108
+ 'buying_power': self.account_balance,
109
+ 'equity': self.account_balance
110
+ }
111
+
112
+ def get_positions(self) -> Dict[str, Dict[str, Any]]:
113
+ """Get mock positions."""
114
+ return self.positions
115
+
116
+ def get_current_price(self, symbol: str) -> float:
117
+ """Get mock current price."""
118
+ # Return a mock price
119
+ return 100.0
@@ -0,0 +1,174 @@
1
+ """
2
+ Trade execution engine for DeltaFQ.
3
+ """
4
+
5
+ import pandas as pd
6
+ from typing import Dict, List, Optional, Any
7
+ from datetime import datetime
8
+ from ..core.base import BaseComponent
9
+ from ..core.exceptions import TradingError
10
+ from .order_manager import OrderManager
11
+ from .position_manager import PositionManager
12
+
13
+
14
+ class ExecutionEngine(BaseComponent):
15
+ """
16
+ Trade execution engine for real-time trading.
17
+ Supports paper trading (broker=None) and live trading (broker=Broker).
18
+ Paper trading manages cash internally. Live trading uses broker for account info.
19
+ """
20
+
21
+ def __init__(self, broker=None, initial_capital: Optional[float] = None,
22
+ commission: float = 0.001, **kwargs):
23
+ """
24
+ Initialize execution engine.
25
+ Args:
26
+ broker: Broker for live trading. None for paper trading.
27
+ initial_capital: Initial capital for paper trading. Defaults to 1000000.
28
+ commission: Commission rate for paper trading. Defaults to 0.001.
29
+ """
30
+ super().__init__(**kwargs)
31
+ self.broker = broker
32
+ self.order_manager = OrderManager()
33
+ self.position_manager = PositionManager()
34
+
35
+ # Paper trading mode: manage cash internally
36
+ if broker is None:
37
+ self.initial_capital = initial_capital if initial_capital is not None else 1000000
38
+ self.cash = self.initial_capital
39
+ self.commission = commission
40
+ self.trades: List[Dict[str, Any]] = []
41
+ self.is_paper_trading = True
42
+ else:
43
+ # Live trading mode: get account info from broker
44
+ self.cash = None
45
+ self.commission = None
46
+ self.trades = []
47
+ self.is_paper_trading = False
48
+
49
+ def initialize(self) -> bool:
50
+ """Initialize execution engine."""
51
+ if self.is_paper_trading:
52
+ self.logger.info(f"Initializing paper trading execution engine with capital: {self.initial_capital}")
53
+ else:
54
+ self.logger.info("Initializing live trading execution engine")
55
+
56
+ if self.broker:
57
+ return self.broker.initialize()
58
+
59
+ return True
60
+
61
+ def execute_order(self, symbol: str, quantity: int, order_type: str = "limit",
62
+ price: Optional[float] = None, timestamp: Optional[datetime] = None) -> str:
63
+ """Execute an order. Default is limit order (price required)."""
64
+ try:
65
+ # Validate price for limit orders
66
+ if order_type == "limit" and price is None:
67
+ raise TradingError("Price is required for limit orders")
68
+
69
+ # Create order
70
+ order_id = self.order_manager.create_order(
71
+ symbol=symbol,
72
+ quantity=quantity,
73
+ order_type=order_type,
74
+ price=price
75
+ )
76
+
77
+ # Execute through broker
78
+ if self.broker:
79
+ broker_order_id = self.broker.place_order(
80
+ symbol=symbol,
81
+ quantity=quantity,
82
+ order_type=order_type,
83
+ price=price
84
+ )
85
+
86
+ # Update order with broker ID
87
+ order = self.order_manager.get_order(order_id)
88
+ if order:
89
+ order['broker_order_id'] = broker_order_id
90
+
91
+ self.logger.info(f"Order executed through broker: {order_id} -> {broker_order_id}")
92
+ else:
93
+ self._execute_paper_trade(order_id, price, timestamp)
94
+ self.logger.info(f"Order executed in paper trading: {order_id}, timestamp: {timestamp}")
95
+
96
+ return order_id
97
+
98
+ except Exception as e:
99
+ raise TradingError(f"Failed to execute order: {str(e)}")
100
+
101
+ def _execute_paper_trade(self, order_id: str, execution_price: float, timestamp: Optional[datetime] = None):
102
+ """Execute paper trading with cash management."""
103
+ order = self.order_manager.get_order(order_id)
104
+ if not order:
105
+ return
106
+
107
+ symbol = order['symbol']
108
+ quantity = order['quantity']
109
+ timestamp = timestamp or datetime.now()
110
+
111
+ if quantity > 0: # Buy
112
+ gross_cost = quantity * execution_price
113
+ commission_amount = gross_cost * self.commission
114
+ total_cost = gross_cost + commission_amount
115
+
116
+ if total_cost <= self.cash:
117
+ self.cash -= total_cost
118
+ self.position_manager.add_position(symbol, quantity, execution_price)
119
+ self.order_manager.mark_executed(order_id, execution_price)
120
+
121
+ # Record trade (unified record with full details)
122
+ self.trades.append({
123
+ 'order_id': order_id,
124
+ 'symbol': symbol,
125
+ 'quantity': quantity,
126
+ 'price': execution_price,
127
+ 'type': 'buy',
128
+ 'timestamp': timestamp,
129
+ 'commission': commission_amount,
130
+ 'cost': total_cost
131
+ })
132
+ else:
133
+ self.logger.warning(f"Insufficient cash for buy: need {total_cost:.2f}, have {self.cash:.2f}")
134
+ self.order_manager.cancel_order(order_id)
135
+ else: # Sell
136
+ quantity = abs(quantity)
137
+ if self.position_manager.can_sell(symbol, quantity):
138
+ gross_revenue = quantity * execution_price
139
+ commission_amount = gross_revenue * self.commission
140
+ net_revenue = gross_revenue - commission_amount
141
+
142
+ # Calculate profit/loss
143
+ buy_cost = self._get_latest_buy_cost(symbol)
144
+ profit_loss = net_revenue - buy_cost if buy_cost else net_revenue
145
+
146
+ self.position_manager.reduce_position(symbol, quantity, execution_price)
147
+ self.cash += net_revenue
148
+ self.order_manager.mark_executed(order_id, execution_price)
149
+
150
+ # Record trade (unified record with full details)
151
+ self.trades.append({
152
+ 'order_id': order_id,
153
+ 'symbol': symbol,
154
+ 'quantity': quantity,
155
+ 'price': execution_price,
156
+ 'type': 'sell',
157
+ 'timestamp': timestamp,
158
+ 'commission': commission_amount,
159
+ 'gross_revenue': gross_revenue,
160
+ 'net_revenue': net_revenue,
161
+ 'buy_cost': buy_cost,
162
+ 'profit_loss': profit_loss
163
+ })
164
+ else:
165
+ self.logger.warning(f"Insufficient position for sell: {symbol}, need {quantity}")
166
+ self.order_manager.cancel_order(order_id)
167
+
168
+ def _get_latest_buy_cost(self, symbol: str) -> float:
169
+ """Get the latest buy cost for a symbol (for PnL calculation)."""
170
+ for trade in reversed(self.trades):
171
+ if trade.get('symbol') == symbol and trade.get('type') == 'buy':
172
+ return float(trade.get('cost', 0.0))
173
+ return 0.0
174
+
@@ -0,0 +1,110 @@
1
+ """
2
+ Order management system for DeltaFQ.
3
+ """
4
+
5
+ import pandas as pd
6
+ from typing import Dict, List, Optional, Any
7
+ from datetime import datetime
8
+ from ..core.base import BaseComponent
9
+
10
+
11
+ class OrderManager(BaseComponent):
12
+ """Manage trading orders."""
13
+
14
+ def __init__(self, **kwargs):
15
+ """Initialize order manager."""
16
+ super().__init__(**kwargs)
17
+ self.orders = {}
18
+ self.order_counter = 0
19
+
20
+ def initialize(self) -> bool:
21
+ """Initialize order manager."""
22
+ self.logger.info("Initializing order manager")
23
+ return True
24
+
25
+ def create_order(self, symbol: str, quantity: int, order_type: str = "limit",
26
+ price: Optional[float] = None, stop_price: Optional[float] = None) -> str:
27
+ """Create a new order."""
28
+ self.order_counter += 1
29
+ order_id = f"ORD_{self.order_counter:06d}"
30
+
31
+ order = {
32
+ 'id': order_id,
33
+ 'symbol': symbol,
34
+ 'quantity': quantity,
35
+ 'order_type': order_type,
36
+ 'price': price,
37
+ 'stop_price': stop_price,
38
+ 'status': 'pending',
39
+ 'created_at': datetime.now(),
40
+ 'updated_at': datetime.now()
41
+ }
42
+
43
+ self.orders[order_id] = order
44
+ self.logger.info(f"Order created: {order_id}")
45
+ return order_id
46
+
47
+ def get_order(self, order_id: str) -> Optional[Dict[str, Any]]:
48
+ """Get order by ID."""
49
+ return self.orders.get(order_id)
50
+
51
+ def update_order_status(self, order_id: str, status: str) -> bool:
52
+ """Update order status."""
53
+ if order_id in self.orders:
54
+ self.orders[order_id]['status'] = status
55
+ self.orders[order_id]['updated_at'] = datetime.now()
56
+ return True
57
+ return False
58
+
59
+ def mark_executed(self, order_id: str, execution_price: Optional[float] = None) -> bool:
60
+ """Mark order as executed."""
61
+ if order_id in self.orders:
62
+ self.orders[order_id]['status'] = 'executed'
63
+ self.orders[order_id]['execution_price'] = execution_price
64
+ self.orders[order_id]['executed_at'] = datetime.now()
65
+ self.orders[order_id]['updated_at'] = datetime.now()
66
+ return True
67
+ return False
68
+
69
+ def cancel_order(self, order_id: str) -> bool:
70
+ """Cancel an order."""
71
+ if order_id in self.orders and self.orders[order_id]['status'] == 'pending':
72
+ self.orders[order_id]['status'] = 'cancelled'
73
+ self.orders[order_id]['updated_at'] = datetime.now()
74
+ return True
75
+ return False
76
+
77
+ def get_orders_by_symbol(self, symbol: str) -> List[Dict[str, Any]]:
78
+ """Get all orders for a symbol."""
79
+ return [order for order in self.orders.values() if order['symbol'] == symbol]
80
+
81
+ def get_orders_by_status(self, status: str) -> List[Dict[str, Any]]:
82
+ """Get all orders with specific status."""
83
+ return [order for order in self.orders.values() if order['status'] == status]
84
+
85
+ def get_pending_orders(self) -> List[Dict[str, Any]]:
86
+ """Get all pending orders."""
87
+ return self.get_orders_by_status('pending')
88
+
89
+ def get_executed_orders(self) -> List[Dict[str, Any]]:
90
+ """Get all executed orders."""
91
+ return self.get_orders_by_status('executed')
92
+
93
+ def get_order_history(self) -> List[Dict[str, Any]]:
94
+ """Get complete order history."""
95
+ return list(self.orders.values())
96
+
97
+ def cleanup_old_orders(self, days: int = 30) -> int:
98
+ """Clean up old orders."""
99
+ cutoff_date = datetime.now() - pd.Timedelta(days=days)
100
+ old_orders = [
101
+ order_id for order_id, order in self.orders.items()
102
+ if order['created_at'] < cutoff_date and order['status'] in ['executed', 'cancelled']
103
+ ]
104
+
105
+ for order_id in old_orders:
106
+ del self.orders[order_id]
107
+
108
+ self.logger.info(f"Cleaned up {len(old_orders)} old orders")
109
+ return len(old_orders)
110
+
@@ -0,0 +1,92 @@
1
+ """
2
+ Position management for DeltaFQ.
3
+ """
4
+
5
+ from typing import Dict, Optional
6
+ from datetime import datetime
7
+ from ..core.base import BaseComponent
8
+
9
+
10
+ class PositionManager(BaseComponent):
11
+ """Manage trading positions."""
12
+
13
+ def __init__(self, **kwargs):
14
+ """Initialize position manager."""
15
+ super().__init__(**kwargs)
16
+ self.positions = {}
17
+
18
+ def initialize(self) -> bool:
19
+ """Initialize position manager."""
20
+ self.logger.info("Initializing position manager")
21
+ return True
22
+
23
+ def add_position(self, symbol: str, quantity: int, price: Optional[float] = None) -> bool:
24
+ """Add to existing position or create new position."""
25
+ if symbol in self.positions:
26
+ # Update existing position
27
+ current_quantity = self.positions[symbol]['quantity']
28
+ current_avg_price = self.positions[symbol]['avg_price']
29
+
30
+ new_quantity = current_quantity + quantity
31
+ if price:
32
+ new_avg_price = ((current_quantity * current_avg_price) + (quantity * price)) / new_quantity
33
+ else:
34
+ new_avg_price = current_avg_price
35
+
36
+ self.positions[symbol]['quantity'] = new_quantity
37
+ self.positions[symbol]['avg_price'] = new_avg_price
38
+ self.positions[symbol]['updated_at'] = datetime.now()
39
+ else:
40
+ # Create new position
41
+ self.positions[symbol] = {
42
+ 'symbol': symbol,
43
+ 'quantity': quantity,
44
+ 'avg_price': price or 0.0,
45
+ 'created_at': datetime.now(),
46
+ 'updated_at': datetime.now()
47
+ }
48
+
49
+ self.logger.info(f"Position updated: {symbol} -> {self.positions[symbol]['quantity']}")
50
+ return True
51
+
52
+ def reduce_position(self, symbol: str, quantity: int, price: Optional[float] = None) -> bool:
53
+ """Reduce existing position."""
54
+ if symbol not in self.positions:
55
+ self.logger.warning(f"No position found for symbol: {symbol}")
56
+ return False
57
+
58
+ current_quantity = self.positions[symbol]['quantity']
59
+ if current_quantity < quantity:
60
+ self.logger.warning(f"Insufficient position: {symbol}")
61
+ return False
62
+
63
+ new_quantity = current_quantity - quantity
64
+
65
+ if new_quantity == 0:
66
+ del self.positions[symbol]
67
+ else:
68
+ self.positions[symbol]['quantity'] = new_quantity
69
+ self.positions[symbol]['updated_at'] = datetime.now()
70
+
71
+ self.logger.info(f"Position reduced: {symbol} -> {new_quantity}")
72
+ return True
73
+
74
+ def get_position(self, symbol: str) -> int:
75
+ """Get current position quantity for symbol."""
76
+ return self.positions.get(symbol, {}).get('quantity', 0)
77
+
78
+ def get_all_positions(self) -> Dict[str, int]:
79
+ """Get all current positions."""
80
+ return {symbol: pos['quantity'] for symbol, pos in self.positions.items()}
81
+
82
+ def can_sell(self, symbol: str, quantity: int) -> bool:
83
+ """Check if we can sell the specified quantity."""
84
+ return self.get_position(symbol) >= quantity
85
+
86
+ def close_position(self, symbol: str, price: Optional[float] = None) -> bool:
87
+ """Close entire position for symbol."""
88
+ if symbol not in self.positions:
89
+ return False
90
+
91
+ quantity = self.positions[symbol]['quantity']
92
+ return self.reduce_position(symbol, quantity, price)
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: deltafq
3
+ Version: 0.4.0
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: yfinance>=0.1.70
29
+ Requires-Dist: requests>=2.25.0
30
+ Provides-Extra: viz
31
+ Requires-Dist: plotly>=5.0.0; extra == "viz"
32
+ Provides-Extra: talib
33
+ Requires-Dist: TA-Lib>=0.4.24; extra == "talib"
34
+ Provides-Extra: dev
35
+ Requires-Dist: pytest>=6.2.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=2.12.0; extra == "dev"
37
+ Requires-Dist: black>=21.0.0; extra == "dev"
38
+ Requires-Dist: flake8>=3.9.0; extra == "dev"
39
+ Requires-Dist: sphinx>=4.0.0; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+ # DeltaFQ
43
+
44
+ Modern Python library for strategy research, backtesting, paper/live trading, and streamlined reporting.
45
+
46
+ ## Highlights
47
+
48
+ - **Clean architecture**: `data` → `strategy` (signals) → `backtest` (execution + metrics)
49
+ - **Execution engine**: Unified order routing for paper/live trading via a `Broker` abstraction
50
+ - **Indicators**: Rich `TechnicalIndicators` (SMA/EMA/RSI/KDJ/BOLL/ATR/…)
51
+ - **Signals**: Simple, composable `SignalGenerator` helpers (e.g., Bollinger `touch`/`cross`/`cross_current`)
52
+ - **Reports**: Console-friendly summary with i18n (Chinese/English) powered by `PerformanceReporter`
53
+ - **Charts**: `PerformanceChart` delivers Matplotlib or Plotly (optional) performance dashboards
54
+
55
+ ## Install
56
+
57
+ ```bash
58
+ pip install deltafq
59
+ ```
60
+
61
+ ## 60-second Quick Start (Bollinger strategy)
62
+
63
+ ```python
64
+ import deltafq as dfq
65
+
66
+ symbol = "AAPL"
67
+ fetcher = dfq.data.DataFetcher()
68
+ indicators = dfq.indicators.TechnicalIndicators()
69
+ generator = dfq.strategy.SignalGenerator()
70
+ engine = dfq.backtest.BacktestEngine(initial_capital=100_000)
71
+ reporter = dfq.backtest.PerformanceReporter()
72
+ chart = dfq.charts.PerformanceChart()
73
+
74
+ data = fetcher.fetch_data(symbol, "2023-01-01", "2023-12-31", clean=True)
75
+ bands = indicators.boll(data["Close"], period=20, std_dev=2)
76
+ signals = generator.boll_signals(price=data["Close"], bands=bands, method="cross_current")
77
+
78
+ trades_df, values_df = engine.run_backtest(symbol, signals, data["Close"], strategy_name="BOLL")
79
+
80
+ # Text summary (zh/en available)
81
+ reporter.print_summary(
82
+ symbol=symbol,
83
+ trades_df=trades_df,
84
+ values_df=values_df,
85
+ title=f"{symbol} BOLL Strategy",
86
+ language="en",
87
+ )
88
+
89
+ # Optional performance dashboard (Matplotlib by default; set use_plotly=True for interactive charts)
90
+ chart.plot_backtest_charts(values_df=values_df, benchmark_close=data["Close"], title=f"{symbol} BOLL Strategy")
91
+ ```
92
+
93
+ ## What’s inside
94
+
95
+ - `deltafq/data`: fetching, cleaning, validation
96
+ - `deltafq/indicators`: classic TA indicators
97
+ - `deltafq/strategy`: signal generation + BaseStrategy helpers
98
+ - `deltafq/backtest`: execution via `ExecutionEngine`; reporting via `PerformanceReporter`
99
+ - `deltafq/charts`: signal and performance charts (Matplotlib + optional Plotly)
100
+
101
+ ## Examples
102
+
103
+ See the `examples/` folder for ready-to-run scripts:
104
+
105
+ - `04_backtest_result.py`: Bollinger strategy summary + charts
106
+ - `05_visualize_charts.py`: standalone visualization demos
107
+ - `06_base_strategy`: implement a moving-average cross using `BaseStrategy`
108
+
109
+ ## Contributing
110
+
111
+ Contributions are welcome! Please open an issue or submit a PR.
112
+
113
+ ## License
114
+
115
+ MIT License – see [LICENSE](LICENSE).
@@ -0,0 +1,42 @@
1
+ deltafq/__init__.py,sha256=aSYOpWiM3MuD_bF61ckgxNGSOFnW7Q1VE_t5eg4sNPA,535
2
+ deltafq/backtest/__init__.py,sha256=RNOK7AwB2wdJrgcYXc3ok5E7pax3-rzI_g73dEv7aYc,771
3
+ deltafq/backtest/engine.py,sha256=D-wbIHcawdgMvZikUvgLB7hpwYVVpaEY2gSH1yu4jtE,6283
4
+ deltafq/backtest/metrics.py,sha256=zxJuxTrVOkZPlSAbrnfS6l61LEPx4Vr8I8nHX588jOI,2331
5
+ deltafq/backtest/performance.py,sha256=Xi6J0GDrt0_0r-4cY8TVNNt6SUzPimaV2RDCXC0J7Xs,14091
6
+ deltafq/charts/__init__.py,sha256=-vKa-dhgt_f3EfBf5-QgUVwTNSnoudxhbUXuJWvARmQ,249
7
+ deltafq/charts/performance.py,sha256=P0povd-9rCqm4IYbREd4-U0xzr96zvFybCuHl2LVAkU,13861
8
+ deltafq/charts/price.py,sha256=8oq0_Z_ZX5hoM2BtKYgHNM6JjYxBqSXLA9pA04UQ3SU,2251
9
+ deltafq/charts/signals.py,sha256=cz7EGE7LBQXM7d1Xf3LMXnl5lH2rZKJKgKzLjfLBRcM,7678
10
+ deltafq/core/__init__.py,sha256=ZIc0Bb9n09Hpqk1Zv2fTKFNIu9NgUCMs3XGbRQGWiJQ,329
11
+ deltafq/core/base.py,sha256=2wgZ5BoN5-tHyQDgEwVBT6AByFsgLIKN9KEUVbOzCG8,510
12
+ deltafq/core/config.py,sha256=9S7APw7zayCxjzcRh71zX7jqJGDlpVMcWoW-lcJnwBY,1840
13
+ deltafq/core/exceptions.py,sha256=58zP4yMgeWj4aLCv3SLbTGmmh6psoxVWWu-QkdtC1bM,655
14
+ deltafq/core/logger.py,sha256=FVVaYjJmWlN2DCpKEUTU_4HuzqCsx7uohLwgHxevOuM,1293
15
+ deltafq/data/__init__.py,sha256=xSrxuhlC3Rvlp2I5_yz3Cp-XqxDG_3j08ecawaoy1Fk,291
16
+ deltafq/data/cleaner.py,sha256=RuqqPgNHizJ16e0TUAtS3pnftphxvRBKKAhfGtXs7aQ,1289
17
+ deltafq/data/fetcher.py,sha256=mGqoEwRAotCPgqlum2OGpjztN1hsNwilkAm4K1MPW_g,2060
18
+ deltafq/data/storage.py,sha256=hKurYcWN0jlBGd6TqYle-BMooZ2q_CwoGjaM7BRWeOs,10889
19
+ deltafq/data/validator.py,sha256=hFCYXaHNGlqEIcc8hv8F1Nc88JaBzI9nzgylePKVRaw,1813
20
+ deltafq/indicators/__init__.py,sha256=3iMKAj6DL3Hu01ZwtCE8ynUkU2o4cdoLSDddOFLceyY,294
21
+ deltafq/indicators/fundamental.py,sha256=tT6OsdfR7114QUBaDG8_4QH_3k68FVitgh5bmhmYS7E,827
22
+ deltafq/indicators/talib_indicators.py,sha256=EpvnUXpOCYNDzAT60X-kLo0iHVmRg59E-MtF7KkHNeg,3424
23
+ deltafq/indicators/technical.py,sha256=dnXUEfru4XedU1jcB6mXthB7ZPIsQyBwC2iHoKOSBUg,10763
24
+ deltafq/live/__init__.py,sha256=nEB4zVk68zAJEaCcP5os6cJ8jc6cm-RlvL0yw3q9z-c,322
25
+ deltafq/live/connection.py,sha256=efjTC6T-37wmELIAJUXDJnzcj0zGislPswLxq54rmOI,8613
26
+ deltafq/live/data_feed.py,sha256=Cl7UTKgwKeNvuQ2HyLHsPgt0V_Kh5PU76XmW4c1V10w,5784
27
+ deltafq/live/monitoring.py,sha256=-akC_mhtlCbdbKExMGhTour9M0R89C84KulpHsM1p40,7067
28
+ deltafq/live/risk_control.py,sha256=rYijF7K_YkV-CaDUEGn6BoV851GT-llBw-p4HRwEHxY,7773
29
+ deltafq/strategy/__init__.py,sha256=VsoXJrtLbKy9z1Mwnp1m7RkB7bewjeBU0HcQyVAycZs,176
30
+ deltafq/strategy/base.py,sha256=R2zCs2oVm68B2MgHYtEBC3_olZG1T82dT4AiLAiZFSA,1235
31
+ deltafq/strategy/signals.py,sha256=a2I0I-FfA5dlMLOVT7FSdxIX0SOhu4jtte8lVwyI0b4,8807
32
+ deltafq/trader/__init__.py,sha256=AoxAD0QHECZUn8xTY_VvpzD4olQ0GTJZTglVSco3yr0,296
33
+ deltafq/trader/broker.py,sha256=e0E2LJO9CXP3w0mcsijkYfhqOxZ2Est0P0iPFS985x8,3644
34
+ deltafq/trader/engine.py,sha256=YA3KQhQ6qmcQzE26ljNkswVFnBHHvdl0sAcbxIhseMU,7423
35
+ deltafq/trader/order_manager.py,sha256=rSo5OrF_7I-zeYZ1FG3R80ucLTAHF6cpXQI1pMM7ETc,4154
36
+ deltafq/trader/position_manager.py,sha256=fJ2MbjTS5TMEDw5kqHqY3_KLKxcGLS2hvAUb1skbsjw,3621
37
+ deltafq-0.4.0.dist-info/licenses/LICENSE,sha256=_NsOVmjUmvFiFNOp8vowVeILNMu9CRuudY6PeVIikSA,1084
38
+ deltafq-0.4.0.dist-info/METADATA,sha256=LUqGqYmMvByViGr3FTVMnVxNqtWYQo2Oit-J86PoKaY,4319
39
+ deltafq-0.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ deltafq-0.4.0.dist-info/entry_points.txt,sha256=TVBHZfYgwrYpVZfsBonsJriXHkZm1CvBvnDW7--cxl0,45
41
+ deltafq-0.4.0.dist-info/top_level.txt,sha256=j1Q3ce7BEqdXVZd-mlHiJBDHq3iJGiKRKEXPW8xHLHo,8
42
+ deltafq-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ deltafq = deltafq.cli:main