mcli-framework 7.12.0__py3-none-any.whl → 7.12.3__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 mcli-framework might be problematic. Click here for more details.

Files changed (216) hide show
  1. mcli/app/__init__.py +0 -2
  2. mcli/app/commands_cmd.py +19 -23
  3. mcli/app/completion_helpers.py +5 -5
  4. mcli/app/init_cmd.py +10 -10
  5. mcli/app/lock_cmd.py +82 -27
  6. mcli/app/main.py +4 -50
  7. mcli/app/model/model.py +5 -10
  8. mcli/app/store_cmd.py +8 -8
  9. mcli/app/video/__init__.py +0 -2
  10. mcli/app/video/video.py +1 -14
  11. mcli/chat/chat.py +90 -108
  12. mcli/chat/command_rag.py +0 -4
  13. mcli/chat/enhanced_chat.py +32 -41
  14. mcli/chat/system_controller.py +37 -37
  15. mcli/chat/system_integration.py +4 -5
  16. mcli/cli.py +2 -3
  17. mcli/lib/api/api.py +4 -9
  18. mcli/lib/api/daemon_client.py +19 -20
  19. mcli/lib/api/daemon_client_local.py +1 -3
  20. mcli/lib/api/daemon_decorator.py +6 -6
  21. mcli/lib/api/mcli_decorators.py +4 -8
  22. mcli/lib/auth/__init__.py +0 -1
  23. mcli/lib/auth/auth.py +4 -5
  24. mcli/lib/auth/mcli_manager.py +7 -12
  25. mcli/lib/auth/token_util.py +5 -5
  26. mcli/lib/config/__init__.py +29 -1
  27. mcli/lib/config/config.py +0 -1
  28. mcli/lib/custom_commands.py +1 -1
  29. mcli/lib/discovery/command_discovery.py +15 -15
  30. mcli/lib/erd/erd.py +7 -7
  31. mcli/lib/files/files.py +1 -1
  32. mcli/lib/fs/__init__.py +31 -1
  33. mcli/lib/fs/fs.py +12 -13
  34. mcli/lib/lib.py +0 -1
  35. mcli/lib/logger/logger.py +7 -10
  36. mcli/lib/performance/optimizer.py +25 -27
  37. mcli/lib/performance/rust_bridge.py +22 -27
  38. mcli/lib/performance/uvloop_config.py +0 -1
  39. mcli/lib/pickles/__init__.py +0 -1
  40. mcli/lib/pickles/pickles.py +0 -2
  41. mcli/lib/secrets/commands.py +0 -2
  42. mcli/lib/secrets/manager.py +0 -1
  43. mcli/lib/secrets/repl.py +2 -3
  44. mcli/lib/secrets/store.py +1 -2
  45. mcli/lib/services/data_pipeline.py +34 -34
  46. mcli/lib/services/lsh_client.py +38 -40
  47. mcli/lib/shell/shell.py +2 -2
  48. mcli/lib/toml/__init__.py +0 -1
  49. mcli/lib/ui/styling.py +0 -1
  50. mcli/lib/ui/visual_effects.py +33 -41
  51. mcli/lib/watcher/watcher.py +0 -1
  52. mcli/ml/__init__.py +1 -1
  53. mcli/ml/api/__init__.py +1 -1
  54. mcli/ml/api/app.py +8 -9
  55. mcli/ml/api/middleware.py +10 -10
  56. mcli/ml/api/routers/__init__.py +1 -1
  57. mcli/ml/api/routers/admin_router.py +3 -3
  58. mcli/ml/api/routers/auth_router.py +17 -18
  59. mcli/ml/api/routers/backtest_router.py +2 -2
  60. mcli/ml/api/routers/data_router.py +2 -2
  61. mcli/ml/api/routers/model_router.py +14 -15
  62. mcli/ml/api/routers/monitoring_router.py +2 -2
  63. mcli/ml/api/routers/portfolio_router.py +2 -2
  64. mcli/ml/api/routers/prediction_router.py +10 -9
  65. mcli/ml/api/routers/trade_router.py +2 -2
  66. mcli/ml/api/routers/websocket_router.py +6 -7
  67. mcli/ml/api/schemas.py +2 -2
  68. mcli/ml/auth/__init__.py +1 -1
  69. mcli/ml/auth/auth_manager.py +22 -23
  70. mcli/ml/auth/models.py +17 -17
  71. mcli/ml/auth/permissions.py +17 -17
  72. mcli/ml/backtesting/__init__.py +1 -1
  73. mcli/ml/backtesting/backtest_engine.py +31 -35
  74. mcli/ml/backtesting/performance_metrics.py +12 -14
  75. mcli/ml/backtesting/run.py +1 -2
  76. mcli/ml/cache.py +35 -36
  77. mcli/ml/cli/__init__.py +1 -1
  78. mcli/ml/cli/main.py +21 -24
  79. mcli/ml/config/__init__.py +1 -1
  80. mcli/ml/config/settings.py +28 -29
  81. mcli/ml/configs/__init__.py +1 -1
  82. mcli/ml/configs/dvc_config.py +14 -15
  83. mcli/ml/configs/mlflow_config.py +12 -13
  84. mcli/ml/configs/mlops_manager.py +19 -21
  85. mcli/ml/dashboard/__init__.py +4 -4
  86. mcli/ml/dashboard/app.py +20 -30
  87. mcli/ml/dashboard/app_supabase.py +16 -19
  88. mcli/ml/dashboard/app_training.py +11 -14
  89. mcli/ml/dashboard/cli.py +2 -2
  90. mcli/ml/dashboard/common.py +2 -3
  91. mcli/ml/dashboard/components/__init__.py +1 -1
  92. mcli/ml/dashboard/components/charts.py +13 -11
  93. mcli/ml/dashboard/components/metrics.py +7 -7
  94. mcli/ml/dashboard/components/tables.py +12 -9
  95. mcli/ml/dashboard/overview.py +2 -2
  96. mcli/ml/dashboard/pages/__init__.py +1 -1
  97. mcli/ml/dashboard/pages/cicd.py +15 -18
  98. mcli/ml/dashboard/pages/debug_dependencies.py +7 -7
  99. mcli/ml/dashboard/pages/monte_carlo_predictions.py +11 -18
  100. mcli/ml/dashboard/pages/predictions_enhanced.py +24 -32
  101. mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -24
  102. mcli/ml/dashboard/pages/test_portfolio.py +3 -6
  103. mcli/ml/dashboard/pages/trading.py +16 -18
  104. mcli/ml/dashboard/pages/workflows.py +20 -30
  105. mcli/ml/dashboard/utils.py +9 -9
  106. mcli/ml/dashboard/warning_suppression.py +3 -3
  107. mcli/ml/data_ingestion/__init__.py +1 -1
  108. mcli/ml/data_ingestion/api_connectors.py +41 -46
  109. mcli/ml/data_ingestion/data_pipeline.py +36 -46
  110. mcli/ml/data_ingestion/stream_processor.py +43 -46
  111. mcli/ml/database/__init__.py +1 -1
  112. mcli/ml/database/migrations/env.py +2 -2
  113. mcli/ml/database/models.py +22 -24
  114. mcli/ml/database/session.py +14 -14
  115. mcli/ml/experimentation/__init__.py +1 -1
  116. mcli/ml/experimentation/ab_testing.py +45 -46
  117. mcli/ml/features/__init__.py +1 -1
  118. mcli/ml/features/ensemble_features.py +22 -27
  119. mcli/ml/features/recommendation_engine.py +30 -30
  120. mcli/ml/features/stock_features.py +29 -32
  121. mcli/ml/features/test_feature_engineering.py +10 -11
  122. mcli/ml/logging.py +4 -4
  123. mcli/ml/mlops/__init__.py +1 -1
  124. mcli/ml/mlops/data_versioning.py +29 -30
  125. mcli/ml/mlops/experiment_tracker.py +24 -24
  126. mcli/ml/mlops/model_serving.py +31 -34
  127. mcli/ml/mlops/pipeline_orchestrator.py +27 -35
  128. mcli/ml/models/__init__.py +5 -6
  129. mcli/ml/models/base_models.py +23 -23
  130. mcli/ml/models/ensemble_models.py +31 -31
  131. mcli/ml/models/recommendation_models.py +18 -19
  132. mcli/ml/models/test_models.py +14 -16
  133. mcli/ml/monitoring/__init__.py +1 -1
  134. mcli/ml/monitoring/drift_detection.py +32 -36
  135. mcli/ml/monitoring/metrics.py +2 -2
  136. mcli/ml/optimization/__init__.py +1 -1
  137. mcli/ml/optimization/optimize.py +1 -2
  138. mcli/ml/optimization/portfolio_optimizer.py +30 -32
  139. mcli/ml/predictions/__init__.py +1 -1
  140. mcli/ml/preprocessing/__init__.py +1 -1
  141. mcli/ml/preprocessing/data_cleaners.py +22 -23
  142. mcli/ml/preprocessing/feature_extractors.py +23 -26
  143. mcli/ml/preprocessing/ml_pipeline.py +23 -23
  144. mcli/ml/preprocessing/test_preprocessing.py +7 -8
  145. mcli/ml/scripts/populate_sample_data.py +0 -4
  146. mcli/ml/serving/serve.py +1 -2
  147. mcli/ml/tasks.py +17 -17
  148. mcli/ml/tests/test_integration.py +29 -30
  149. mcli/ml/tests/test_training_dashboard.py +21 -21
  150. mcli/ml/trading/__init__.py +1 -1
  151. mcli/ml/trading/migrations.py +5 -5
  152. mcli/ml/trading/models.py +21 -23
  153. mcli/ml/trading/paper_trading.py +16 -13
  154. mcli/ml/trading/risk_management.py +17 -18
  155. mcli/ml/trading/trading_service.py +25 -28
  156. mcli/ml/training/__init__.py +1 -1
  157. mcli/ml/training/train.py +0 -1
  158. mcli/public/oi/oi.py +1 -2
  159. mcli/self/completion_cmd.py +6 -10
  160. mcli/self/logs_cmd.py +19 -24
  161. mcli/self/migrate_cmd.py +22 -20
  162. mcli/self/redis_cmd.py +10 -11
  163. mcli/self/self_cmd.py +62 -18
  164. mcli/self/store_cmd.py +10 -12
  165. mcli/self/visual_cmd.py +9 -14
  166. mcli/self/zsh_cmd.py +2 -4
  167. mcli/workflow/daemon/async_command_database.py +23 -24
  168. mcli/workflow/daemon/async_process_manager.py +27 -29
  169. mcli/workflow/daemon/client.py +27 -33
  170. mcli/workflow/daemon/daemon.py +32 -36
  171. mcli/workflow/daemon/enhanced_daemon.py +24 -33
  172. mcli/workflow/daemon/process_cli.py +11 -12
  173. mcli/workflow/daemon/process_manager.py +23 -26
  174. mcli/workflow/daemon/test_daemon.py +4 -5
  175. mcli/workflow/dashboard/dashboard_cmd.py +0 -1
  176. mcli/workflow/doc_convert.py +15 -17
  177. mcli/workflow/gcloud/__init__.py +0 -1
  178. mcli/workflow/gcloud/gcloud.py +11 -8
  179. mcli/workflow/git_commit/ai_service.py +14 -15
  180. mcli/workflow/lsh_integration.py +9 -11
  181. mcli/workflow/model_service/client.py +26 -31
  182. mcli/workflow/model_service/download_and_run_efficient_models.py +10 -14
  183. mcli/workflow/model_service/lightweight_embedder.py +25 -35
  184. mcli/workflow/model_service/lightweight_model_server.py +26 -32
  185. mcli/workflow/model_service/lightweight_test.py +7 -10
  186. mcli/workflow/model_service/model_service.py +80 -91
  187. mcli/workflow/model_service/ollama_efficient_runner.py +14 -18
  188. mcli/workflow/model_service/openai_adapter.py +23 -23
  189. mcli/workflow/model_service/pdf_processor.py +21 -26
  190. mcli/workflow/model_service/test_efficient_runner.py +12 -16
  191. mcli/workflow/model_service/test_example.py +11 -13
  192. mcli/workflow/model_service/test_integration.py +3 -5
  193. mcli/workflow/model_service/test_new_features.py +7 -8
  194. mcli/workflow/notebook/converter.py +1 -1
  195. mcli/workflow/notebook/notebook_cmd.py +5 -6
  196. mcli/workflow/notebook/schema.py +0 -1
  197. mcli/workflow/notebook/validator.py +7 -3
  198. mcli/workflow/openai/openai.py +1 -2
  199. mcli/workflow/registry/registry.py +4 -1
  200. mcli/workflow/repo/repo.py +6 -7
  201. mcli/workflow/scheduler/cron_parser.py +16 -19
  202. mcli/workflow/scheduler/job.py +10 -10
  203. mcli/workflow/scheduler/monitor.py +15 -15
  204. mcli/workflow/scheduler/persistence.py +17 -18
  205. mcli/workflow/scheduler/scheduler.py +37 -38
  206. mcli/workflow/secrets/__init__.py +1 -1
  207. mcli/workflow/sync/test_cmd.py +0 -1
  208. mcli/workflow/wakatime/__init__.py +5 -9
  209. mcli/workflow/wakatime/wakatime.py +1 -2
  210. {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/METADATA +1 -1
  211. mcli_framework-7.12.3.dist-info/RECORD +279 -0
  212. mcli_framework-7.12.0.dist-info/RECORD +0 -279
  213. {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/WHEEL +0 -0
  214. {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/entry_points.txt +0 -0
  215. {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/licenses/LICENSE +0 -0
  216. {mcli_framework-7.12.0.dist-info → mcli_framework-7.12.3.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
- """Permission management system"""
1
+ """Permission management system."""
2
2
 
3
3
  from datetime import datetime
4
4
  from enum import Enum
5
5
  from functools import wraps
6
- from typing import Any, Dict, List, Set
6
+ from typing import Any, Dict, Set
7
7
 
8
8
  from fastapi import HTTPException, status
9
9
  from sqlalchemy.orm import Session
@@ -12,7 +12,7 @@ from mcli.ml.database.models import User, UserRole
12
12
 
13
13
 
14
14
  class Permission(Enum):
15
- """System permissions"""
15
+ """System permissions."""
16
16
 
17
17
  # Model permissions
18
18
  MODEL_VIEW = "model:view"
@@ -97,13 +97,13 @@ ROLE_PERMISSIONS: Dict[UserRole, Set[Permission]] = {
97
97
 
98
98
 
99
99
  def has_permission(user: User, permission: Permission) -> bool:
100
- """Check if user has specific permission"""
100
+ """Check if user has specific permission."""
101
101
  user_permissions = ROLE_PERMISSIONS.get(user.role, set())
102
102
  return permission in user_permissions
103
103
 
104
104
 
105
105
  def check_permission(user: User, permission: Permission) -> None:
106
- """Check permission and raise exception if not allowed"""
106
+ """Check permission and raise exception if not allowed."""
107
107
  if not has_permission(user, permission):
108
108
  raise HTTPException(
109
109
  status_code=status.HTTP_403_FORBIDDEN, detail=f"Permission denied: {permission.value}"
@@ -111,7 +111,7 @@ def check_permission(user: User, permission: Permission) -> None:
111
111
 
112
112
 
113
113
  def require_permission(permission: Permission):
114
- """Decorator to require specific permission"""
114
+ """Decorator to require specific permission."""
115
115
 
116
116
  def decorator(func):
117
117
  @wraps(func)
@@ -133,7 +133,7 @@ def require_permission(permission: Permission):
133
133
 
134
134
 
135
135
  def require_any_permission(*permissions: Permission):
136
- """Decorator to require any of the specified permissions"""
136
+ """Decorator to require any of the specified permissions."""
137
137
 
138
138
  def decorator(func):
139
139
  @wraps(func)
@@ -159,7 +159,7 @@ def require_any_permission(*permissions: Permission):
159
159
 
160
160
 
161
161
  def require_all_permissions(*permissions: Permission):
162
- """Decorator to require all of the specified permissions"""
162
+ """Decorator to require all of the specified permissions."""
163
163
 
164
164
  def decorator(func):
165
165
  @wraps(func)
@@ -182,7 +182,7 @@ def require_all_permissions(*permissions: Permission):
182
182
 
183
183
 
184
184
  class PermissionChecker:
185
- """FastAPI dependency for permission checking"""
185
+ """FastAPI dependency for permission checking."""
186
186
 
187
187
  def __init__(self, permission: Permission):
188
188
  self.permission = permission
@@ -194,11 +194,11 @@ class PermissionChecker:
194
194
 
195
195
  # Resource-based permissions
196
196
  class ResourcePermission:
197
- """Check permissions for specific resources"""
197
+ """Check permissions for specific resources."""
198
198
 
199
199
  @staticmethod
200
200
  def can_edit_portfolio(user: User, portfolio) -> bool:
201
- """Check if user can edit a specific portfolio"""
201
+ """Check if user can edit a specific portfolio."""
202
202
  # Admin can edit any portfolio
203
203
  if user.role == UserRole.ADMIN:
204
204
  return True
@@ -211,7 +211,7 @@ class ResourcePermission:
211
211
 
212
212
  @staticmethod
213
213
  def can_view_portfolio(user: User, portfolio) -> bool:
214
- """Check if user can view a specific portfolio"""
214
+ """Check if user can view a specific portfolio."""
215
215
  # Admin can view any portfolio
216
216
  if user.role == UserRole.ADMIN:
217
217
  return True
@@ -228,20 +228,20 @@ class ResourcePermission:
228
228
 
229
229
  @staticmethod
230
230
  def can_delete_model(user: User, model) -> bool:
231
- """Check if user can delete a specific model"""
231
+ """Check if user can delete a specific model."""
232
232
  # Only admin can delete models
233
233
  return user.role == UserRole.ADMIN
234
234
 
235
235
  @staticmethod
236
236
  def can_deploy_model(user: User, model) -> bool:
237
- """Check if user can deploy a specific model"""
237
+ """Check if user can deploy a specific model."""
238
238
  # Admin and Analyst can deploy models
239
239
  return user.role in [UserRole.ADMIN, UserRole.ANALYST]
240
240
 
241
241
 
242
242
  # Audit logging
243
243
  class AuditLogger:
244
- """Log permission-related actions"""
244
+ """Log permission-related actions."""
245
245
 
246
246
  @staticmethod
247
247
  async def log_access(
@@ -252,7 +252,7 @@ class AuditLogger:
252
252
  details: Dict[str, Any] = None,
253
253
  db: Session = None,
254
254
  ):
255
- """Log access attempt"""
255
+ """Log access attempt."""
256
256
  log_entry = {
257
257
  "user_id": str(user.id),
258
258
  "username": user.username,
@@ -269,7 +269,7 @@ class AuditLogger:
269
269
 
270
270
  # Permission groups for easier management
271
271
  class PermissionGroup:
272
- """Predefined permission groups"""
272
+ """Predefined permission groups."""
273
273
 
274
274
  BASIC_USER = {
275
275
  Permission.MODEL_VIEW,
@@ -1,4 +1,4 @@
1
- """Backtesting framework for trading strategies"""
1
+ """Backtesting framework for trading strategies."""
2
2
 
3
3
  from .backtest_engine import (
4
4
  BacktestConfig,
@@ -1,14 +1,10 @@
1
- """Backtesting engine for trading strategies"""
1
+ """Backtesting engine for trading strategies."""
2
2
 
3
- import json
4
3
  import logging
5
- import os
6
- import sys
7
- from dataclasses import dataclass, field
8
- from datetime import datetime, timedelta
4
+ from dataclasses import dataclass
5
+ from datetime import datetime
9
6
  from enum import Enum
10
- from pathlib import Path
11
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
7
+ from typing import Any, Dict, List, Optional
12
8
 
13
9
  import numpy as np
14
10
  import pandas as pd
@@ -19,7 +15,7 @@ logger = logging.getLogger(__name__)
19
15
 
20
16
 
21
17
  class OrderType(Enum):
22
- """Order types"""
18
+ """Order types."""
23
19
 
24
20
  MARKET = "market"
25
21
  LIMIT = "limit"
@@ -28,7 +24,7 @@ class OrderType(Enum):
28
24
 
29
25
 
30
26
  class OrderSide(Enum):
31
- """Order side"""
27
+ """Order side."""
32
28
 
33
29
  BUY = "buy"
34
30
  SELL = "sell"
@@ -36,7 +32,7 @@ class OrderSide(Enum):
36
32
 
37
33
  @dataclass
38
34
  class BacktestConfig:
39
- """Backtesting configuration"""
35
+ """Backtesting configuration."""
40
36
 
41
37
  initial_capital: float = 100000.0
42
38
  start_date: Optional[datetime] = None
@@ -55,7 +51,7 @@ class BacktestConfig:
55
51
 
56
52
  @dataclass
57
53
  class BacktestResult:
58
- """Backtesting results"""
54
+ """Backtesting results."""
59
55
 
60
56
  portfolio_value: pd.Series
61
57
  returns: pd.Series
@@ -67,7 +63,7 @@ class BacktestResult:
67
63
 
68
64
 
69
65
  class TradingStrategy:
70
- """Base trading strategy class"""
66
+ """Base trading strategy class."""
71
67
 
72
68
  def __init__(self, model: Optional[StockRecommendationModel] = None):
73
69
  self.model = model
@@ -77,7 +73,7 @@ class TradingStrategy:
77
73
  def generate_signals(
78
74
  self, data: pd.DataFrame, current_date: datetime, portfolio_value: float
79
75
  ) -> List[Dict[str, Any]]:
80
- """Generate trading signals"""
76
+ """Generate trading signals."""
81
77
  signals = []
82
78
 
83
79
  if self.model:
@@ -104,7 +100,7 @@ class TradingStrategy:
104
100
  def _get_model_recommendations(
105
101
  self, data: pd.DataFrame, current_date: datetime
106
102
  ) -> List[PortfolioRecommendation]:
107
- """Get recommendations from ML model"""
103
+ """Get recommendations from ML model."""
108
104
  # Filter data up to current date
109
105
  historical_data = data[data["date"] <= current_date]
110
106
 
@@ -125,7 +121,7 @@ class TradingStrategy:
125
121
  def _momentum_strategy(
126
122
  self, data: pd.DataFrame, current_date: datetime
127
123
  ) -> List[Dict[str, Any]]:
128
- """Simple momentum strategy"""
124
+ """Simple momentum strategy."""
129
125
  signals = []
130
126
 
131
127
  # Get recent data
@@ -163,7 +159,7 @@ class TradingStrategy:
163
159
 
164
160
 
165
161
  class PositionManager:
166
- """Manage portfolio positions"""
162
+ """Manage portfolio positions."""
167
163
 
168
164
  def __init__(self, config: BacktestConfig):
169
165
  self.config = config
@@ -174,7 +170,7 @@ class PositionManager:
174
170
  def open_position(
175
171
  self, ticker: str, quantity: int, price: float, date: datetime, signal: Dict[str, Any]
176
172
  ):
177
- """Open a new position"""
173
+ """Open a new position."""
178
174
  cost = quantity * price * (1 + self.config.commission + self.config.slippage)
179
175
 
180
176
  if cost > self.cash:
@@ -197,7 +193,7 @@ class PositionManager:
197
193
  return True
198
194
 
199
195
  def close_position(self, ticker: str, price: float, date: datetime) -> float:
200
- """Close a position"""
196
+ """Close a position."""
201
197
  if ticker not in self.positions:
202
198
  return 0
203
199
 
@@ -218,7 +214,7 @@ class PositionManager:
218
214
  return realized_pnl
219
215
 
220
216
  def update_positions(self, price_data: Dict[str, float]):
221
- """Update position prices and calculate unrealized PnL"""
217
+ """Update position prices and calculate unrealized PnL."""
222
218
  for ticker, position in self.positions.items():
223
219
  if ticker in price_data:
224
220
  current_price = price_data[ticker]
@@ -242,7 +238,7 @@ class PositionManager:
242
238
  return None, None
243
239
 
244
240
  def get_portfolio_value(self, price_data: Dict[str, float]) -> float:
245
- """Calculate total portfolio value"""
241
+ """Calculate total portfolio value."""
246
242
  positions_value = sum(
247
243
  pos["quantity"] * price_data.get(ticker, pos["current_price"])
248
244
  for ticker, pos in self.positions.items()
@@ -250,7 +246,7 @@ class PositionManager:
250
246
  return self.cash + positions_value
251
247
 
252
248
  def get_position_weights(self, price_data: Dict[str, float]) -> Dict[str, float]:
253
- """Get position weights"""
249
+ """Get position weights."""
254
250
  portfolio_value = self.get_portfolio_value(price_data)
255
251
  weights = {}
256
252
 
@@ -263,7 +259,7 @@ class PositionManager:
263
259
 
264
260
 
265
261
  class BacktestEngine:
266
- """Main backtesting engine"""
262
+ """Main backtesting engine."""
267
263
 
268
264
  def __init__(self, config: BacktestConfig):
269
265
  self.config = config
@@ -273,13 +269,13 @@ class BacktestEngine:
273
269
  self.strategy = None
274
270
 
275
271
  def set_strategy(self, strategy: TradingStrategy):
276
- """Set trading strategy"""
272
+ """Set trading strategy."""
277
273
  self.strategy = strategy
278
274
 
279
275
  def run(
280
276
  self, price_data: pd.DataFrame, trading_data: Optional[pd.DataFrame] = None
281
277
  ) -> BacktestResult:
282
- """Run backtest"""
278
+ """Run backtest."""
283
279
  logger.info("Starting backtest...")
284
280
 
285
281
  # Prepare data
@@ -367,12 +363,12 @@ class BacktestEngine:
367
363
  def _get_current_prices(
368
364
  self, price_data: pd.DataFrame, current_date: datetime
369
365
  ) -> Dict[str, float]:
370
- """Get current prices for all tickers"""
366
+ """Get current prices for all tickers."""
371
367
  current_data = price_data[price_data["date"] == current_date]
372
368
  return dict(zip(current_data["symbol"], current_data["close"]))
373
369
 
374
370
  def _should_rebalance(self, day_index: int, current_date: datetime) -> bool:
375
- """Check if should rebalance portfolio"""
371
+ """Check if should rebalance portfolio."""
376
372
  if self.config.rebalance_frequency == "daily":
377
373
  return True
378
374
  elif self.config.rebalance_frequency == "weekly":
@@ -387,7 +383,7 @@ class BacktestEngine:
387
383
  current_prices: Dict[str, float],
388
384
  current_date: datetime,
389
385
  ):
390
- """Execute trading signals"""
386
+ """Execute trading signals."""
391
387
  for signal in signals:
392
388
  ticker = signal["ticker"]
393
389
 
@@ -402,9 +398,9 @@ class BacktestEngine:
402
398
  position_value = portfolio_value * signal.get("position_size", 0.05)
403
399
  quantity = int(position_value / price)
404
400
 
405
- if quantity > 0:
401
+ if quantity > 0: # noqa: SIM102
406
402
  # Check if already have position
407
- if ticker not in self.position_manager.positions:
403
+ if ticker not in self.position_manager.positions: # noqa: SIM102
408
404
  # Check max positions
409
405
  if len(self.position_manager.positions) < self.config.max_positions:
410
406
  success = self.position_manager.open_position(
@@ -423,7 +419,7 @@ class BacktestEngine:
423
419
  }
424
420
  )
425
421
 
426
- elif signal["action"] == "sell":
422
+ elif signal["action"] == "sell": # noqa: SIM102
427
423
  if ticker in self.position_manager.positions:
428
424
  pnl = self.position_manager.close_position(ticker, price, current_date)
429
425
 
@@ -441,7 +437,7 @@ class BacktestEngine:
441
437
  )
442
438
 
443
439
  def _execute_exit(self, ticker: str, price: float, current_date: datetime, exit_type: str):
444
- """Execute position exit"""
440
+ """Execute position exit."""
445
441
  if ticker in self.position_manager.positions:
446
442
  position = self.position_manager.positions[ticker]
447
443
  pnl = self.position_manager.close_position(ticker, price, current_date)
@@ -460,7 +456,7 @@ class BacktestEngine:
460
456
  def _get_position_snapshot(
461
457
  self, date: datetime, current_prices: Dict[str, float]
462
458
  ) -> Dict[str, Any]:
463
- """Get current position snapshot"""
459
+ """Get current position snapshot."""
464
460
  snapshot = {
465
461
  "date": date,
466
462
  "num_positions": len(self.position_manager.positions),
@@ -476,7 +472,7 @@ class BacktestEngine:
476
472
  return snapshot
477
473
 
478
474
  def _calculate_metrics(self, portfolio_df: pd.DataFrame) -> Dict[str, float]:
479
- """Calculate performance metrics"""
475
+ """Calculate performance metrics."""
480
476
  returns = portfolio_df["returns"]
481
477
 
482
478
  # Basic metrics
@@ -515,7 +511,7 @@ class BacktestEngine:
515
511
  def _get_benchmark_returns(
516
512
  self, price_data: pd.DataFrame, dates: np.ndarray
517
513
  ) -> Optional[pd.Series]:
518
- """Get benchmark returns"""
514
+ """Get benchmark returns."""
519
515
  if self.config.benchmark not in price_data["symbol"].unique():
520
516
  return None
521
517
 
@@ -1,21 +1,19 @@
1
- """Performance metrics and analysis for backtesting"""
1
+ """Performance metrics and analysis for backtesting."""
2
2
 
3
3
  import logging
4
4
  from dataclasses import dataclass
5
- from datetime import datetime
6
- from typing import Any, Dict, List, Optional, Tuple
5
+ from typing import Dict, Optional, Tuple
7
6
 
8
7
  import matplotlib.pyplot as plt
9
8
  import numpy as np
10
9
  import pandas as pd
11
- import seaborn as sns
12
10
 
13
11
  logger = logging.getLogger(__name__)
14
12
 
15
13
 
16
14
  @dataclass
17
15
  class PortfolioMetrics:
18
- """Portfolio performance metrics"""
16
+ """Portfolio performance metrics."""
19
17
 
20
18
  total_return: float
21
19
  annualized_return: float
@@ -39,7 +37,7 @@ class PortfolioMetrics:
39
37
 
40
38
  @dataclass
41
39
  class RiskMetrics:
42
- """Risk metrics"""
40
+ """Risk metrics."""
43
41
 
44
42
  value_at_risk_95: float
45
43
  conditional_var_95: float
@@ -56,7 +54,7 @@ class RiskMetrics:
56
54
 
57
55
 
58
56
  class PerformanceAnalyzer:
59
- """Analyze backtest performance"""
57
+ """Analyze backtest performance."""
60
58
 
61
59
  def __init__(self, risk_free_rate: float = 0.02):
62
60
  self.risk_free_rate = risk_free_rate
@@ -67,7 +65,7 @@ class PerformanceAnalyzer:
67
65
  benchmark_returns: Optional[pd.Series] = None,
68
66
  trades: Optional[pd.DataFrame] = None,
69
67
  ) -> Tuple[PortfolioMetrics, RiskMetrics]:
70
- """Calculate comprehensive performance metrics"""
68
+ """Calculate comprehensive performance metrics."""
71
69
 
72
70
  # Portfolio metrics
73
71
  portfolio_metrics = self._calculate_portfolio_metrics(returns, trades)
@@ -80,7 +78,7 @@ class PerformanceAnalyzer:
80
78
  def _calculate_portfolio_metrics(
81
79
  self, returns: pd.Series, trades: Optional[pd.DataFrame] = None
82
80
  ) -> PortfolioMetrics:
83
- """Calculate portfolio performance metrics"""
81
+ """Calculate portfolio performance metrics."""
84
82
 
85
83
  # Basic returns
86
84
  total_return = (1 + returns).prod() - 1
@@ -148,7 +146,7 @@ class PerformanceAnalyzer:
148
146
  def _calculate_risk_metrics(
149
147
  self, returns: pd.Series, benchmark_returns: Optional[pd.Series] = None
150
148
  ) -> RiskMetrics:
151
- """Calculate risk metrics"""
149
+ """Calculate risk metrics."""
152
150
 
153
151
  # Value at Risk (VaR)
154
152
  var_95 = np.percentile(returns, 5)
@@ -231,7 +229,7 @@ class PerformanceAnalyzer:
231
229
  )
232
230
 
233
231
  def _calculate_max_drawdown_duration(self, drawdown: pd.Series) -> int:
234
- """Calculate maximum drawdown duration in days"""
232
+ """Calculate maximum drawdown duration in days."""
235
233
  is_drawdown = drawdown < 0
236
234
  drawdown_periods = []
237
235
  current_duration = 0
@@ -250,7 +248,7 @@ class PerformanceAnalyzer:
250
248
  return max(drawdown_periods) if drawdown_periods else 0
251
249
 
252
250
  def _analyze_trades(self, trades: pd.DataFrame) -> Dict[str, float]:
253
- """Analyze trade statistics"""
251
+ """Analyze trade statistics."""
254
252
  # Filter for trades with PnL
255
253
  pnl_trades = trades[trades["pnl"].notna()].copy()
256
254
 
@@ -308,7 +306,7 @@ class PerformanceAnalyzer:
308
306
  }
309
307
 
310
308
  def _max_consecutive(self, arr: np.ndarray, value: bool) -> int:
311
- """Calculate maximum consecutive occurrences of value"""
309
+ """Calculate maximum consecutive occurrences of value."""
312
310
  max_count = 0
313
311
  current_count = 0
314
312
 
@@ -323,7 +321,7 @@ class PerformanceAnalyzer:
323
321
 
324
322
 
325
323
  def plot_performance(backtest_result, save_path: Optional[str] = None):
326
- """Plot backtest performance charts"""
324
+ """Plot backtest performance charts."""
327
325
  fig, axes = plt.subplots(3, 2, figsize=(15, 12))
328
326
 
329
327
  # Portfolio value
@@ -3,13 +3,12 @@
3
3
 
4
4
  import click
5
5
 
6
- from mcli.lib.ui.styling import error, info, success
6
+ from mcli.lib.ui.styling import error, info
7
7
 
8
8
 
9
9
  @click.group(name="mcli-backtest", help="Backtesting CLI for MCLI trading strategies")
10
10
  def cli():
11
11
  """Main CLI group for backtesting."""
12
- pass
13
12
 
14
13
 
15
14
  @cli.command(name="run", help="Run a backtest on historical data")