Skip to content

Commit 8c5ab39

Browse files
committed
feat(cli): implement enhanced cli with argument grouping and validation
- restructure cli module with proper argument parsing and validation - add argument group manager for better organization of cli options - implement help documentation generation for all argument groups - create template commands for expert creation and management - update cli entry point to use new modular structure - add comprehensive cli documentation in english and russian - refactor argument validation utilities and error handling - improve cli endpoint for http-based argument parsing - update tests to reflect new cli structure and functionality
1 parent f21088e commit 8c5ab39

28 files changed

Lines changed: 1211 additions & 693 deletions

pyproject.toml

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,7 @@ members = [
6868
"examples/expert_bybit_rsi",
6969
]
7070

71-
[tool.ruff]
72-
fix = true
73-
line-length = 88
74-
target-version = "py312"
75-
src = [
76-
"src",
77-
"packages/app/src",
78-
"examples",
79-
"tests",
80-
]
81-
exclude = [
82-
".git",
83-
".venv",
84-
"__pycache__",
85-
"template.py",
86-
]
71+
[tool.ruff.lint]
8772
select = [
8873
"E", # pycodestyle errors
8974
"W", # pycodestyle warnings
@@ -102,7 +87,7 @@ ignore = [
10287
"RUF012" # Mutable class attributes should be annotated with `typing.ClassVar`
10388
]
10489

105-
[tool.ruff.isort]
90+
[tool.ruff.lint.isort]
10691
known-first-party = ["metaexpert"]
10792

10893
[tool.ruff.format]
@@ -111,7 +96,7 @@ indent-style = "space"
11196
line-ending = "lf"
11297

11398
[tool.mypy]
114-
python_version = 3.12
99+
python_version = "3.12"
115100
warn_return_any = true
116101
warn_unused_configs = true
117102
disallow_untyped_defs = true
@@ -127,3 +112,13 @@ strict_equality = true
127112
implicit_reexport = false
128113
show_error_codes = true
129114
show_column_numbers = true
115+
116+
# Игнорировать определенные файлы и директории
117+
[[tool.mypy.overrides]]
118+
module = [
119+
"matplotlib.*",
120+
"numpy.*",
121+
"pandas.*",
122+
"websocket_client.*",
123+
]
124+
ignore_missing_imports = true
-4.7 KB
Binary file not shown.
-6.54 KB
Binary file not shown.
-5.59 KB
Binary file not shown.

src/metaexpert/__init__.py

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@
1717
- MarginMode: Margin modes for futures trading
1818
- PositionMode: Position modes for futures trading
1919
"""
20+
2021
from datetime import datetime
2122
from pathlib import Path
2223
from types import ModuleType
2324

24-
from metaexpert._argument import Namespace, parse_arguments
2525
from metaexpert._process import Process
2626
from metaexpert._service import Service
2727
from metaexpert._trade_mode import TradeMode
28+
from metaexpert.cli.argument_parser import Namespace, parse_arguments
2829
from metaexpert.config import APP_NAME, MODE_BACKTEST
2930
from metaexpert.exchanges import Exchange
30-
from metaexpert.logger import Logger, configure_logging, get_logger, setup_logger
31+
from metaexpert.logger import Logger, configure_logging, setup_logger
3132

3233
# Set up the main logger for the MetaExpert system
3334
logger: Logger = setup_logger(APP_NAME)
@@ -41,28 +42,23 @@ class MetaExpert(Service):
4142

4243
def __init__(
4344
self,
44-
4545
# Required Parameters
4646
exchange: str | None = None,
4747
*,
48-
4948
# API Credentials (required for live mode)
5049
api_key: str | None = None,
5150
api_secret: str | None = None,
5251
api_passphrase: str | None = None,
53-
5452
# Connection Settings
5553
subaccount: str | None = None,
5654
base_url: str | None = None,
5755
testnet: bool = True,
5856
proxy: dict[str, str] | None = None,
59-
6057
# Market & Trading Mode
6158
market_type: str | None = "futures",
6259
contract_type: str | None = "inverse",
6360
margin_mode: str | None = "isolated",
6461
position_mode: str | None = "hedge",
65-
6662
# Logging Configuration
6763
log_level: str = "INFO",
6864
log_file: str = "expert.log",
@@ -71,12 +67,11 @@ def __init__(
7167
log_to_console: bool = True,
7268
structured_logging: bool = False,
7369
async_logging: bool = False,
74-
7570
# Advanced System Settings
7671
rate_limit: int = 1200,
7772
enable_metrics: bool = True,
7873
persist_state: bool = True,
79-
state_file: str = "state.json"
74+
state_file: str = "state.json",
8075
) -> None:
8176
"""Initialize the expert trading system.
8277
@@ -117,7 +112,7 @@ def __init__(
117112
api_secret=api_secret or self.args.api_secret,
118113
base_url=base_url or self.args.base_url,
119114
market_type=market_type or self.args.market_type,
120-
contract_type=contract_type or self.args.contract_type
115+
contract_type=contract_type or self.args.contract_type,
121116
)
122117

123118
# Setup enhanced logger with new features
@@ -128,12 +123,17 @@ def __init__(
128123
error_log_file=error_log_file,
129124
log_to_console=log_to_console,
130125
structured_logging=structured_logging,
131-
async_logging=async_logging
126+
async_logging=async_logging,
132127
)
133128

134129
# Log initialization
135130
logger.info("Starting expert on %s", self.args.exchange)
136-
logger.info("Market type: %s, Contract type: %s, Mode: %s", self.args.market_type, self.args.contract_type, self.args.trade_mode)
131+
logger.info(
132+
"Market type: %s, Contract type: %s, Mode: %s",
133+
self.args.market_type,
134+
self.args.contract_type,
135+
self.args.trade_mode,
136+
)
137137
logger.info("Pair: %s, Timeframe: %s", self.args.pair, self.args.timeframe)
138138

139139
def _setup_enhanced_logging(
@@ -144,7 +144,7 @@ def _setup_enhanced_logging(
144144
error_log_file: str,
145145
log_to_console: bool,
146146
structured_logging: bool,
147-
async_logging: bool
147+
async_logging: bool,
148148
) -> None:
149149
"""Set up enhanced logging with the new features.
150150
@@ -165,45 +165,47 @@ def _setup_enhanced_logging(
165165
"console": {
166166
"level": log_level,
167167
"format": "[%(asctime)s] %(levelname)s: %(name)s: %(message)s",
168-
"structured": structured_logging
168+
"structured": structured_logging,
169169
},
170170
"file": {
171171
"level": log_level,
172172
"format": "[%(asctime)s] %(levelname)s: %(name)s: %(message)s",
173173
"structured": structured_logging,
174174
"filename": log_file,
175175
"max_size": 10485760, # 10MB
176-
"backup_count": 5
176+
"backup_count": 5,
177177
},
178178
"trade_file": {
179179
"level": "INFO",
180180
"format": "[%(asctime)s] %(levelname)s: %(name)s: %(message)s",
181181
"structured": structured_logging,
182182
"filename": trade_log_file,
183183
"max_size": 10485760, # 10MB
184-
"backup_count": 5
184+
"backup_count": 5,
185185
},
186186
"error_file": {
187187
"level": "ERROR",
188188
"format": "[%(asctime)s] %(levelname)s: %(name)s: %(message)s",
189189
"structured": structured_logging,
190190
"filename": error_log_file,
191191
"max_size": 10485760, # 10MB
192-
"backup_count": 5
193-
}
192+
"backup_count": 5,
193+
},
194194
},
195195
"structured_logging": structured_logging,
196-
"async_logging": async_logging
196+
"async_logging": async_logging,
197197
}
198-
198+
199199
# Apply configuration
200200
result = configure_logging(config)
201201
if result["status"] == "error":
202-
logger.warning("Failed to configure enhanced logging: %s", result["message"])
203-
202+
logger.warning(
203+
"Failed to configure enhanced logging: %s", result["message"]
204+
)
205+
204206
# Note: We don't reassign the global logger here to avoid the syntax error
205207
# The enhanced features are configured through the centralized configuration system
206-
208+
207209
except Exception as e:
208210
logger.error("Failed to set up enhanced logging: %s", str(e))
209211

@@ -214,14 +216,18 @@ def __repr__(self) -> str:
214216
return f"<{type(self).__name__} {self.strategy_name!r}>"
215217

216218
def run(
217-
self,
218-
trade_mode: str = "paper",
219-
backtest_start: str | datetime = datetime.now().replace(year=datetime.now().year - 1).strftime("%Y-%m-%d"),
220-
backtest_end: str | datetime = datetime.now().strftime("%Y-%m-%d"),
221-
initial_capital: float = 10000,
219+
self,
220+
trade_mode: str = "paper",
221+
backtest_start: str | datetime = datetime.now()
222+
.replace(year=datetime.now().year - 1)
223+
.strftime("%Y-%m-%d"),
224+
backtest_end: str | datetime = datetime.now().strftime("%Y-%m-%d"),
225+
initial_capital: float = 10000,
222226
) -> None:
223227
"""Run the expert trading system."""
224-
self.trade_mode: TradeMode = TradeMode.get_mode_from(trade_mode or self.args.trade_mode)
228+
self.trade_mode: TradeMode = TradeMode.get_mode_from(
229+
trade_mode or self.args.trade_mode
230+
)
225231
self.backtest_start: str | datetime = backtest_start
226232
self.backtest_end: str | datetime = backtest_end
227233
self.initial_capital: float = initial_capital
@@ -271,4 +277,4 @@ def run(
271277
logger.info("Expert shutdown complete")
272278

273279
# from metaexpert.exchanges.binance import balance
274-
#balance = import_module("metaexpert.exchanges.binance").get_balance
280+
# balance = import_module("metaexpert.exchanges.binance").get_balance

src/metaexpert/__main__.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,33 @@
33
import argparse
44
import sys
55

6-
from metaexpert._argument import parse_arguments
6+
from metaexpert.cli import add_template_commands
77
from metaexpert.template_creator import create_expert_from_template
8-
from metaexpert.cli.template_commands import add_template_commands
98

109

1110
def main() -> None:
1211
"""Main entry point for the MetaExpert package."""
1312
# Create the main argument parser
1413
parser = argparse.ArgumentParser(description="MetaExpert Trading Library")
15-
14+
1615
# Add existing arguments
17-
# Note: We're simplifying this for now. In a real implementation,
16+
# Note: We're simplifying this for now. In a real implementation,
1817
# we would integrate the existing argument parsing with the new commands.
19-
18+
2019
# Add template-related commands
2120
add_template_commands(parser)
22-
21+
2322
# Parse arguments
2423
args = parser.parse_args()
25-
24+
2625
# Check if a template command was specified
27-
if hasattr(args, 'func'):
26+
if hasattr(args, "func"):
2827
# Execute the appropriate command
2928
args.func(args)
3029
sys.exit(0)
31-
30+
3231
# Check if the user wants to create a new expert file from template (legacy)
33-
if hasattr(args, 'new') and args.new:
32+
if hasattr(args, "new") and args.new:
3433
create_expert_from_template(args.new)
3534
sys.exit(0)
3635

0 commit comments

Comments
 (0)