diff --git a/pyproject.toml b/pyproject.toml index 16adc8c..486c512 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "manifoldbt" -version = "0.7.0" +version = "0.8.0" description = "Rust-powered backtesting engine for quantitative research" requires-python = ">=3.9" license = { file = "LICENSE" } diff --git a/python/manifoldbt/cli.py b/python/manifoldbt/cli.py index e74baba..cf387e9 100644 --- a/python/manifoldbt/cli.py +++ b/python/manifoldbt/cli.py @@ -18,7 +18,7 @@ def main() -> None: # ── ingest ──────────────────────────────────────────────────────────── ing = sub.add_parser("ingest", help="Ingest bars from a data provider") - ing.add_argument("--provider", required=True, help="binance | hyperliquid | databento") + ing.add_argument("--provider", required=True, help="binance | bybit | hyperliquid | databento") ing.add_argument("--symbol", required=True, help="e.g. BTCUSDT, ESH5") ing.add_argument("--symbol-id", required=True, type=int, help="Unique integer ID for this symbol") ing.add_argument("--start", required=True, help="RFC3339 start (e.g. 2025-01-01T00:00:00Z)") @@ -32,6 +32,23 @@ def main() -> None: help="crypto_spot | crypto_perp | equity | future | option | forex | index") ing.add_argument("--license-key", default=None, help="Pro license key (or set via bt.activate())") + # ── activate ────────────────────────────────────────────────────────── + act = sub.add_parser("activate", help="Activate a Pro license key") + act.add_argument("key", nargs="?", default=None, + help="Pro license key. If omitted, reads from stdin.") + + # ── import-csv ──────────────────────────────────────────────────────── + csv = sub.add_parser("import-csv", help="Import bars from a CSV file (standard/MT4/MT5)") + csv.add_argument("csv_file", help="Path to the CSV file") + csv.add_argument("--symbol", required=True, help="e.g. EURUSD, BTCUSDT") + csv.add_argument("--symbol-id", required=True, type=int, help="Unique integer ID for this symbol") + csv.add_argument("--interval", default="1m", help="Bar interval of the rows (default: 1m)") + csv.add_argument("--data-root", default="data", help="Store directory (default: data)") + csv.add_argument("--metadata-db", default="metadata/metadata.sqlite", help="Metadata SQLite path") + csv.add_argument("--exchange", default="CSV", help="Exchange label for metadata (default: CSV)") + csv.add_argument("--asset-class", default="crypto_spot", + help="crypto_spot | crypto_perp | equity | future | option | forex | index") + args = parser.parse_args() if args.command is None: @@ -40,6 +57,34 @@ def main() -> None: if args.command == "ingest": _cmd_ingest(args) + elif args.command == "activate": + _cmd_activate(args) + elif args.command == "import-csv": + _cmd_import_csv(args) + + +def _cmd_activate(args: argparse.Namespace) -> None: + import manifoldbt as bt + + key = args.key + if not key: + key = sys.stdin.read().strip() + if not key: + print("Error: missing license key", file=sys.stderr) + sys.exit(1) + + try: + bt.activate(key) + except Exception as e: + print(f"Activation failed: {e}", file=sys.stderr) + sys.exit(1) + + tier, email = bt.license_info() + if tier == "Pro": + print(f"Activated: {tier}" + (f" ({email})" if email else "")) + else: + print("Activation did not produce a Pro license.", file=sys.stderr) + sys.exit(1) def _cmd_ingest(args: argparse.Namespace) -> None: @@ -75,3 +120,23 @@ def _cmd_ingest(args: argparse.Namespace) -> None: except Exception as e: print(f"Error: {e}", file=sys.stderr) sys.exit(1) + + +def _cmd_import_csv(args: argparse.Namespace) -> None: + import manifoldbt as bt + + try: + store = bt.import_csv( + args.csv_file, + symbol=args.symbol, + symbol_id=args.symbol_id, + interval=args.interval, + data_root=args.data_root, + metadata_db=args.metadata_db, + exchange=args.exchange, + asset_class=args.asset_class, + ) + print(f"Imported {args.csv_file} to {store.data_root()} (symbols: {store.list_symbols()})") + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1)