chore: update file permissions to executable across repository
- Change file mode from 644 to 755 for all project files - Add chain_id parameter to get_balance RPC endpoint with default "ait-devnet" - Rename Miner.extra_meta_data to extra_metadata for consistency
This commit is contained in:
796
cli/aitbc_cli/commands/market_maker.py
Executable file
796
cli/aitbc_cli/commands/market_maker.py
Executable file
@@ -0,0 +1,796 @@
|
||||
"""Market making commands for AITBC CLI"""
|
||||
|
||||
import click
|
||||
import json
|
||||
import uuid
|
||||
import httpx
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any, List
|
||||
from datetime import datetime, timedelta
|
||||
from ..utils import output, error, success, warning
|
||||
|
||||
|
||||
@click.group()
|
||||
def market_maker():
|
||||
"""Market making bot management commands"""
|
||||
pass
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--exchange", required=True, help="Exchange name")
|
||||
@click.option("--pair", required=True, help="Trading pair symbol (e.g., AITBC/BTC)")
|
||||
@click.option("--spread", type=float, default=0.005, help="Bid-ask spread (as percentage)")
|
||||
@click.option("--depth", type=float, default=1000000, help="Order book depth amount")
|
||||
@click.option("--max-order-size", type=float, default=1000, help="Maximum order size")
|
||||
@click.option("--min-order-size", type=float, default=10, help="Minimum order size")
|
||||
@click.option("--target-inventory", type=float, default=50000, help="Target inventory balance")
|
||||
@click.option("--rebalance-threshold", type=float, default=0.1, help="Inventory rebalance threshold")
|
||||
@click.option("--description", help="Bot description")
|
||||
@click.pass_context
|
||||
def create(ctx, exchange: str, pair: str, spread: float, depth: float, max_order_size: float, min_order_size: float, target_inventory: float, rebalance_threshold: float, description: Optional[str]):
|
||||
"""Create a new market making bot"""
|
||||
|
||||
# Generate unique bot ID
|
||||
bot_id = f"mm_{exchange.lower()}_{pair.replace('/', '_')}_{str(uuid.uuid4())[:8]}"
|
||||
|
||||
# Create bot configuration
|
||||
bot_config = {
|
||||
"bot_id": bot_id,
|
||||
"exchange": exchange,
|
||||
"pair": pair,
|
||||
"status": "stopped",
|
||||
"strategy": "basic_market_making",
|
||||
"config": {
|
||||
"spread": spread,
|
||||
"depth": depth,
|
||||
"max_order_size": max_order_size,
|
||||
"min_order_size": min_order_size,
|
||||
"target_inventory": target_inventory,
|
||||
"rebalance_threshold": rebalance_threshold
|
||||
},
|
||||
"performance": {
|
||||
"total_trades": 0,
|
||||
"total_volume": 0.0,
|
||||
"total_profit": 0.0,
|
||||
"inventory_value": 0.0,
|
||||
"orders_placed": 0,
|
||||
"orders_filled": 0
|
||||
},
|
||||
"created_at": datetime.utcnow().isoformat(),
|
||||
"last_updated": None,
|
||||
"description": description or f"Market making bot for {pair} on {exchange}",
|
||||
"current_orders": [],
|
||||
"inventory": {
|
||||
"base_asset": 0.0,
|
||||
"quote_asset": target_inventory
|
||||
}
|
||||
}
|
||||
|
||||
# Store bot configuration
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
bots_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Load existing bots
|
||||
bots = {}
|
||||
if bots_file.exists():
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
# Add new bot
|
||||
bots[bot_id] = bot_config
|
||||
|
||||
# Save bots
|
||||
with open(bots_file, 'w') as f:
|
||||
json.dump(bots, f, indent=2)
|
||||
|
||||
success(f"Market making bot created: {bot_id}")
|
||||
output({
|
||||
"bot_id": bot_id,
|
||||
"exchange": exchange,
|
||||
"pair": pair,
|
||||
"status": "created",
|
||||
"spread": spread,
|
||||
"depth": depth,
|
||||
"created_at": bot_config["created_at"]
|
||||
})
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Bot ID to configure")
|
||||
@click.option("--spread", type=float, help="New bid-ask spread")
|
||||
@click.option("--depth", type=float, help="New order book depth")
|
||||
@click.option("--max-order-size", type=float, help="New maximum order size")
|
||||
@click.option("--target-inventory", type=float, help="New target inventory")
|
||||
@click.option("--rebalance-threshold", type=float, help="New rebalance threshold")
|
||||
@click.pass_context
|
||||
def config(ctx, bot_id: str, spread: Optional[float], depth: Optional[float], max_order_size: Optional[float], target_inventory: Optional[float], rebalance_threshold: Optional[float]):
|
||||
"""Configure market making bot parameters"""
|
||||
|
||||
# Load bots
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
if not bots_file.exists():
|
||||
error("No market making bots found.")
|
||||
return
|
||||
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
if bot_id not in bots:
|
||||
error(f"Bot '{bot_id}' not found.")
|
||||
return
|
||||
|
||||
bot = bots[bot_id]
|
||||
|
||||
# Update configuration
|
||||
config_updates = {}
|
||||
if spread is not None:
|
||||
bot["config"]["spread"] = spread
|
||||
config_updates["spread"] = spread
|
||||
if depth is not None:
|
||||
bot["config"]["depth"] = depth
|
||||
config_updates["depth"] = depth
|
||||
if max_order_size is not None:
|
||||
bot["config"]["max_order_size"] = max_order_size
|
||||
config_updates["max_order_size"] = max_order_size
|
||||
if target_inventory is not None:
|
||||
bot["config"]["target_inventory"] = target_inventory
|
||||
config_updates["target_inventory"] = target_inventory
|
||||
if rebalance_threshold is not None:
|
||||
bot["config"]["rebalance_threshold"] = rebalance_threshold
|
||||
config_updates["rebalance_threshold"] = rebalance_threshold
|
||||
|
||||
if not config_updates:
|
||||
error("No configuration updates provided.")
|
||||
return
|
||||
|
||||
# Update timestamp
|
||||
bot["last_updated"] = datetime.utcnow().isoformat()
|
||||
|
||||
# Save bots
|
||||
with open(bots_file, 'w') as f:
|
||||
json.dump(bots, f, indent=2)
|
||||
|
||||
success(f"Bot '{bot_id}' configuration updated")
|
||||
output({
|
||||
"bot_id": bot_id,
|
||||
"config_updates": config_updates,
|
||||
"updated_at": bot["last_updated"]
|
||||
})
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Bot ID to start")
|
||||
@click.option("--dry-run", is_flag=True, help="Run in simulation mode without real orders")
|
||||
@click.pass_context
|
||||
def start(ctx, bot_id: str, dry_run: bool):
|
||||
"""Start a market making bot"""
|
||||
|
||||
# Load bots
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
if not bots_file.exists():
|
||||
error("No market making bots found.")
|
||||
return
|
||||
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
if bot_id not in bots:
|
||||
error(f"Bot '{bot_id}' not found.")
|
||||
return
|
||||
|
||||
bot = bots[bot_id]
|
||||
|
||||
# Check if bot is already running
|
||||
if bot["status"] == "running":
|
||||
warning(f"Bot '{bot_id}' is already running.")
|
||||
return
|
||||
|
||||
# Update bot status
|
||||
bot["status"] = "running" if not dry_run else "simulation"
|
||||
bot["started_at"] = datetime.utcnow().isoformat()
|
||||
bot["last_updated"] = datetime.utcnow().isoformat()
|
||||
bot["dry_run"] = dry_run
|
||||
|
||||
# Initialize performance tracking for this run
|
||||
bot["current_run"] = {
|
||||
"started_at": bot["started_at"],
|
||||
"orders_placed": 0,
|
||||
"orders_filled": 0,
|
||||
"total_volume": 0.0,
|
||||
"total_profit": 0.0
|
||||
}
|
||||
|
||||
# Save bots
|
||||
with open(bots_file, 'w') as f:
|
||||
json.dump(bots, f, indent=2)
|
||||
|
||||
mode = "simulation" if dry_run else "live"
|
||||
success(f"Bot '{bot_id}' started in {mode} mode")
|
||||
output({
|
||||
"bot_id": bot_id,
|
||||
"status": bot["status"],
|
||||
"mode": mode,
|
||||
"started_at": bot["started_at"],
|
||||
"exchange": bot["exchange"],
|
||||
"pair": bot["pair"]
|
||||
})
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Bot ID to stop")
|
||||
@click.pass_context
|
||||
def stop(ctx, bot_id: str):
|
||||
"""Stop a market making bot"""
|
||||
|
||||
# Load bots
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
if not bots_file.exists():
|
||||
error("No market making bots found.")
|
||||
return
|
||||
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
if bot_id not in bots:
|
||||
error(f"Bot '{bot_id}' not found.")
|
||||
return
|
||||
|
||||
bot = bots[bot_id]
|
||||
|
||||
# Check if bot is running
|
||||
if bot["status"] not in ["running", "simulation"]:
|
||||
warning(f"Bot '{bot_id}' is not currently running.")
|
||||
return
|
||||
|
||||
# Update bot status
|
||||
bot["status"] = "stopped"
|
||||
bot["stopped_at"] = datetime.utcnow().isoformat()
|
||||
bot["last_updated"] = datetime.utcnow().isoformat()
|
||||
|
||||
# Cancel all current orders (simulation)
|
||||
bot["current_orders"] = []
|
||||
|
||||
# Save bots
|
||||
with open(bots_file, 'w') as f:
|
||||
json.dump(bots, f, indent=2)
|
||||
|
||||
success(f"Bot '{bot_id}' stopped")
|
||||
output({
|
||||
"bot_id": bot_id,
|
||||
"status": "stopped",
|
||||
"stopped_at": bot["stopped_at"],
|
||||
"final_performance": bot.get("current_run", {})
|
||||
})
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", help="Specific bot ID to check")
|
||||
@click.option("--exchange", help="Filter by exchange")
|
||||
@click.option("--pair", help="Filter by trading pair")
|
||||
@click.pass_context
|
||||
def performance(ctx, bot_id: Optional[str], exchange: Optional[str], pair: Optional[str]):
|
||||
"""Get performance metrics for market making bots"""
|
||||
|
||||
# Load bots
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
if not bots_file.exists():
|
||||
error("No market making bots found.")
|
||||
return
|
||||
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
# Filter bots
|
||||
performance_data = {}
|
||||
|
||||
for current_bot_id, bot in bots.items():
|
||||
if bot_id and current_bot_id != bot_id:
|
||||
continue
|
||||
if exchange and bot["exchange"] != exchange:
|
||||
continue
|
||||
if pair and bot["pair"] != pair:
|
||||
continue
|
||||
|
||||
# Calculate performance metrics
|
||||
perf = bot.get("performance", {})
|
||||
current_run = bot.get("current_run", {})
|
||||
|
||||
bot_performance = {
|
||||
"bot_id": current_bot_id,
|
||||
"exchange": bot["exchange"],
|
||||
"pair": bot["pair"],
|
||||
"status": bot["status"],
|
||||
"created_at": bot["created_at"],
|
||||
"total_trades": perf.get("total_trades", 0),
|
||||
"total_volume": perf.get("total_volume", 0.0),
|
||||
"total_profit": perf.get("total_profit", 0.0),
|
||||
"orders_placed": perf.get("orders_placed", 0),
|
||||
"orders_filled": perf.get("orders_filled", 0),
|
||||
"fill_rate": (perf.get("orders_filled", 0) / max(perf.get("orders_placed", 1), 1)) * 100,
|
||||
"current_inventory": bot.get("inventory", {}),
|
||||
"current_orders": len(bot.get("current_orders", [])),
|
||||
"strategy": bot.get("strategy", "unknown"),
|
||||
"config": bot.get("config", {})
|
||||
}
|
||||
|
||||
# Add current run data if available
|
||||
if current_run:
|
||||
bot_performance["current_run"] = current_run
|
||||
if "started_at" in current_run:
|
||||
start_time = datetime.fromisoformat(current_run["started_at"].replace('Z', '+00:00'))
|
||||
runtime = datetime.utcnow() - start_time
|
||||
bot_performance["run_time_hours"] = runtime.total_seconds() / 3600
|
||||
|
||||
performance_data[current_bot_id] = bot_performance
|
||||
|
||||
if not performance_data:
|
||||
error("No market making bots found matching the criteria.")
|
||||
return
|
||||
|
||||
output({
|
||||
"performance_data": performance_data,
|
||||
"total_bots": len(performance_data),
|
||||
"generated_at": datetime.utcnow().isoformat()
|
||||
})
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.pass_context
|
||||
def list(ctx):
|
||||
"""List all market making bots"""
|
||||
|
||||
# Load bots
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
if not bots_file.exists():
|
||||
warning("No market making bots found.")
|
||||
return
|
||||
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
# Format bot list
|
||||
bot_list = []
|
||||
for bot_id, bot in bots.items():
|
||||
bot_info = {
|
||||
"bot_id": bot_id,
|
||||
"exchange": bot["exchange"],
|
||||
"pair": bot["pair"],
|
||||
"status": bot["status"],
|
||||
"strategy": bot.get("strategy", "unknown"),
|
||||
"created_at": bot["created_at"],
|
||||
"last_updated": bot.get("last_updated"),
|
||||
"total_trades": bot.get("performance", {}).get("total_trades", 0),
|
||||
"current_orders": len(bot.get("current_orders", []))
|
||||
}
|
||||
bot_list.append(bot_info)
|
||||
|
||||
output({
|
||||
"market_makers": bot_list,
|
||||
"total_bots": len(bot_list),
|
||||
"running_bots": len([b for b in bot_list if b["status"] in ["running", "simulation"]]),
|
||||
"stopped_bots": len([b for b in bot_list if b["status"] == "stopped"])
|
||||
})
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.argument("bot_id")
|
||||
@click.pass_context
|
||||
def status(ctx, bot_id: str):
|
||||
"""Get detailed status of a specific market making bot"""
|
||||
|
||||
# Load bots
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
if not bots_file.exists():
|
||||
error("No market making bots found.")
|
||||
return
|
||||
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
if bot_id not in bots:
|
||||
error(f"Bot '{bot_id}' not found.")
|
||||
return
|
||||
|
||||
bot = bots[bot_id]
|
||||
|
||||
# Calculate uptime if running
|
||||
uptime_hours = None
|
||||
if bot["status"] in ["running", "simulation"] and "started_at" in bot:
|
||||
start_time = datetime.fromisoformat(bot["started_at"].replace('Z', '+00:00'))
|
||||
uptime = datetime.utcnow() - start_time
|
||||
uptime_hours = uptime.total_seconds() / 3600
|
||||
|
||||
output({
|
||||
"bot_id": bot_id,
|
||||
"exchange": bot["exchange"],
|
||||
"pair": bot["pair"],
|
||||
"status": bot["status"],
|
||||
"strategy": bot.get("strategy", "unknown"),
|
||||
"config": bot.get("config", {}),
|
||||
"performance": bot.get("performance", {}),
|
||||
"inventory": bot.get("inventory", {}),
|
||||
"current_orders": bot.get("current_orders", []),
|
||||
"created_at": bot["created_at"],
|
||||
"last_updated": bot.get("last_updated"),
|
||||
"started_at": bot.get("started_at"),
|
||||
"stopped_at": bot.get("stopped_at"),
|
||||
"uptime_hours": uptime_hours,
|
||||
"dry_run": bot.get("dry_run", False),
|
||||
"description": bot.get("description")
|
||||
})
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.argument("bot_id")
|
||||
@click.pass_context
|
||||
def remove(ctx, bot_id: str):
|
||||
"""Remove a market making bot"""
|
||||
|
||||
# Load bots
|
||||
bots_file = Path.home() / ".aitbc" / "market_makers.json"
|
||||
if not bots_file.exists():
|
||||
error("No market making bots found.")
|
||||
return
|
||||
|
||||
with open(bots_file, 'r') as f:
|
||||
bots = json.load(f)
|
||||
|
||||
if bot_id not in bots:
|
||||
error(f"Bot '{bot_id}' not found.")
|
||||
return
|
||||
|
||||
bot = bots[bot_id]
|
||||
|
||||
# Check if bot is running
|
||||
if bot["status"] in ["running", "simulation"]:
|
||||
error(f"Cannot remove bot '{bot_id}' while it is running. Stop it first.")
|
||||
return
|
||||
|
||||
# Remove bot
|
||||
del bots[bot_id]
|
||||
|
||||
# Save bots
|
||||
with open(bots_file, 'w') as f:
|
||||
json.dump(bots, f, indent=2)
|
||||
|
||||
success(f"Market making bot '{bot_id}' removed")
|
||||
output({
|
||||
"bot_id": bot_id,
|
||||
"status": "removed",
|
||||
"exchange": bot["exchange"],
|
||||
"pair": bot["pair"]
|
||||
})
|
||||
|
||||
|
||||
@click.group()
|
||||
def market_maker():
|
||||
"""Market making operations"""
|
||||
pass
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--exchange", required=True, help="Exchange name (e.g., Binance, Coinbase)")
|
||||
@click.option("--pair", required=True, help="Trading pair (e.g., AITBC/BTC)")
|
||||
@click.option("--spread", type=float, default=0.001, help="Bid-ask spread (as percentage)")
|
||||
@click.option("--depth", type=int, default=5, help="Order book depth levels")
|
||||
@click.option("--base-balance", type=float, help="Base asset balance for market making")
|
||||
@click.option("--quote-balance", type=float, help="Quote asset balance for market making")
|
||||
@click.option("--min-order-size", type=float, help="Minimum order size")
|
||||
@click.option("--max-order-size", type=float, help="Maximum order size")
|
||||
@click.option("--strategy", default="simple", help="Market making strategy")
|
||||
@click.pass_context
|
||||
def create(ctx, exchange: str, pair: str, spread: float, depth: int,
|
||||
base_balance: Optional[float], quote_balance: Optional[float],
|
||||
min_order_size: Optional[float], max_order_size: Optional[float],
|
||||
strategy: str):
|
||||
"""Create a new market making bot"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
bot_config = {
|
||||
"exchange": exchange,
|
||||
"pair": pair,
|
||||
"spread": spread,
|
||||
"depth": depth,
|
||||
"strategy": strategy,
|
||||
"status": "created"
|
||||
}
|
||||
|
||||
if base_balance is not None:
|
||||
bot_config["base_balance"] = base_balance
|
||||
if quote_balance is not None:
|
||||
bot_config["quote_balance"] = quote_balance
|
||||
if min_order_size is not None:
|
||||
bot_config["min_order_size"] = min_order_size
|
||||
if max_order_size is not None:
|
||||
bot_config["max_order_size"] = max_order_size
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/create",
|
||||
json=bot_config,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
success(f"Market maker bot created for '{pair}' on '{exchange}'!")
|
||||
success(f"Bot ID: {result.get('bot_id')}")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to create market maker: {response.status_code}")
|
||||
if response.text:
|
||||
error(f"Error details: {response.text}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Market maker bot ID")
|
||||
@click.option("--spread", type=float, help="New bid-ask spread")
|
||||
@click.option("--depth", type=int, help="New order book depth")
|
||||
@click.option("--base-balance", type=float, help="New base asset balance")
|
||||
@click.option("--quote-balance", type=float, help="New quote asset balance")
|
||||
@click.option("--min-order-size", type=float, help="New minimum order size")
|
||||
@click.option("--max-order-size", type=float, help="New maximum order size")
|
||||
@click.option("--strategy", help="New market making strategy")
|
||||
@click.pass_context
|
||||
def config(ctx, bot_id: str, spread: Optional[float], depth: Optional[int],
|
||||
base_balance: Optional[float], quote_balance: Optional[float],
|
||||
min_order_size: Optional[float], max_order_size: Optional[float],
|
||||
strategy: Optional[str]):
|
||||
"""Configure market maker bot settings"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
updates = {}
|
||||
if spread is not None:
|
||||
updates["spread"] = spread
|
||||
if depth is not None:
|
||||
updates["depth"] = depth
|
||||
if base_balance is not None:
|
||||
updates["base_balance"] = base_balance
|
||||
if quote_balance is not None:
|
||||
updates["quote_balance"] = quote_balance
|
||||
if min_order_size is not None:
|
||||
updates["min_order_size"] = min_order_size
|
||||
if max_order_size is not None:
|
||||
updates["max_order_size"] = max_order_size
|
||||
if strategy is not None:
|
||||
updates["strategy"] = strategy
|
||||
|
||||
if not updates:
|
||||
error("No configuration updates provided")
|
||||
return
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/config/{bot_id}",
|
||||
json=updates,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
success(f"Market maker {bot_id} configured successfully!")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to configure market maker: {response.status_code}")
|
||||
if response.text:
|
||||
error(f"Error details: {response.text}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Market maker bot ID")
|
||||
@click.option("--dry-run", is_flag=True, help="Test run without executing real trades")
|
||||
@click.pass_context
|
||||
def start(ctx, bot_id: str, dry_run: bool):
|
||||
"""Start market maker bot"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
start_data = {
|
||||
"dry_run": dry_run
|
||||
}
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/start/{bot_id}",
|
||||
json=start_data,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
mode = " (dry run)" if dry_run else ""
|
||||
success(f"Market maker {bot_id} started{mode}!")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to start market maker: {response.status_code}")
|
||||
if response.text:
|
||||
error(f"Error details: {response.text}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Market maker bot ID")
|
||||
@click.pass_context
|
||||
def stop(ctx, bot_id: str):
|
||||
"""Stop market maker bot"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.post(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/stop/{bot_id}",
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
success(f"Market maker {bot_id} stopped!")
|
||||
output(result, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to stop market maker: {response.status_code}")
|
||||
if response.text:
|
||||
error(f"Error details: {response.text}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", help="Specific bot ID to check")
|
||||
@click.option("--exchange", help="Filter by exchange")
|
||||
@click.option("--pair", help="Filter by trading pair")
|
||||
@click.option("--status", help="Filter by status (running, stopped, created)")
|
||||
@click.pass_context
|
||||
def performance(ctx, bot_id: Optional[str], exchange: Optional[str],
|
||||
pair: Optional[str], status: Optional[str]):
|
||||
"""Get market maker performance analytics"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
params = {}
|
||||
if bot_id:
|
||||
params["bot_id"] = bot_id
|
||||
if exchange:
|
||||
params["exchange"] = exchange
|
||||
if pair:
|
||||
params["pair"] = pair
|
||||
if status:
|
||||
params["status"] = status
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/performance",
|
||||
params=params,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
performance_data = response.json()
|
||||
success("Market maker performance:")
|
||||
output(performance_data, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to get performance data: {response.status_code}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", help="Specific bot ID to list")
|
||||
@click.option("--exchange", help="Filter by exchange")
|
||||
@click.option("--pair", help="Filter by trading pair")
|
||||
@click.option("--status", help="Filter by status")
|
||||
@click.pass_context
|
||||
def list(ctx, bot_id: Optional[str], exchange: Optional[str],
|
||||
pair: Optional[str], status: Optional[str]):
|
||||
"""List market maker bots"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
params = {}
|
||||
if bot_id:
|
||||
params["bot_id"] = bot_id
|
||||
if exchange:
|
||||
params["exchange"] = exchange
|
||||
if pair:
|
||||
params["pair"] = pair
|
||||
if status:
|
||||
params["status"] = status
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/list",
|
||||
params=params,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
bots = response.json()
|
||||
success("Market maker bots:")
|
||||
output(bots, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to list market makers: {response.status_code}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Market maker bot ID")
|
||||
@click.option("--hours", type=int, default=24, help="Hours of history to retrieve")
|
||||
@click.pass_context
|
||||
def history(ctx, bot_id: str, hours: int):
|
||||
"""Get market maker trading history"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
params = {
|
||||
"hours": hours
|
||||
}
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/history/{bot_id}",
|
||||
params=params,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
history_data = response.json()
|
||||
success(f"Market maker {bot_id} history (last {hours} hours):")
|
||||
output(history_data, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to get market maker history: {response.status_code}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.option("--bot-id", required=True, help="Market maker bot ID")
|
||||
@click.pass_context
|
||||
def status(ctx, bot_id: str):
|
||||
"""Get market maker bot status"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/status/{bot_id}",
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
status_data = response.json()
|
||||
success(f"Market maker {bot_id} status:")
|
||||
output(status_data, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to get market maker status: {response.status_code}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
|
||||
|
||||
@market_maker.command()
|
||||
@click.pass_context
|
||||
def strategies(ctx):
|
||||
"""List available market making strategies"""
|
||||
config = ctx.obj['config']
|
||||
|
||||
try:
|
||||
with httpx.Client() as client:
|
||||
response = client.get(
|
||||
f"{config.coordinator_url}/api/v1/market-maker/strategies",
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if response.status_code == 200:
|
||||
strategies = response.json()
|
||||
success("Available market making strategies:")
|
||||
output(strategies, ctx.obj['output_format'])
|
||||
else:
|
||||
error(f"Failed to list strategies: {response.status_code}")
|
||||
except Exception as e:
|
||||
error(f"Network error: {e}")
|
||||
Reference in New Issue
Block a user