refactor: clear comment and logs
This commit is contained in:
590
main.py
590
main.py
@@ -7,7 +7,6 @@ from dataclasses import dataclass
|
|||||||
|
|
||||||
import mexc_spot_v3
|
import mexc_spot_v3
|
||||||
|
|
||||||
# 配置日志
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
level=logging.DEBUG,
|
level=logging.DEBUG,
|
||||||
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
@@ -24,32 +23,13 @@ class Order:
|
|||||||
order_id: str
|
order_id: str
|
||||||
price: float
|
price: float
|
||||||
quantity: float
|
quantity: float
|
||||||
side: str # 'BUY' or 'SELL'
|
side: str
|
||||||
status: str # 'NEW', 'FILLED', 'CANCELED'
|
status: str
|
||||||
filled_time: Optional[float] = None # 订单成交时间戳
|
filled_time: Optional[float] = None
|
||||||
|
|
||||||
|
|
||||||
class GridTradingBot:
|
class GridTradingBot:
|
||||||
def __init__(self, conf: Dict):
|
def __init__(self, conf: Dict):
|
||||||
"""
|
|
||||||
初始化网格交易机器人
|
|
||||||
|
|
||||||
参数:
|
|
||||||
conf (Dict): 配置字典,包含:
|
|
||||||
- symbol: 交易对 (如 'BTCUSDC')
|
|
||||||
- csv_symbol: CSV中映射的交易对 (如 'BTCUSDT')
|
|
||||||
- csv_file: CSV记录文件 (如 'output/mexc-spot-grid-trades.csv')
|
|
||||||
- grid_percentage: 每格百分比 (如 0.005 表示 0.5%)
|
|
||||||
- grid_count: 单边网格数量 (如 3)
|
|
||||||
- order_amount: 每单加密货币数量 (如 0.00001 BTC)
|
|
||||||
- min_order_value: 最小挂单价值 (如 1 USDC)
|
|
||||||
- reserve_base: 保留的基础货币数量 (如 1 BTC)
|
|
||||||
- reserve_quote: 保留的报价货币数量 (如 100 USDC)
|
|
||||||
"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.__init__] Initializing GridTradingBot with config: %r",
|
|
||||||
conf,
|
|
||||||
)
|
|
||||||
self.config = conf
|
self.config = conf
|
||||||
self.symbol = conf["symbol"]
|
self.symbol = conf["symbol"]
|
||||||
self.csv_symbol = conf["csv_symbol"]
|
self.csv_symbol = conf["csv_symbol"]
|
||||||
@@ -60,34 +40,14 @@ class GridTradingBot:
|
|||||||
self.min_order_value = conf["min_order_value"]
|
self.min_order_value = conf["min_order_value"]
|
||||||
self.reserve_base = conf["reserve_base"]
|
self.reserve_base = conf["reserve_base"]
|
||||||
self.reserve_quote = conf["reserve_quote"]
|
self.reserve_quote = conf["reserve_quote"]
|
||||||
|
self.active_orders: Dict[str, Order] = {}
|
||||||
# 当前活跃的订单
|
|
||||||
self.active_orders: Dict[str, Order] = {} # order_id -> Order
|
|
||||||
|
|
||||||
# 当前网格范围
|
|
||||||
self.current_buy_levels = 0
|
self.current_buy_levels = 0
|
||||||
self.current_sell_levels = 0
|
self.current_sell_levels = 0
|
||||||
|
|
||||||
# 初始化API
|
|
||||||
logger.debug("[GridTradingBot.__init__] Initializing MEXC API clients")
|
|
||||||
self.api_trade = mexc_spot_v3.mexc_trade()
|
self.api_trade = mexc_spot_v3.mexc_trade()
|
||||||
self.api_market = mexc_spot_v3.mexc_market()
|
self.api_market = mexc_spot_v3.mexc_market()
|
||||||
|
|
||||||
# 运行标志
|
|
||||||
self.running = False
|
self.running = False
|
||||||
logger.info("[GridTradingBot.__init__] GridTradingBot initialized successfully")
|
|
||||||
|
|
||||||
def record_transaction(self, order_response: Dict[str, Any]) -> bool:
|
def record_transaction(self, order_response: Dict[str, Any]) -> bool:
|
||||||
"""
|
|
||||||
记录交易到CSV文件
|
|
||||||
|
|
||||||
Args:
|
|
||||||
order_data: 订单数据字典
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
是否成功记录
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
csv_symbol = self.csv_symbol
|
csv_symbol = self.csv_symbol
|
||||||
order_id = order_response["orderId"]
|
order_id = order_response["orderId"]
|
||||||
@@ -95,11 +55,9 @@ class GridTradingBot:
|
|||||||
cummulative_quote_qty = order_response["cummulativeQuoteQty"]
|
cummulative_quote_qty = order_response["cummulativeQuoteQty"]
|
||||||
side = order_response["side"]
|
side = order_response["side"]
|
||||||
trade_type = "买入" if side == "BUY" else "卖出"
|
trade_type = "买入" if side == "BUY" else "卖出"
|
||||||
|
|
||||||
timestamp = datetime.fromtimestamp(
|
timestamp = datetime.fromtimestamp(
|
||||||
order_response["updateTime"] / 1000
|
order_response["updateTime"] / 1000
|
||||||
).strftime("%Y-%m-%dT%H:%M")
|
).strftime("%Y-%m-%dT%H:%M")
|
||||||
|
|
||||||
row = [
|
row = [
|
||||||
timestamp,
|
timestamp,
|
||||||
trade_type,
|
trade_type,
|
||||||
@@ -110,16 +68,12 @@ class GridTradingBot:
|
|||||||
"CEX",
|
"CEX",
|
||||||
f"MEXC API - Order ID: {order_id}",
|
f"MEXC API - Order ID: {order_id}",
|
||||||
]
|
]
|
||||||
|
|
||||||
# 检查文件是否存在
|
|
||||||
file_exists = False
|
file_exists = False
|
||||||
try:
|
try:
|
||||||
with open(self.csv_file, "r", encoding="utf-8") as f:
|
with open(self.csv_file, "r", encoding="utf-8") as f:
|
||||||
file_exists = True
|
file_exists = True
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# 写入CSV
|
|
||||||
with open(self.csv_file, "a", newline="", encoding="utf-8") as f:
|
with open(self.csv_file, "a", newline="", encoding="utf-8") as f:
|
||||||
writer = csv.writer(f)
|
writer = csv.writer(f)
|
||||||
if not file_exists:
|
if not file_exists:
|
||||||
@@ -136,178 +90,56 @@ class GridTradingBot:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
writer.writerow(row)
|
writer.writerow(row)
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.record_transaction] Transaction recorded, order ID: %s",
|
|
||||||
order_id,
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.record_transaction] Transaction recording failed: %s",
|
|
||||||
str(e),
|
|
||||||
)
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def api_get_price(self) -> float:
|
def api_get_price(self) -> float:
|
||||||
"""获取当前市场价格"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_get_price] Fetching current market price for symbol: %s",
|
|
||||||
self.symbol,
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
ticker = self.api_market.get_price({"symbol": self.symbol})
|
ticker = self.api_market.get_price({"symbol": self.symbol})
|
||||||
price = float(ticker["price"])
|
price = float(ticker["price"])
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_get_price] Current market price: %f", price
|
|
||||||
)
|
|
||||||
return price
|
return price
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.api_get_price] Failed to get price for %s: %s",
|
|
||||||
self.symbol,
|
|
||||||
str(e),
|
|
||||||
)
|
|
||||||
return self.api_get_price()
|
return self.api_get_price()
|
||||||
|
|
||||||
def api_get_balances(self) -> Tuple[float, float]:
|
def api_get_balances(self) -> Tuple[float, float]:
|
||||||
"""获取基础货币和报价货币的可用余额(不包括冻结金额)"""
|
base_currency = self.symbol[:-4]
|
||||||
base_currency = self.symbol[:-4] # 假设报价货币是4个字母,如USDC
|
|
||||||
quote_currency = self.symbol[-4:]
|
quote_currency = self.symbol[-4:]
|
||||||
base_available = 0.0
|
base_available = 0.0
|
||||||
quote_available = 0.0
|
quote_available = 0.0
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_get_balances] Fetching balances for %s and %s",
|
|
||||||
base_currency,
|
|
||||||
quote_currency,
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
balances = self.api_trade.get_account_info()
|
balances = self.api_trade.get_account_info()
|
||||||
for balance in balances.get("balances", []):
|
for balance in balances.get("balances", []):
|
||||||
if balance["asset"] == base_currency:
|
if balance["asset"] == base_currency:
|
||||||
base_available = float(balance["free"])
|
base_available = float(balance["free"])
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_get_balances] Base currency (%s) available: %f",
|
|
||||||
base_currency,
|
|
||||||
base_available,
|
|
||||||
)
|
|
||||||
elif balance["asset"] == quote_currency:
|
elif balance["asset"] == quote_currency:
|
||||||
quote_available = float(balance["free"])
|
quote_available = float(balance["free"])
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_get_balances] Quote currency (%s) available: %f",
|
|
||||||
quote_currency,
|
|
||||||
quote_available,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_get_balances] Final balances - base: %f, quote: %f",
|
|
||||||
base_available,
|
|
||||||
quote_available,
|
|
||||||
)
|
|
||||||
return base_available, quote_available
|
return base_available, quote_available
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.api_get_balances] Failed to get balances: %s", str(e)
|
|
||||||
)
|
|
||||||
return self.api_get_balances()
|
return self.api_get_balances()
|
||||||
|
|
||||||
def calculate_order_value(self, price: float) -> float:
|
def calculate_order_value(self, price: float) -> float:
|
||||||
"""计算订单价值 (价格 * 数量)"""
|
|
||||||
value = price * self.order_amount
|
value = price * self.order_amount
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.calculate_order_value] Calculated order value: price=%f * amount=%f = %f",
|
|
||||||
price,
|
|
||||||
self.order_amount,
|
|
||||||
value,
|
|
||||||
)
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def is_order_value_valid(self, price: float) -> bool:
|
def is_order_value_valid(self, price: float) -> bool:
|
||||||
"""检查订单价值是否满足最小挂单要求"""
|
|
||||||
value = self.calculate_order_value(price)
|
value = self.calculate_order_value(price)
|
||||||
is_valid = value >= self.min_order_value
|
is_valid = value >= self.min_order_value
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.is_order_value_valid] Order value validation: %f >= %f? %s",
|
|
||||||
value,
|
|
||||||
self.min_order_value,
|
|
||||||
is_valid,
|
|
||||||
)
|
|
||||||
return is_valid
|
return is_valid
|
||||||
|
|
||||||
def calculate_grid_price(self, base_price: float, level: int, side: str) -> float:
|
def calculate_grid_price(self, base_price: float, level: int, side: str) -> float:
|
||||||
"""
|
|
||||||
计算网格价格
|
|
||||||
|
|
||||||
参数:
|
|
||||||
base_price: 基础价格
|
|
||||||
level: 网格级别 (正数)
|
|
||||||
side: 'BUY' 或 'SELL'
|
|
||||||
|
|
||||||
返回:
|
|
||||||
计算后的价格
|
|
||||||
"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.calculate_grid_price] Calculating grid price: base=%f, level=%d, side=%s",
|
|
||||||
base_price,
|
|
||||||
level,
|
|
||||||
side,
|
|
||||||
)
|
|
||||||
|
|
||||||
if side == "BUY":
|
if side == "BUY":
|
||||||
price = base_price / (1 + self.grid_percentage) ** level
|
price = base_price / (1 + self.grid_percentage) ** level
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.calculate_grid_price] Calculated BUY price: %f / (1 + %f)^%d = %f",
|
|
||||||
base_price,
|
|
||||||
self.grid_percentage,
|
|
||||||
level,
|
|
||||||
price,
|
|
||||||
)
|
|
||||||
return price
|
return price
|
||||||
elif side == "SELL":
|
elif side == "SELL":
|
||||||
price = base_price * (1 + self.grid_percentage) ** level
|
price = base_price * (1 + self.grid_percentage) ** level
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.calculate_grid_price] Calculated SELL price: %f * (1 + %f)^%d = %f",
|
|
||||||
base_price,
|
|
||||||
self.grid_percentage,
|
|
||||||
level,
|
|
||||||
price,
|
|
||||||
)
|
|
||||||
return price
|
return price
|
||||||
else:
|
else:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.calculate_grid_price] Invalid side for grid price calculation: %s",
|
|
||||||
side,
|
|
||||||
)
|
|
||||||
raise ValueError(f"Invalid side: {side}")
|
raise ValueError(f"Invalid side: {side}")
|
||||||
|
|
||||||
def api_place_order(self, price: float, side: str) -> Optional[str]:
|
def api_place_order(self, price: float, side: str) -> Optional[str]:
|
||||||
"""
|
|
||||||
下订单
|
|
||||||
|
|
||||||
参数:
|
|
||||||
price: 价格
|
|
||||||
side: 'BUY' 或 'SELL'
|
|
||||||
|
|
||||||
返回:
|
|
||||||
订单ID (如果下单成功) 或 None (如果失败)
|
|
||||||
"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_place_order] Attempting to place %s order at price: %f",
|
|
||||||
side,
|
|
||||||
price,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not self.is_order_value_valid(price):
|
if not self.is_order_value_valid(price):
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.api_place_order] Order value too small: price=%f, amount=%f, value=%f < min=%f",
|
|
||||||
price,
|
|
||||||
self.order_amount,
|
|
||||||
price * self.order_amount,
|
|
||||||
self.min_order_value,
|
|
||||||
)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
order_detail = self.api_trade.post_order(
|
order_detail = self.api_trade.post_order(
|
||||||
{
|
{
|
||||||
@@ -318,222 +150,76 @@ class GridTradingBot:
|
|||||||
"quantity": self.order_amount,
|
"quantity": self.order_amount,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
order_id = order_detail.get("orderId")
|
order_id = order_detail.get("orderId")
|
||||||
if order_id:
|
if order_id:
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.api_place_order] Successfully placed %s order - price: %f, amount: %f, order_id: %s",
|
|
||||||
side,
|
|
||||||
price,
|
|
||||||
self.order_amount,
|
|
||||||
order_id,
|
|
||||||
)
|
|
||||||
return order_id
|
return order_id
|
||||||
else:
|
else:
|
||||||
logger.error(
|
return None
|
||||||
"[GridTradingBot.api_place_order] Failed to place %s order at %f: %r",
|
|
||||||
side,
|
|
||||||
price,
|
|
||||||
order_detail,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.api_place_order] Exception occurred while placing %s order at %f: %s",
|
|
||||||
side,
|
|
||||||
price,
|
|
||||||
str(e),
|
|
||||||
)
|
|
||||||
return self.api_place_order(price, side)
|
return self.api_place_order(price, side)
|
||||||
|
|
||||||
def api_cancel_order(self, order_id: str):
|
def api_cancel_order(self, order_id: str):
|
||||||
"""取消订单"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_cancel_order] Attempting to cancel order: %s", order_id
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
self.api_trade.delete_order({"symbol": self.symbol, "orderId": order_id})
|
self.api_trade.delete_order({"symbol": self.symbol, "orderId": order_id})
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.api_cancel_order] Successfully canceled order: %s",
|
|
||||||
order_id,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.api_cancel_order] Failed to cancel order %s: %s",
|
|
||||||
order_id,
|
|
||||||
str(e),
|
|
||||||
)
|
|
||||||
self.api_cancel_order(order_id)
|
self.api_cancel_order(order_id)
|
||||||
|
|
||||||
def api_cancel_all_orders(self):
|
def api_cancel_all_orders(self):
|
||||||
"""取消所有活跃订单"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_cancel_all_orders] Attempting to cancel all open orders for symbol: %s",
|
|
||||||
self.symbol,
|
|
||||||
)
|
|
||||||
try:
|
try:
|
||||||
self.api_trade.delete_openorders({"symbol": self.symbol})
|
self.api_trade.delete_openorders({"symbol": self.symbol})
|
||||||
self.active_orders.clear()
|
self.active_orders.clear()
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.api_cancel_all_orders] Successfully canceled all open orders for %s",
|
|
||||||
self.symbol,
|
|
||||||
)
|
|
||||||
self.current_buy_levels = 0
|
self.current_buy_levels = 0
|
||||||
self.current_sell_levels = 0
|
self.current_sell_levels = 0
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.api_cancel_all_orders] Failed to cancel all open orders: %s",
|
|
||||||
str(e),
|
|
||||||
)
|
|
||||||
self.api_cancel_all_orders()
|
self.api_cancel_all_orders()
|
||||||
|
|
||||||
def api_update_order_statuses(self):
|
def api_update_order_statuses(self):
|
||||||
"""更新所有活跃订单的状态"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_update_order_statuses] Updating status for %d active orders",
|
|
||||||
len(self.active_orders),
|
|
||||||
)
|
|
||||||
|
|
||||||
# 分别处理买单和卖单
|
|
||||||
for side in ["BUY", "SELL"]:
|
for side in ["BUY", "SELL"]:
|
||||||
# 获取当前方向的所有订单并按价格排序
|
|
||||||
orders = self.get_active_orders_by_side(side)
|
orders = self.get_active_orders_by_side(side)
|
||||||
|
|
||||||
# 买单从高到低排序,卖单从低到高排序
|
|
||||||
if side == "BUY":
|
if side == "BUY":
|
||||||
orders.sort(key=lambda x: float(x.price), reverse=True)
|
orders.sort(key=lambda x: float(x.price), reverse=True)
|
||||||
else:
|
else:
|
||||||
orders.sort(key=lambda x: float(x.price))
|
orders.sort(key=lambda x: float(x.price))
|
||||||
|
|
||||||
# 遍历订单
|
|
||||||
for order in orders:
|
for order in orders:
|
||||||
order_id = order.order_id
|
order_id = order.order_id
|
||||||
try:
|
try:
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_update_order_statuses] Checking status for order: %s",
|
|
||||||
order_id,
|
|
||||||
)
|
|
||||||
order_info = self.api_trade.get_order(
|
order_info = self.api_trade.get_order(
|
||||||
{"symbol": self.symbol, "orderId": order_id}
|
{"symbol": self.symbol, "orderId": order_id}
|
||||||
)
|
)
|
||||||
|
|
||||||
# 更新订单状态
|
|
||||||
old_status = self.active_orders[order_id].status
|
old_status = self.active_orders[order_id].status
|
||||||
new_status = order_info["status"]
|
new_status = order_info["status"]
|
||||||
self.active_orders[order_id].status = new_status
|
self.active_orders[order_id].status = new_status
|
||||||
|
|
||||||
# 记录状态变化
|
|
||||||
if old_status != new_status:
|
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.api_update_order_statuses] Order %s status changed from %s to %s",
|
|
||||||
order_id,
|
|
||||||
old_status,
|
|
||||||
new_status,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 如果订单已完成,记录成交时间
|
|
||||||
if new_status == "FILLED":
|
if new_status == "FILLED":
|
||||||
self.active_orders[order_id].filled_time = time.time()
|
self.active_orders[order_id].filled_time = time.time()
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_update_order_statuses] Order %s filled at %f",
|
|
||||||
order_id,
|
|
||||||
self.active_orders[order_id].filled_time,
|
|
||||||
)
|
|
||||||
self.record_transaction(order_info)
|
self.record_transaction(order_info)
|
||||||
|
|
||||||
if new_status in ["CANCELED"]:
|
if new_status in ["CANCELED"]:
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.api_update_order_statuses] Removing order %s from active orders (status: %s)",
|
|
||||||
order_id,
|
|
||||||
new_status,
|
|
||||||
)
|
|
||||||
self.active_orders.pop(order_id)
|
self.active_orders.pop(order_id)
|
||||||
|
|
||||||
if new_status == "NEW":
|
if new_status == "NEW":
|
||||||
logger.debug("[GridTradingBot.api_update_order_statuses] NEW order detected for %s side, skipping.", side)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.api_update_order_statuses] Failed to get status for order %s: %s",
|
|
||||||
order_id,
|
|
||||||
str(e),
|
|
||||||
)
|
|
||||||
self.api_update_order_statuses()
|
self.api_update_order_statuses()
|
||||||
|
|
||||||
def get_active_orders_by_side(self, side: str) -> List[Order]:
|
def get_active_orders_by_side(self, side: str) -> List[Order]:
|
||||||
"""获取指定方向的所有活跃订单"""
|
|
||||||
orders = [order for order in self.active_orders.values() if order.side == side]
|
orders = [order for order in self.active_orders.values() if order.side == side]
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.get_active_orders_by_side] Found %d active %s orders",
|
|
||||||
len(orders),
|
|
||||||
side,
|
|
||||||
)
|
|
||||||
return orders
|
return orders
|
||||||
|
|
||||||
def get_extreme_prices(self) -> Tuple[Optional[float], Optional[float]]:
|
def get_extreme_prices(self) -> Tuple[Optional[float], Optional[float]]:
|
||||||
"""
|
|
||||||
获取当前最极端的买单和卖单价格
|
|
||||||
|
|
||||||
返回:
|
|
||||||
(最低买单价格, 最高卖单价格) 如果没有订单则为 (None, None)
|
|
||||||
"""
|
|
||||||
buy_orders = self.get_active_orders_by_side("BUY")
|
buy_orders = self.get_active_orders_by_side("BUY")
|
||||||
sell_orders = self.get_active_orders_by_side("SELL")
|
sell_orders = self.get_active_orders_by_side("SELL")
|
||||||
|
|
||||||
lowest_buy = min([order.price for order in buy_orders]) if buy_orders else None
|
lowest_buy = min([order.price for order in buy_orders]) if buy_orders else None
|
||||||
highest_sell = (
|
highest_sell = (
|
||||||
max([order.price for order in sell_orders]) if sell_orders else None
|
max([order.price for order in sell_orders]) if sell_orders else None
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.get_extreme_prices] Extreme prices - lowest buy: %s, highest sell: %s",
|
|
||||||
lowest_buy,
|
|
||||||
highest_sell,
|
|
||||||
)
|
|
||||||
return lowest_buy, highest_sell
|
return lowest_buy, highest_sell
|
||||||
|
|
||||||
def initialize_grid(self):
|
def initialize_grid(self):
|
||||||
"""初始化网格"""
|
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.initialize_grid] Initializing grid for symbol: %s",
|
|
||||||
self.symbol,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
market_price = self.api_get_price()
|
market_price = self.api_get_price()
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.initialize_grid] Current market price: %f",
|
|
||||||
market_price,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 初始下单: 买1和卖1
|
|
||||||
buy_price = self.calculate_grid_price(market_price, 1, "BUY")
|
buy_price = self.calculate_grid_price(market_price, 1, "BUY")
|
||||||
sell_price = self.calculate_grid_price(market_price, 1, "SELL")
|
sell_price = self.calculate_grid_price(market_price, 1, "SELL")
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.initialize_grid] Initial buy price: %f, sell price: %f",
|
|
||||||
buy_price,
|
|
||||||
sell_price,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 检查余额并下单
|
|
||||||
base_balance, quote_balance = self.api_get_balances()
|
base_balance, quote_balance = self.api_get_balances()
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.initialize_grid] Current balances - base: %f, quote: %f",
|
|
||||||
base_balance,
|
|
||||||
quote_balance,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 下单买1
|
|
||||||
if quote_balance > self.reserve_quote:
|
if quote_balance > self.reserve_quote:
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.initialize_grid] Quote balance sufficient (%f > %f), checking order value",
|
|
||||||
quote_balance,
|
|
||||||
self.reserve_quote,
|
|
||||||
)
|
|
||||||
if self.is_order_value_valid(buy_price):
|
if self.is_order_value_valid(buy_price):
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.initialize_grid] Order value valid, placing buy order"
|
|
||||||
)
|
|
||||||
buy_order_id = self.api_place_order(buy_price, "BUY")
|
buy_order_id = self.api_place_order(buy_price, "BUY")
|
||||||
if buy_order_id:
|
if buy_order_id:
|
||||||
self.active_orders[buy_order_id] = Order(
|
self.active_orders[buy_order_id] = Order(
|
||||||
@@ -544,34 +230,8 @@ class GridTradingBot:
|
|||||||
status="NEW",
|
status="NEW",
|
||||||
)
|
)
|
||||||
self.current_buy_levels = 1
|
self.current_buy_levels = 1
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.initialize_grid] Initial buy order placed at %f",
|
|
||||||
buy_price,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.initialize_grid] Initial buy order value too small: %f < %f",
|
|
||||||
buy_price * self.order_amount,
|
|
||||||
self.min_order_value,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.initialize_grid] Insufficient quote balance for initial buy: %f <= %f",
|
|
||||||
quote_balance,
|
|
||||||
self.reserve_quote,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 下单卖1
|
|
||||||
if base_balance > self.reserve_base:
|
if base_balance > self.reserve_base:
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.initialize_grid] Base balance sufficient (%f > %f), checking order value",
|
|
||||||
base_balance,
|
|
||||||
self.reserve_base,
|
|
||||||
)
|
|
||||||
if self.is_order_value_valid(sell_price):
|
if self.is_order_value_valid(sell_price):
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.initialize_grid] Order value valid, placing sell order"
|
|
||||||
)
|
|
||||||
sell_order_id = self.api_place_order(sell_price, "SELL")
|
sell_order_id = self.api_place_order(sell_price, "SELL")
|
||||||
if sell_order_id:
|
if sell_order_id:
|
||||||
self.active_orders[sell_order_id] = Order(
|
self.active_orders[sell_order_id] = Order(
|
||||||
@@ -582,49 +242,16 @@ class GridTradingBot:
|
|||||||
status="NEW",
|
status="NEW",
|
||||||
)
|
)
|
||||||
self.current_sell_levels = 1
|
self.current_sell_levels = 1
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.initialize_grid] Initial sell order placed at %f",
|
|
||||||
sell_price,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.initialize_grid] Initial sell order value too small: %f < %f",
|
|
||||||
sell_price * self.order_amount,
|
|
||||||
self.min_order_value,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.initialize_grid] Insufficient base balance for initial sell: %f <= %f",
|
|
||||||
base_balance,
|
|
||||||
self.reserve_base,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.extend_grid()
|
self.extend_grid()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.initialize_grid] Failed to initialize grid: %s", str(e)
|
|
||||||
)
|
|
||||||
self.initialize_grid()
|
self.initialize_grid()
|
||||||
|
|
||||||
def extend_grid(self):
|
def extend_grid(self):
|
||||||
"""扩展网格,保证两侧都有指定个数的挂单"""
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] Extending grid - current buy levels: %d/%d, sell levels: %d/%d",
|
|
||||||
self.current_buy_levels,
|
|
||||||
self.grid_count,
|
|
||||||
self.current_sell_levels,
|
|
||||||
self.grid_count,
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 如果两侧都没有挂单 (如: 插针) 则重启机器人
|
|
||||||
lowest_buy, highest_sell = self.get_extreme_prices()
|
lowest_buy, highest_sell = self.get_extreme_prices()
|
||||||
if lowest_buy is None and highest_sell is None:
|
if lowest_buy is None and highest_sell is None:
|
||||||
self.stop()
|
self.stop()
|
||||||
self.run()
|
self.run()
|
||||||
|
|
||||||
# 扩展买单网格 (向下)
|
|
||||||
while self.current_buy_levels < self.grid_count:
|
while self.current_buy_levels < self.grid_count:
|
||||||
lowest_buy, highest_sell = self.get_extreme_prices()
|
lowest_buy, highest_sell = self.get_extreme_prices()
|
||||||
base_balance, quote_balance = self.api_get_balances()
|
base_balance, quote_balance = self.api_get_balances()
|
||||||
@@ -632,26 +259,12 @@ class GridTradingBot:
|
|||||||
lowest_buy = self.calculate_grid_price(
|
lowest_buy = self.calculate_grid_price(
|
||||||
highest_sell, self.grid_count, "BUY"
|
highest_sell, self.grid_count, "BUY"
|
||||||
)
|
)
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] lowest_buy was None, calculating based on highest_sell"
|
|
||||||
)
|
|
||||||
new_buy_price = self.calculate_grid_price(lowest_buy, 1, "BUY")
|
new_buy_price = self.calculate_grid_price(lowest_buy, 1, "BUY")
|
||||||
logger.debug(
|
if (
|
||||||
"[GridTradingBot.extend_grid] Extending buy grid - current lowest: %f, new price: %f",
|
quote_balance - self.order_amount * new_buy_price
|
||||||
lowest_buy,
|
> self.reserve_quote
|
||||||
new_buy_price,
|
):
|
||||||
)
|
|
||||||
|
|
||||||
if quote_balance - self.order_amount * new_buy_price > self.reserve_quote:
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] Quote balance sufficient (%f > %f), checking order value",
|
|
||||||
quote_balance,
|
|
||||||
self.reserve_quote,
|
|
||||||
)
|
|
||||||
if self.is_order_value_valid(new_buy_price):
|
if self.is_order_value_valid(new_buy_price):
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] Order value valid, placing extended buy order"
|
|
||||||
)
|
|
||||||
buy_order_id = self.api_place_order(new_buy_price, "BUY")
|
buy_order_id = self.api_place_order(new_buy_price, "BUY")
|
||||||
if buy_order_id:
|
if buy_order_id:
|
||||||
self.active_orders[buy_order_id] = Order(
|
self.active_orders[buy_order_id] = Order(
|
||||||
@@ -662,27 +275,10 @@ class GridTradingBot:
|
|||||||
status="NEW",
|
status="NEW",
|
||||||
)
|
)
|
||||||
self.current_buy_levels += 1
|
self.current_buy_levels += 1
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.extend_grid] Extended buy grid to level %d at price %f",
|
|
||||||
self.current_buy_levels,
|
|
||||||
new_buy_price,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.extend_grid] Extended buy order value too small: %f < %f",
|
|
||||||
new_buy_price * self.order_amount,
|
|
||||||
self.min_order_value,
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.extend_grid] Insufficient quote balance for extended buy: %f <= %f",
|
|
||||||
quote_balance,
|
|
||||||
self.reserve_quote,
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
# 扩展卖单网格 (向上)
|
|
||||||
while self.current_sell_levels < self.grid_count:
|
while self.current_sell_levels < self.grid_count:
|
||||||
lowest_buy, highest_sell = self.get_extreme_prices()
|
lowest_buy, highest_sell = self.get_extreme_prices()
|
||||||
base_balance, quote_balance = self.api_get_balances()
|
base_balance, quote_balance = self.api_get_balances()
|
||||||
@@ -690,26 +286,9 @@ class GridTradingBot:
|
|||||||
highest_sell = self.calculate_grid_price(
|
highest_sell = self.calculate_grid_price(
|
||||||
lowest_buy, self.grid_count, "SELL"
|
lowest_buy, self.grid_count, "SELL"
|
||||||
)
|
)
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] highest_sell was None, calculating based on lowest_buy"
|
|
||||||
)
|
|
||||||
new_sell_price = self.calculate_grid_price(highest_sell, 1, "SELL")
|
new_sell_price = self.calculate_grid_price(highest_sell, 1, "SELL")
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] Extending sell grid - current highest: %f, new price: %f",
|
|
||||||
highest_sell,
|
|
||||||
new_sell_price,
|
|
||||||
)
|
|
||||||
|
|
||||||
if base_balance > self.reserve_base:
|
if base_balance > self.reserve_base:
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] Base balance sufficient (%f > %f), checking order value",
|
|
||||||
base_balance,
|
|
||||||
self.reserve_base,
|
|
||||||
)
|
|
||||||
if self.is_order_value_valid(new_sell_price):
|
if self.is_order_value_valid(new_sell_price):
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.extend_grid] Order value valid, placing extended sell order"
|
|
||||||
)
|
|
||||||
sell_order_id = self.api_place_order(new_sell_price, "SELL")
|
sell_order_id = self.api_place_order(new_sell_price, "SELL")
|
||||||
if sell_order_id:
|
if sell_order_id:
|
||||||
self.active_orders[sell_order_id] = Order(
|
self.active_orders[sell_order_id] = Order(
|
||||||
@@ -720,70 +299,27 @@ class GridTradingBot:
|
|||||||
status="NEW",
|
status="NEW",
|
||||||
)
|
)
|
||||||
self.current_sell_levels += 1
|
self.current_sell_levels += 1
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.extend_grid] Extended sell grid to level %d at price %f",
|
|
||||||
self.current_sell_levels,
|
|
||||||
new_sell_price,
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.extend_grid] Extended sell order value too small: %f < %f",
|
|
||||||
new_sell_price * self.order_amount,
|
|
||||||
self.min_order_value,
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.extend_grid] Insufficient base balance for extended sell: %f <= %f",
|
|
||||||
base_balance,
|
|
||||||
self.reserve_base,
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.extend_grid] Failed to extend grid: %s", str(e)
|
|
||||||
)
|
|
||||||
self.extend_grid()
|
self.extend_grid()
|
||||||
|
|
||||||
def adjust_grid_for_filled(self):
|
def adjust_grid_for_filled(self):
|
||||||
"""调整网格 - 优化后的对称逻辑:
|
|
||||||
买单成交时:
|
|
||||||
1. 取消最高价卖单
|
|
||||||
2. 在卖单侧挂一个更低价的卖单(卖0)
|
|
||||||
3. 在买单侧挂一个更低价的买单(买n)
|
|
||||||
卖单成交时:
|
|
||||||
1. 取消最低价买单
|
|
||||||
2. 在买单侧挂一个更高价的买单(买0)
|
|
||||||
3. 在卖单侧挂一个更高价的卖单(卖n)"""
|
|
||||||
logger.debug("[GridTradingBot.adjust_grid] Adjusting grid with symmetric logic")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 获取当前所有订单的快照
|
|
||||||
current_order_ids = set(self.active_orders.keys())
|
current_order_ids = set(self.active_orders.keys())
|
||||||
|
|
||||||
# 找出新成交的订单(之前存在且现在状态为FILLED)
|
|
||||||
newly_filled_orders = [
|
newly_filled_orders = [
|
||||||
order
|
order
|
||||||
for order in self.active_orders.values()
|
for order in self.active_orders.values()
|
||||||
if order.order_id in current_order_ids and order.status == "FILLED"
|
if order.order_id in current_order_ids and order.status == "FILLED"
|
||||||
]
|
]
|
||||||
|
|
||||||
if newly_filled_orders:
|
if newly_filled_orders:
|
||||||
logger.debug("[GridTradingBot.adjust_grid] Newly filled orders found")
|
|
||||||
# 处理每个新成交的订单
|
|
||||||
for filled_order in newly_filled_orders:
|
for filled_order in newly_filled_orders:
|
||||||
filled_side = filled_order.side
|
filled_side = filled_order.side
|
||||||
filled_price = filled_order.price
|
filled_price = filled_order.price
|
||||||
filled_id = filled_order.order_id
|
filled_id = filled_order.order_id
|
||||||
self.active_orders.pop(filled_id, None)
|
self.active_orders.pop(filled_id, None)
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.adjust_grid] Processing newly filled %s order at price %f",
|
|
||||||
filled_side,
|
|
||||||
filled_price,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 获取当前活跃订单
|
|
||||||
active_buys = [
|
active_buys = [
|
||||||
o
|
o
|
||||||
for o in self.active_orders.values()
|
for o in self.active_orders.values()
|
||||||
@@ -794,28 +330,17 @@ class GridTradingBot:
|
|||||||
for o in self.active_orders.values()
|
for o in self.active_orders.values()
|
||||||
if o.side == "SELL" and o.status == "NEW"
|
if o.side == "SELL" and o.status == "NEW"
|
||||||
]
|
]
|
||||||
|
|
||||||
if filled_side == "BUY":
|
if filled_side == "BUY":
|
||||||
self.current_buy_levels -= 1
|
self.current_buy_levels -= 1
|
||||||
elif filled_side == "SELL":
|
elif filled_side == "SELL":
|
||||||
self.current_sell_levels -= 1
|
self.current_sell_levels -= 1
|
||||||
if filled_side == "BUY" and active_sells:
|
if filled_side == "BUY" and active_sells:
|
||||||
# 买单成交时的处理逻辑
|
|
||||||
# 1. 取消最高价卖单
|
|
||||||
highest_sell = max(active_sells, key=lambda x: x.price)
|
highest_sell = max(active_sells, key=lambda x: x.price)
|
||||||
self.api_cancel_order(highest_sell.order_id)
|
self.api_cancel_order(highest_sell.order_id)
|
||||||
self.active_orders.pop(highest_sell.order_id, None)
|
self.active_orders.pop(highest_sell.order_id, None)
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.adjust_grid] Cancelled highest SELL order at %f (order_id: %s)",
|
|
||||||
highest_sell.price,
|
|
||||||
highest_sell.order_id,
|
|
||||||
)
|
|
||||||
# 2. 在卖单侧挂一个更低价的卖单(卖0)
|
|
||||||
# 使用最近成交的买单价格作为基准
|
|
||||||
new_sell_price = self.calculate_grid_price(
|
new_sell_price = self.calculate_grid_price(
|
||||||
filled_price, 1, "SELL"
|
filled_price, 1, "SELL"
|
||||||
)
|
)
|
||||||
# 检查余额和订单价值
|
|
||||||
base_balance, _ = self.api_get_balances()
|
base_balance, _ = self.api_get_balances()
|
||||||
if (
|
if (
|
||||||
base_balance > self.reserve_base
|
base_balance > self.reserve_base
|
||||||
@@ -830,30 +355,13 @@ class GridTradingBot:
|
|||||||
side="SELL",
|
side="SELL",
|
||||||
status="NEW",
|
status="NEW",
|
||||||
)
|
)
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.adjust_grid] Placed new lower SELL order at %f (order_id: %s)",
|
|
||||||
new_sell_price,
|
|
||||||
sell_order_id,
|
|
||||||
)
|
|
||||||
|
|
||||||
elif filled_side == "SELL" and active_buys:
|
elif filled_side == "SELL" and active_buys:
|
||||||
# 卖单成交时的处理逻辑
|
|
||||||
# 1. 取消最低价买单
|
|
||||||
lowest_buy = min(active_buys, key=lambda x: x.price)
|
lowest_buy = min(active_buys, key=lambda x: x.price)
|
||||||
self.api_cancel_order(lowest_buy.order_id)
|
self.api_cancel_order(lowest_buy.order_id)
|
||||||
self.active_orders.pop(lowest_buy.order_id, None)
|
self.active_orders.pop(lowest_buy.order_id, None)
|
||||||
|
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.adjust_grid] Cancelled lowest BUY order at %f (order_id: %s)",
|
|
||||||
lowest_buy.price,
|
|
||||||
lowest_buy.order_id,
|
|
||||||
)
|
|
||||||
# 2. 在买单侧挂一个更高价的买单(买0)
|
|
||||||
# 使用最近成交的卖单价格作为基准
|
|
||||||
new_buy_price = self.calculate_grid_price(
|
new_buy_price = self.calculate_grid_price(
|
||||||
filled_price, 1, "BUY"
|
filled_price, 1, "BUY"
|
||||||
)
|
)
|
||||||
# 检查余额和订单价值
|
|
||||||
_, quote_balance = self.api_get_balances()
|
_, quote_balance = self.api_get_balances()
|
||||||
if (
|
if (
|
||||||
quote_balance > self.reserve_quote
|
quote_balance > self.reserve_quote
|
||||||
@@ -868,15 +376,7 @@ class GridTradingBot:
|
|||||||
side="BUY",
|
side="BUY",
|
||||||
status="NEW",
|
status="NEW",
|
||||||
)
|
)
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.adjust_grid] Placed new higher BUY order at %f (order_id: %s)",
|
|
||||||
new_buy_price,
|
|
||||||
buy_order_id,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.adjust_grid] Failed to adjust grid: %s", str(e)
|
|
||||||
)
|
|
||||||
self.adjust_grid_for_filled()
|
self.adjust_grid_for_filled()
|
||||||
|
|
||||||
def adjust_grid_for_violation(self):
|
def adjust_grid_for_violation(self):
|
||||||
@@ -886,96 +386,50 @@ class GridTradingBot:
|
|||||||
if lowest_buy is None and market_price < self.calculate_grid_price(
|
if lowest_buy is None and market_price < self.calculate_grid_price(
|
||||||
highest_sell, self.grid_count + 1, "BUY"
|
highest_sell, self.grid_count + 1, "BUY"
|
||||||
):
|
):
|
||||||
logger.warning(
|
|
||||||
"[GridTradingBot.adjust_grid_for_violation] Price varied too low, restarting grid."
|
|
||||||
)
|
|
||||||
self.api_cancel_all_orders()
|
self.api_cancel_all_orders()
|
||||||
self.initialize_grid()
|
self.initialize_grid()
|
||||||
elif highest_sell is None and market_price > self.calculate_grid_price(
|
elif highest_sell is None and market_price > self.calculate_grid_price(
|
||||||
lowest_buy, self.grid_count + 1, "SELL"
|
lowest_buy, self.grid_count + 1, "SELL"
|
||||||
):
|
):
|
||||||
logger.warning(
|
|
||||||
"[GridTradingBot.adjust_grid_for_violation] Price varied too high, restarting grid."
|
|
||||||
)
|
|
||||||
self.api_cancel_all_orders()
|
self.api_cancel_all_orders()
|
||||||
self.initialize_grid()
|
self.initialize_grid()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(
|
|
||||||
"[GridTradingBot.adjust_grid_for_violation] Failed to adjust grid: %s",
|
|
||||||
str(e),
|
|
||||||
)
|
|
||||||
self.adjust_grid_for_violation()
|
self.adjust_grid_for_violation()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""运行网格交易机器人"""
|
|
||||||
self.running = True
|
self.running = True
|
||||||
logger.info(
|
|
||||||
"[GridTradingBot.run] Starting grid trading bot for symbol: %s", self.symbol
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 初始化网格
|
|
||||||
self.initialize_grid()
|
self.initialize_grid()
|
||||||
|
|
||||||
# 主循环
|
|
||||||
while self.running:
|
while self.running:
|
||||||
try:
|
try:
|
||||||
logger.debug("[GridTradingBot.run] Starting main loop iteration")
|
|
||||||
|
|
||||||
# 更新订单状态
|
|
||||||
self.api_update_order_statuses()
|
self.api_update_order_statuses()
|
||||||
|
|
||||||
# 调整网格
|
|
||||||
self.adjust_grid_for_filled()
|
self.adjust_grid_for_filled()
|
||||||
self.adjust_grid_for_violation()
|
self.adjust_grid_for_violation()
|
||||||
|
|
||||||
# 尝试扩展网格
|
|
||||||
self.extend_grid()
|
self.extend_grid()
|
||||||
|
time.sleep(1)
|
||||||
# 等待一段时间再检查
|
|
||||||
sleep_time = 1
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.run] Sleeping for %d seconds", sleep_time
|
|
||||||
)
|
|
||||||
time.sleep(sleep_time)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error("[GridTradingBot.run] Error in main loop: %s", str(e))
|
time.sleep(3)
|
||||||
sleep_time = 3
|
|
||||||
logger.debug(
|
|
||||||
"[GridTradingBot.run] Error occurred, sleeping for %d seconds",
|
|
||||||
sleep_time,
|
|
||||||
)
|
|
||||||
time.sleep(sleep_time) # 出错后等待更长时间
|
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
logger.info("[GridTradingBot.run] Received keyboard interrupt, stopping...")
|
pass
|
||||||
finally:
|
finally:
|
||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""停止机器人"""
|
|
||||||
logger.info("[GridTradingBot.stop] Stopping grid trading bot...")
|
|
||||||
self.running = False
|
self.running = False
|
||||||
self.api_cancel_all_orders()
|
self.api_cancel_all_orders()
|
||||||
logger.info("[GridTradingBot.stop] Grid trading bot stopped successfully")
|
|
||||||
|
|
||||||
|
|
||||||
# 示例配置和使用
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
config = {
|
config = {
|
||||||
"symbol": "BTCUSDC", # 交易对
|
"symbol": "BTCUSDC",
|
||||||
"csv_symbol": "BTCUSDT", # CSV记录映射交易对
|
"csv_symbol": "BTCUSDT",
|
||||||
"csv_file": "output/mexc_spot_grid_trades.csv", # CSV记录文件
|
"csv_file": "output/mexc_spot_grid_trades.csv",
|
||||||
"grid_percentage": 0.001, # 等比网格的公比
|
"grid_percentage": 0.001,
|
||||||
"grid_count": 3, # 单侧的挂单数,实时平衡
|
"grid_count": 3,
|
||||||
"order_amount": 0.00001, # BTC数量
|
"order_amount": 0.00001,
|
||||||
"min_order_value": 1, # 交易所限制订单价值至少 1 USDC
|
"min_order_value": 1,
|
||||||
"reserve_base": 0, # 保留 0 BTC 不参与网格
|
"reserve_base": 0,
|
||||||
"reserve_quote": 0, # 保留 0 USDC 不参与网格
|
"reserve_quote": 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("[__main__] Creating GridTradingBot instance with config: %r", config)
|
|
||||||
bot = GridTradingBot(config)
|
bot = GridTradingBot(config)
|
||||||
bot.run()
|
bot.run()
|
||||||
|
Reference in New Issue
Block a user