mcli-framework 7.3.1__py3-none-any.whl → 7.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.
Potentially problematic release.
This version of mcli-framework might be problematic. Click here for more details.
- mcli/app/commands_cmd.py +741 -0
- mcli/ml/dashboard/app_integrated.py +286 -37
- mcli/ml/dashboard/app_training.py +1 -1
- mcli/ml/dashboard/pages/cicd.py +1 -1
- mcli/ml/dashboard/pages/debug_dependencies.py +364 -0
- mcli/ml/dashboard/pages/gravity_viz.py +565 -0
- mcli/ml/dashboard/pages/monte_carlo_predictions.py +555 -0
- mcli/ml/dashboard/pages/overview.py +378 -0
- mcli/ml/dashboard/pages/scrapers_and_logs.py +22 -6
- mcli/ml/dashboard/pages/test_portfolio.py +54 -4
- mcli/ml/dashboard/pages/trading.py +80 -26
- mcli/ml/dashboard/streamlit_extras_utils.py +297 -0
- mcli/ml/dashboard/utils.py +7 -0
- mcli/ml/dashboard/warning_suppression.py +34 -0
- mcli/ml/database/session.py +169 -16
- mcli/ml/predictions/monte_carlo.py +428 -0
- mcli/ml/trading/__init__.py +7 -1
- mcli/ml/trading/alpaca_client.py +82 -18
- mcli/self/self_cmd.py +263 -24
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/METADATA +3 -2
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/RECORD +25 -18
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/WHEEL +0 -0
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/entry_points.txt +0 -0
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/licenses/LICENSE +0 -0
- {mcli_framework-7.3.1.dist-info → mcli_framework-7.4.0.dist-info}/top_level.txt +0 -0
mcli/ml/trading/alpaca_client.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Alpaca Trading API client for executing trades"""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
import os
|
|
4
5
|
from datetime import datetime, timedelta
|
|
5
6
|
from decimal import Decimal
|
|
6
7
|
from typing import Dict, List, Optional, Tuple, Union
|
|
@@ -18,6 +19,10 @@ from alpaca.data.historical import StockHistoricalDataClient
|
|
|
18
19
|
from alpaca.data.requests import StockBarsRequest
|
|
19
20
|
from alpaca.data.timeframe import TimeFrame
|
|
20
21
|
from pydantic import BaseModel, Field
|
|
22
|
+
from dotenv import load_dotenv
|
|
23
|
+
|
|
24
|
+
# Load environment variables
|
|
25
|
+
load_dotenv()
|
|
21
26
|
|
|
22
27
|
logger = logging.getLogger(__name__)
|
|
23
28
|
|
|
@@ -81,32 +86,44 @@ class AlpacaTradingClient:
|
|
|
81
86
|
self.trading_client = TradingClient(
|
|
82
87
|
api_key=config.api_key,
|
|
83
88
|
secret_key=config.secret_key,
|
|
84
|
-
paper=config.paper_trading
|
|
85
|
-
base_url=config.base_url
|
|
89
|
+
paper=config.paper_trading
|
|
86
90
|
)
|
|
87
91
|
self.data_client = StockHistoricalDataClient(
|
|
88
92
|
api_key=config.api_key,
|
|
89
|
-
secret_key=config.secret_key
|
|
90
|
-
base_url=config.data_url
|
|
93
|
+
secret_key=config.secret_key
|
|
91
94
|
)
|
|
92
95
|
|
|
93
96
|
def get_account(self) -> Dict:
|
|
94
97
|
"""Get account information"""
|
|
95
98
|
try:
|
|
96
99
|
account = self.trading_client.get_account()
|
|
97
|
-
|
|
100
|
+
|
|
101
|
+
# Build response with safe attribute access
|
|
102
|
+
response = {
|
|
98
103
|
"account_id": account.id,
|
|
99
|
-
"equity": float(account.equity),
|
|
100
|
-
"cash": float(account.cash),
|
|
101
|
-
"buying_power": float(account.buying_power),
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"status": account.status,
|
|
107
|
-
"trading_blocked": account.trading_blocked,
|
|
108
|
-
"pattern_day_trader": account.pattern_day_trader,
|
|
104
|
+
"equity": float(account.equity) if hasattr(account, 'equity') else 0.0,
|
|
105
|
+
"cash": float(account.cash) if hasattr(account, 'cash') else 0.0,
|
|
106
|
+
"buying_power": float(account.buying_power) if hasattr(account, 'buying_power') else 0.0,
|
|
107
|
+
"currency": account.currency if hasattr(account, 'currency') else "USD",
|
|
108
|
+
"status": account.status.value if hasattr(account.status, 'value') else str(account.status),
|
|
109
|
+
"trading_blocked": account.trading_blocked if hasattr(account, 'trading_blocked') else False,
|
|
110
|
+
"pattern_day_trader": account.pattern_day_trader if hasattr(account, 'pattern_day_trader') else False,
|
|
109
111
|
}
|
|
112
|
+
|
|
113
|
+
# Add optional fields that may not exist in all account types
|
|
114
|
+
if hasattr(account, 'portfolio_value'):
|
|
115
|
+
response["portfolio_value"] = float(account.portfolio_value)
|
|
116
|
+
else:
|
|
117
|
+
response["portfolio_value"] = response["equity"]
|
|
118
|
+
|
|
119
|
+
if hasattr(account, 'long_market_value'):
|
|
120
|
+
response["unrealized_pl"] = float(account.long_market_value) - float(account.cash)
|
|
121
|
+
else:
|
|
122
|
+
response["unrealized_pl"] = 0.0
|
|
123
|
+
|
|
124
|
+
response["realized_pl"] = 0.0 # Not always available in paper accounts
|
|
125
|
+
|
|
126
|
+
return response
|
|
110
127
|
except Exception as e:
|
|
111
128
|
logger.error(f"Failed to get account info: {e}")
|
|
112
129
|
raise
|
|
@@ -343,11 +360,58 @@ class AlpacaTradingClient:
|
|
|
343
360
|
raise
|
|
344
361
|
|
|
345
362
|
|
|
346
|
-
def create_trading_client(api_key: str, secret_key: str, paper_trading: bool = True) -> AlpacaTradingClient:
|
|
347
|
-
"""
|
|
363
|
+
def create_trading_client(api_key: str = None, secret_key: str = None, paper_trading: bool = True) -> AlpacaTradingClient:
|
|
364
|
+
"""
|
|
365
|
+
Create a trading client with the given credentials or from environment variables
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
api_key: Alpaca API key (if None, loads from ALPACA_API_KEY env var)
|
|
369
|
+
secret_key: Alpaca secret key (if None, loads from ALPACA_SECRET_KEY env var)
|
|
370
|
+
paper_trading: Whether to use paper trading (default: True)
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
AlpacaTradingClient instance
|
|
374
|
+
"""
|
|
375
|
+
# Load from environment if not provided
|
|
376
|
+
if api_key is None:
|
|
377
|
+
api_key = os.getenv("ALPACA_API_KEY")
|
|
378
|
+
if secret_key is None:
|
|
379
|
+
secret_key = os.getenv("ALPACA_SECRET_KEY")
|
|
380
|
+
|
|
381
|
+
if not api_key or not secret_key:
|
|
382
|
+
raise ValueError(
|
|
383
|
+
"Alpaca API credentials not found. "
|
|
384
|
+
"Please provide api_key and secret_key, or set ALPACA_API_KEY and ALPACA_SECRET_KEY environment variables."
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
base_url = os.getenv("ALPACA_BASE_URL", "https://paper-api.alpaca.markets")
|
|
388
|
+
|
|
348
389
|
config = TradingConfig(
|
|
349
390
|
api_key=api_key,
|
|
350
391
|
secret_key=secret_key,
|
|
392
|
+
base_url=base_url,
|
|
351
393
|
paper_trading=paper_trading
|
|
352
394
|
)
|
|
353
|
-
return AlpacaTradingClient(config)
|
|
395
|
+
return AlpacaTradingClient(config)
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def get_alpaca_config_from_env() -> Optional[Dict[str, str]]:
|
|
399
|
+
"""
|
|
400
|
+
Get Alpaca configuration from environment variables
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
Dictionary with API configuration or None if not configured
|
|
404
|
+
"""
|
|
405
|
+
api_key = os.getenv("ALPACA_API_KEY")
|
|
406
|
+
secret_key = os.getenv("ALPACA_SECRET_KEY")
|
|
407
|
+
base_url = os.getenv("ALPACA_BASE_URL", "https://paper-api.alpaca.markets")
|
|
408
|
+
|
|
409
|
+
if not api_key or not secret_key:
|
|
410
|
+
return None
|
|
411
|
+
|
|
412
|
+
return {
|
|
413
|
+
"api_key": api_key,
|
|
414
|
+
"secret_key": secret_key,
|
|
415
|
+
"base_url": base_url,
|
|
416
|
+
"is_paper": "paper" in base_url.lower()
|
|
417
|
+
}
|
mcli/self/self_cmd.py
CHANGED
|
@@ -344,8 +344,38 @@ def collect_commands() -> List[Dict[str, Any]]:
|
|
|
344
344
|
module_name = ".".join(relative_path.with_suffix("").parts)
|
|
345
345
|
|
|
346
346
|
try:
|
|
347
|
-
#
|
|
348
|
-
|
|
347
|
+
# Suppress Streamlit warnings and logging during module import
|
|
348
|
+
import warnings
|
|
349
|
+
import logging
|
|
350
|
+
import sys
|
|
351
|
+
import os
|
|
352
|
+
from contextlib import redirect_stderr
|
|
353
|
+
from io import StringIO
|
|
354
|
+
|
|
355
|
+
# Suppress Python warnings
|
|
356
|
+
with warnings.catch_warnings():
|
|
357
|
+
warnings.filterwarnings("ignore", message=".*missing ScriptRunContext.*")
|
|
358
|
+
warnings.filterwarnings("ignore", message=".*No runtime found.*")
|
|
359
|
+
warnings.filterwarnings("ignore", message=".*Session state does not function.*")
|
|
360
|
+
warnings.filterwarnings("ignore", message=".*to view this Streamlit app.*")
|
|
361
|
+
|
|
362
|
+
# Suppress Streamlit logger warnings
|
|
363
|
+
streamlit_logger = logging.getLogger("streamlit")
|
|
364
|
+
original_level = streamlit_logger.level
|
|
365
|
+
streamlit_logger.setLevel(logging.CRITICAL)
|
|
366
|
+
|
|
367
|
+
# Also suppress specific Streamlit sub-loggers
|
|
368
|
+
logging.getLogger("streamlit.runtime.scriptrunner_utils.script_run_context").setLevel(logging.CRITICAL)
|
|
369
|
+
logging.getLogger("streamlit.runtime.caching.cache_data_api").setLevel(logging.CRITICAL)
|
|
370
|
+
|
|
371
|
+
# Redirect stderr to suppress Streamlit warnings
|
|
372
|
+
with redirect_stderr(StringIO()):
|
|
373
|
+
try:
|
|
374
|
+
# Try to import the module
|
|
375
|
+
module = importlib.import_module(module_name)
|
|
376
|
+
finally:
|
|
377
|
+
# Restore original logging level
|
|
378
|
+
streamlit_logger.setLevel(original_level)
|
|
349
379
|
|
|
350
380
|
# Extract command and group objects
|
|
351
381
|
for name, obj in inspect.getmembers(module):
|
|
@@ -387,23 +417,177 @@ def collect_commands() -> List[Dict[str, Any]]:
|
|
|
387
417
|
return commands
|
|
388
418
|
|
|
389
419
|
|
|
390
|
-
|
|
420
|
+
def open_editor_for_command(command_name: str, command_group: str, description: str) -> Optional[str]:
|
|
421
|
+
"""
|
|
422
|
+
Open the user's default editor to allow them to write command logic.
|
|
423
|
+
|
|
424
|
+
Args:
|
|
425
|
+
command_name: Name of the command
|
|
426
|
+
command_group: Group for the command
|
|
427
|
+
description: Description of the command
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
The Python code written by the user, or None if cancelled
|
|
431
|
+
"""
|
|
432
|
+
import tempfile
|
|
433
|
+
import subprocess
|
|
434
|
+
import os
|
|
435
|
+
import sys
|
|
436
|
+
from pathlib import Path
|
|
437
|
+
|
|
438
|
+
# Get the user's default editor
|
|
439
|
+
editor = os.environ.get('EDITOR')
|
|
440
|
+
if not editor:
|
|
441
|
+
# Try common editors in order of preference
|
|
442
|
+
for common_editor in ['vim', 'nano', 'code', 'subl', 'atom', 'emacs']:
|
|
443
|
+
if subprocess.run(['which', common_editor], capture_output=True).returncode == 0:
|
|
444
|
+
editor = common_editor
|
|
445
|
+
break
|
|
446
|
+
|
|
447
|
+
if not editor:
|
|
448
|
+
click.echo("❌ No editor found. Please set the EDITOR environment variable or install vim/nano.")
|
|
449
|
+
return None
|
|
450
|
+
|
|
451
|
+
# Create a temporary file with the template
|
|
452
|
+
template = get_command_template(command_name, command_group)
|
|
453
|
+
|
|
454
|
+
# Add helpful comments to the template
|
|
455
|
+
enhanced_template = f'''"""
|
|
456
|
+
{command_name} command for mcli.{command_group}.
|
|
457
|
+
|
|
458
|
+
Description: {description}
|
|
459
|
+
|
|
460
|
+
Instructions:
|
|
461
|
+
1. Write your Python command logic below
|
|
462
|
+
2. Use Click decorators for command definition
|
|
463
|
+
3. Save and close the editor to create the command
|
|
464
|
+
4. The command will be automatically converted to JSON format
|
|
465
|
+
|
|
466
|
+
Example Click command structure:
|
|
467
|
+
@click.command()
|
|
468
|
+
@click.argument('name', default='World')
|
|
469
|
+
def my_command(name):
|
|
470
|
+
\"\"\"My custom command.\"\"\"
|
|
471
|
+
click.echo(f"Hello, {{name}}!")
|
|
472
|
+
"""
|
|
473
|
+
import click
|
|
474
|
+
from typing import Optional, List
|
|
475
|
+
from pathlib import Path
|
|
476
|
+
from mcli.lib.logger.logger import get_logger
|
|
477
|
+
|
|
478
|
+
logger = get_logger()
|
|
479
|
+
|
|
480
|
+
# Write your command logic here:
|
|
481
|
+
# Replace this template with your actual command implementation
|
|
482
|
+
|
|
483
|
+
{template.split('"""')[2].split('"""')[0] if '"""' in template else ''}
|
|
484
|
+
|
|
485
|
+
# Your command implementation goes here:
|
|
486
|
+
# Example:
|
|
487
|
+
# @click.command()
|
|
488
|
+
# @click.argument('name', default='World')
|
|
489
|
+
# def {command_name}_command(name):
|
|
490
|
+
# \"\"\"{description}\"\"\"
|
|
491
|
+
# logger.info(f"Executing {command_name} command with name: {{name}}")
|
|
492
|
+
# click.echo(f"Hello, {{name}}! This is the {command_name} command.")
|
|
493
|
+
'''
|
|
494
|
+
|
|
495
|
+
# Create temporary file
|
|
496
|
+
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as temp_file:
|
|
497
|
+
temp_file.write(enhanced_template)
|
|
498
|
+
temp_file_path = temp_file.name
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
# Check if we're in an interactive environment
|
|
502
|
+
if not sys.stdin.isatty() or not sys.stdout.isatty():
|
|
503
|
+
click.echo("❌ Editor requires an interactive terminal. Use --template flag for non-interactive mode.")
|
|
504
|
+
return None
|
|
505
|
+
|
|
506
|
+
# Open editor
|
|
507
|
+
click.echo(f"📝 Opening {editor} to edit command logic...")
|
|
508
|
+
click.echo("💡 Write your Python command logic and save the file to continue.")
|
|
509
|
+
click.echo("💡 Press Ctrl+C to cancel command creation.")
|
|
510
|
+
|
|
511
|
+
# Run the editor
|
|
512
|
+
result = subprocess.run([editor, temp_file_path], check=False)
|
|
513
|
+
|
|
514
|
+
if result.returncode != 0:
|
|
515
|
+
click.echo("❌ Editor exited with error. Command creation cancelled.")
|
|
516
|
+
return None
|
|
517
|
+
|
|
518
|
+
# Read the edited content
|
|
519
|
+
with open(temp_file_path, 'r') as f:
|
|
520
|
+
edited_code = f.read()
|
|
521
|
+
|
|
522
|
+
# Check if the file was actually edited (not just the template)
|
|
523
|
+
if edited_code.strip() == enhanced_template.strip():
|
|
524
|
+
click.echo("⚠️ No changes detected. Command creation cancelled.")
|
|
525
|
+
return None
|
|
526
|
+
|
|
527
|
+
# Extract the actual command code (remove the instructions)
|
|
528
|
+
lines = edited_code.split('\n')
|
|
529
|
+
code_lines = []
|
|
530
|
+
in_code_section = False
|
|
531
|
+
|
|
532
|
+
for line in lines:
|
|
533
|
+
if line.strip().startswith('# Your command implementation goes here:'):
|
|
534
|
+
in_code_section = True
|
|
535
|
+
continue
|
|
536
|
+
if in_code_section:
|
|
537
|
+
code_lines.append(line)
|
|
538
|
+
|
|
539
|
+
if not code_lines or not any(line.strip() for line in code_lines):
|
|
540
|
+
# Fallback: use the entire file content
|
|
541
|
+
code_lines = lines
|
|
542
|
+
|
|
543
|
+
final_code = '\n'.join(code_lines).strip()
|
|
544
|
+
|
|
545
|
+
if not final_code:
|
|
546
|
+
click.echo("❌ No command code found. Command creation cancelled.")
|
|
547
|
+
return None
|
|
548
|
+
|
|
549
|
+
click.echo("✅ Command code captured successfully!")
|
|
550
|
+
return final_code
|
|
551
|
+
|
|
552
|
+
except KeyboardInterrupt:
|
|
553
|
+
click.echo("\n❌ Command creation cancelled by user.")
|
|
554
|
+
return None
|
|
555
|
+
except Exception as e:
|
|
556
|
+
click.echo(f"❌ Error opening editor: {e}")
|
|
557
|
+
return None
|
|
558
|
+
finally:
|
|
559
|
+
# Clean up temporary file
|
|
560
|
+
try:
|
|
561
|
+
os.unlink(temp_file_path)
|
|
562
|
+
except OSError:
|
|
563
|
+
pass
|
|
564
|
+
|
|
565
|
+
|
|
566
|
+
@self_app.command("add-command", deprecated=True)
|
|
391
567
|
@click.argument("command_name", required=True)
|
|
392
568
|
@click.option("--group", "-g", help="Command group (defaults to 'workflow')", default="workflow")
|
|
393
569
|
@click.option(
|
|
394
570
|
"--description", "-d", help="Description for the command", default="Custom command"
|
|
395
571
|
)
|
|
396
|
-
|
|
572
|
+
@click.option(
|
|
573
|
+
"--template", "-t", is_flag=True, help="Use template mode (skip editor and use predefined template)"
|
|
574
|
+
)
|
|
575
|
+
def add_command(command_name, group, description, template):
|
|
397
576
|
"""
|
|
577
|
+
[DEPRECATED] Use 'mcli commands add' instead.
|
|
578
|
+
|
|
398
579
|
Generate a new portable custom command saved to ~/.mcli/commands/.
|
|
399
580
|
|
|
400
|
-
|
|
401
|
-
|
|
581
|
+
This command has been moved to 'mcli commands add' for better organization.
|
|
582
|
+
Please use the new command going forward.
|
|
402
583
|
|
|
403
584
|
Example:
|
|
404
|
-
mcli
|
|
405
|
-
mcli
|
|
585
|
+
mcli commands add my_command
|
|
586
|
+
mcli commands add analytics --group data
|
|
587
|
+
mcli commands add quick_cmd --template # Use template without editor
|
|
406
588
|
"""
|
|
589
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands add'[/yellow]")
|
|
590
|
+
click.echo("[yellow] Please use: mcli commands add {} [options][/yellow]\n".format(command_name))
|
|
407
591
|
command_name = command_name.lower().replace("-", "_")
|
|
408
592
|
|
|
409
593
|
# Validate command name
|
|
@@ -448,7 +632,17 @@ def add_command(command_name, group, description):
|
|
|
448
632
|
return 1
|
|
449
633
|
|
|
450
634
|
# Generate command code
|
|
451
|
-
|
|
635
|
+
if template:
|
|
636
|
+
# Use template mode - generate and save directly
|
|
637
|
+
code = get_command_template(command_name, command_group)
|
|
638
|
+
click.echo(f"📝 Using template for command: {command_name}")
|
|
639
|
+
else:
|
|
640
|
+
# Editor mode - open editor for user to write code
|
|
641
|
+
click.echo(f"🔧 Opening editor for command: {command_name}")
|
|
642
|
+
code = open_editor_for_command(command_name, command_group, description)
|
|
643
|
+
if code is None:
|
|
644
|
+
click.echo("❌ Command creation cancelled.")
|
|
645
|
+
return 1
|
|
452
646
|
|
|
453
647
|
# Save the command
|
|
454
648
|
saved_path = manager.save_command(
|
|
@@ -469,11 +663,16 @@ def add_command(command_name, group, description):
|
|
|
469
663
|
return 0
|
|
470
664
|
|
|
471
665
|
|
|
472
|
-
@self_app.command("list-commands")
|
|
666
|
+
@self_app.command("list-commands", deprecated=True)
|
|
473
667
|
def list_commands():
|
|
474
668
|
"""
|
|
669
|
+
[DEPRECATED] Use 'mcli commands list-custom' instead.
|
|
670
|
+
|
|
475
671
|
List all custom commands stored in ~/.mcli/commands/.
|
|
476
672
|
"""
|
|
673
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands list-custom'[/yellow]")
|
|
674
|
+
click.echo("[yellow] Please use: mcli commands list-custom[/yellow]\n")
|
|
675
|
+
|
|
477
676
|
manager = get_command_manager()
|
|
478
677
|
commands = manager.load_all_commands()
|
|
479
678
|
|
|
@@ -507,13 +706,18 @@ def list_commands():
|
|
|
507
706
|
return 0
|
|
508
707
|
|
|
509
708
|
|
|
510
|
-
@self_app.command("remove-command")
|
|
709
|
+
@self_app.command("remove-command", deprecated=True)
|
|
511
710
|
@click.argument("command_name", required=True)
|
|
512
711
|
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
|
|
513
712
|
def remove_command(command_name, yes):
|
|
514
713
|
"""
|
|
714
|
+
[DEPRECATED] Use 'mcli commands remove' instead.
|
|
715
|
+
|
|
515
716
|
Remove a custom command from ~/.mcli/commands/.
|
|
516
717
|
"""
|
|
718
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands remove'[/yellow]")
|
|
719
|
+
click.echo(f"[yellow] Please use: mcli commands remove {command_name}[/yellow]\n")
|
|
720
|
+
|
|
517
721
|
manager = get_command_manager()
|
|
518
722
|
command_file = manager.commands_dir / f"{command_name}.json"
|
|
519
723
|
|
|
@@ -537,14 +741,19 @@ def remove_command(command_name, yes):
|
|
|
537
741
|
return 1
|
|
538
742
|
|
|
539
743
|
|
|
540
|
-
@self_app.command("export-commands")
|
|
744
|
+
@self_app.command("export-commands", deprecated=True)
|
|
541
745
|
@click.argument("export_file", type=click.Path(), required=False)
|
|
542
746
|
def export_commands(export_file):
|
|
543
747
|
"""
|
|
748
|
+
[DEPRECATED] Use 'mcli commands export' instead.
|
|
749
|
+
|
|
544
750
|
Export all custom commands to a JSON file.
|
|
545
751
|
|
|
546
752
|
If no file is specified, exports to commands-export.json in current directory.
|
|
547
753
|
"""
|
|
754
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands export'[/yellow]")
|
|
755
|
+
click.echo(f"[yellow] Please use: mcli commands export {export_file or '<file>'}[/yellow]\n")
|
|
756
|
+
|
|
548
757
|
manager = get_command_manager()
|
|
549
758
|
|
|
550
759
|
if not export_file:
|
|
@@ -563,13 +772,18 @@ def export_commands(export_file):
|
|
|
563
772
|
return 1
|
|
564
773
|
|
|
565
774
|
|
|
566
|
-
@self_app.command("import-commands")
|
|
775
|
+
@self_app.command("import-commands", deprecated=True)
|
|
567
776
|
@click.argument("import_file", type=click.Path(exists=True), required=True)
|
|
568
777
|
@click.option("--overwrite", is_flag=True, help="Overwrite existing commands")
|
|
569
778
|
def import_commands(import_file, overwrite):
|
|
570
779
|
"""
|
|
780
|
+
[DEPRECATED] Use 'mcli commands import' instead.
|
|
781
|
+
|
|
571
782
|
Import custom commands from a JSON file.
|
|
572
783
|
"""
|
|
784
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands import'[/yellow]")
|
|
785
|
+
click.echo(f"[yellow] Please use: mcli commands import {import_file}[/yellow]\n")
|
|
786
|
+
|
|
573
787
|
manager = get_command_manager()
|
|
574
788
|
import_path = Path(import_file)
|
|
575
789
|
|
|
@@ -593,11 +807,16 @@ def import_commands(import_file, overwrite):
|
|
|
593
807
|
return 0
|
|
594
808
|
|
|
595
809
|
|
|
596
|
-
@self_app.command("verify-commands")
|
|
810
|
+
@self_app.command("verify-commands", deprecated=True)
|
|
597
811
|
def verify_commands():
|
|
598
812
|
"""
|
|
813
|
+
[DEPRECATED] Use 'mcli commands verify' instead.
|
|
814
|
+
|
|
599
815
|
Verify that custom commands match the lockfile.
|
|
600
816
|
"""
|
|
817
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands verify'[/yellow]")
|
|
818
|
+
click.echo("[yellow] Please use: mcli commands verify[/yellow]\n")
|
|
819
|
+
|
|
601
820
|
manager = get_command_manager()
|
|
602
821
|
|
|
603
822
|
# First, ensure lockfile is up to date
|
|
@@ -631,11 +850,16 @@ def verify_commands():
|
|
|
631
850
|
return 1
|
|
632
851
|
|
|
633
852
|
|
|
634
|
-
@self_app.command("update-lockfile")
|
|
853
|
+
@self_app.command("update-lockfile", deprecated=True)
|
|
635
854
|
def update_lockfile():
|
|
636
855
|
"""
|
|
856
|
+
[DEPRECATED] Use 'mcli commands update-lockfile' instead.
|
|
857
|
+
|
|
637
858
|
Update the commands lockfile with current state.
|
|
638
859
|
"""
|
|
860
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands update-lockfile'[/yellow]")
|
|
861
|
+
click.echo("[yellow] Please use: mcli commands update-lockfile[/yellow]\n")
|
|
862
|
+
|
|
639
863
|
manager = get_command_manager()
|
|
640
864
|
|
|
641
865
|
if manager.update_lockfile():
|
|
@@ -1557,7 +1781,7 @@ def update(check: bool, pre: bool, yes: bool, skip_ci_check: bool):
|
|
|
1557
1781
|
console.print(f"[dim]{traceback.format_exc()}[/dim]")
|
|
1558
1782
|
|
|
1559
1783
|
|
|
1560
|
-
@self_app.command("import-script")
|
|
1784
|
+
@self_app.command("import-script", deprecated=True)
|
|
1561
1785
|
@click.argument("script_path", type=click.Path(exists=True))
|
|
1562
1786
|
@click.option("--name", "-n", help="Command name (defaults to script filename)")
|
|
1563
1787
|
@click.option("--group", "-g", default="workflow", help="Command group")
|
|
@@ -1565,15 +1789,20 @@ def update(check: bool, pre: bool, yes: bool, skip_ci_check: bool):
|
|
|
1565
1789
|
@click.option("--interactive", "-i", is_flag=True, help="Open in $EDITOR for review/editing")
|
|
1566
1790
|
def import_script(script_path, name, group, description, interactive):
|
|
1567
1791
|
"""
|
|
1792
|
+
[DEPRECATED] Use 'mcli commands import-script' instead.
|
|
1793
|
+
|
|
1568
1794
|
Import a Python script as a portable JSON command.
|
|
1569
1795
|
|
|
1570
1796
|
Converts a Python script into a JSON command that can be loaded
|
|
1571
1797
|
by mcli. The script should define Click commands.
|
|
1572
1798
|
|
|
1573
1799
|
Examples:
|
|
1574
|
-
mcli
|
|
1575
|
-
mcli
|
|
1800
|
+
mcli commands import-script my_script.py
|
|
1801
|
+
mcli commands import-script my_script.py --name custom-cmd --interactive
|
|
1576
1802
|
"""
|
|
1803
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands import-script'[/yellow]")
|
|
1804
|
+
click.echo(f"[yellow] Please use: mcli commands import-script {script_path}[/yellow]\n")
|
|
1805
|
+
|
|
1577
1806
|
import subprocess
|
|
1578
1807
|
import tempfile
|
|
1579
1808
|
|
|
@@ -1652,21 +1881,26 @@ def import_script(script_path, name, group, description, interactive):
|
|
|
1652
1881
|
return 0
|
|
1653
1882
|
|
|
1654
1883
|
|
|
1655
|
-
@self_app.command("export-script")
|
|
1884
|
+
@self_app.command("export-script", deprecated=True)
|
|
1656
1885
|
@click.argument("command_name")
|
|
1657
1886
|
@click.option("--output", "-o", type=click.Path(), help="Output file path")
|
|
1658
1887
|
@click.option("--standalone", "-s", is_flag=True, help="Make script standalone (add if __name__ == '__main__')")
|
|
1659
1888
|
def export_script(command_name, output, standalone):
|
|
1660
1889
|
"""
|
|
1890
|
+
[DEPRECATED] Use 'mcli commands export-script' instead.
|
|
1891
|
+
|
|
1661
1892
|
Export a JSON command to a Python script.
|
|
1662
1893
|
|
|
1663
1894
|
Converts a portable JSON command back to a standalone Python script
|
|
1664
1895
|
that can be edited and run independently.
|
|
1665
1896
|
|
|
1666
1897
|
Examples:
|
|
1667
|
-
mcli
|
|
1668
|
-
mcli
|
|
1898
|
+
mcli commands export-script my-command
|
|
1899
|
+
mcli commands export-script my-command --output my_script.py --standalone
|
|
1669
1900
|
"""
|
|
1901
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands export-script'[/yellow]")
|
|
1902
|
+
click.echo(f"[yellow] Please use: mcli commands export-script {command_name}[/yellow]\n")
|
|
1903
|
+
|
|
1670
1904
|
manager = get_command_manager()
|
|
1671
1905
|
|
|
1672
1906
|
# Load the command
|
|
@@ -1720,20 +1954,25 @@ def export_script(command_name, output, standalone):
|
|
|
1720
1954
|
return 0
|
|
1721
1955
|
|
|
1722
1956
|
|
|
1723
|
-
@self_app.command("edit-command")
|
|
1957
|
+
@self_app.command("edit-command", deprecated=True)
|
|
1724
1958
|
@click.argument("command_name")
|
|
1725
1959
|
@click.option("--editor", "-e", help="Editor to use (defaults to $EDITOR)")
|
|
1726
1960
|
def edit_command(command_name, editor):
|
|
1727
1961
|
"""
|
|
1962
|
+
[DEPRECATED] Use 'mcli commands edit' instead.
|
|
1963
|
+
|
|
1728
1964
|
Edit a command interactively using $EDITOR.
|
|
1729
1965
|
|
|
1730
1966
|
Opens the command's Python code in your preferred editor,
|
|
1731
1967
|
allows you to make changes, and saves the updated version.
|
|
1732
1968
|
|
|
1733
1969
|
Examples:
|
|
1734
|
-
mcli
|
|
1735
|
-
mcli
|
|
1970
|
+
mcli commands edit my-command
|
|
1971
|
+
mcli commands edit my-command --editor code
|
|
1736
1972
|
"""
|
|
1973
|
+
click.echo("[yellow]⚠️ DEPRECATED: This command has been moved to 'mcli commands edit'[/yellow]")
|
|
1974
|
+
click.echo(f"[yellow] Please use: mcli commands edit {command_name}[/yellow]\n")
|
|
1975
|
+
|
|
1737
1976
|
import subprocess
|
|
1738
1977
|
import tempfile
|
|
1739
1978
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcli-framework
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.4.0
|
|
4
4
|
Summary: 🚀 High-performance CLI framework with Rust extensions, AI chat, and stunning visuals
|
|
5
5
|
Author-email: Luis Fernandez de la Vara <luis@lefv.io>
|
|
6
6
|
Maintainer-email: Luis Fernandez de la Vara <luis@lefv.io>
|
|
@@ -93,7 +93,7 @@ Requires-Dist: polars>=0.19.0
|
|
|
93
93
|
Requires-Dist: pyarrow>=14.0.0
|
|
94
94
|
Requires-Dist: yfinance>=0.2.18
|
|
95
95
|
Requires-Dist: alpha-vantage>=2.3.1
|
|
96
|
-
Requires-Dist: alpaca-py
|
|
96
|
+
Requires-Dist: alpaca-py==0.42.2
|
|
97
97
|
Requires-Dist: cvxpy>=1.4.0
|
|
98
98
|
Requires-Dist: python-jose[cryptography]>=3.3.0
|
|
99
99
|
Requires-Dist: passlib[bcrypt]>=1.7.4
|
|
@@ -119,6 +119,7 @@ Requires-Dist: streamlit-autorefresh>=1.0.1
|
|
|
119
119
|
Requires-Dist: typer>=0.9.0
|
|
120
120
|
Requires-Dist: flask<3.0.0,>=2.3.0
|
|
121
121
|
Requires-Dist: alpaca-trade-api>=0.26
|
|
122
|
+
Requires-Dist: alpaca>=1.0.0
|
|
122
123
|
Provides-Extra: gpu
|
|
123
124
|
Requires-Dist: cupy-cuda12x>=12.3.0; extra == "gpu"
|
|
124
125
|
Requires-Dist: nvidia-ml-py>=12.535.0; extra == "gpu"
|