|
|
@@ -10,6 +10,7 @@ from checker.logger_config import get_logger
|
|
|
from pprint import pformat
|
|
|
|
|
|
web3 = EthClient()
|
|
|
+web3_backup = EthClient(os.getenv("RPC_URL_2"))
|
|
|
mexc = MexcClient()
|
|
|
|
|
|
# 配置日志
|
|
|
@@ -56,14 +57,16 @@ class ArbitrageProcess:
|
|
|
self.eth_price = self.core_data['eth_price']
|
|
|
|
|
|
with self.mexc_lock:
|
|
|
- self.withdraw_info = copy.deepcopy(self.mexc_data['coin_info_map'][self.base_coin][self.NETWORK])
|
|
|
+ self.withdraw_info = copy.deepcopy(self.mexc_data['coin_info_map'][self.coin][self.NETWORK])
|
|
|
self.WITHDRAW_FEE = Decimal(self.withdraw_info['withdrawFee']) # 提現手續費
|
|
|
+ self.WITHDRAW_ENABLE = self.withdraw_info['withdrawEnable'] # 是否启用提现
|
|
|
|
|
|
withdraw_info_formated = pformat(self.withdraw_info, indent=2)
|
|
|
logger.info(f'提現信息識別, 手續費:{self.WITHDRAW_FEE}\n{withdraw_info_formated}')
|
|
|
|
|
|
self.tx = tx
|
|
|
|
|
|
+ self.mexc_price = Decimal(process_item['cexPrice'])
|
|
|
self.profit = Decimal(process_item['profit']) # 這個利潤是實際到手利潤
|
|
|
self.profit_limit = Decimal(process_item['profitLimit']) # 這個利潤是實際到手利潤的limit
|
|
|
|
|
|
@@ -82,30 +85,28 @@ class ArbitrageProcess:
|
|
|
|
|
|
# 存储当前套利交易的细节信息,例如买入数量、价格等
|
|
|
self.sell_price = Decimal(0)
|
|
|
+ self.sell_value = Decimal(0)
|
|
|
self.buy_price = Decimal(0)
|
|
|
- self.chain_tx_hash = None # 链上买入的tx hash
|
|
|
- self.chain_usdt_use = Decimal(process_item['fromTokenAmountHuman']) # 链上usdt减少量(使用量)
|
|
|
- self.chain_buy_amount = Decimal(0) # 链上币增加量(购入量), todo, 暂用即时余额代替
|
|
|
- self.exchange_sell_amount = Decimal(process_item['exchangeOutAmount']) # 交易所卖出量
|
|
|
- self.exchange_sell_order_id = None # 交易所卖出id
|
|
|
- self.exchange_withdrawal_id = None # 交易所提现id
|
|
|
- self.exchange_withdrawal_amount = None # 交易所提现数量
|
|
|
- self.actual_profit = Decimal(0) # 實際利潤
|
|
|
+ self.buy_value = Decimal(0)
|
|
|
+ self.chain_tx_hash = None # 链上卖出的tx hash
|
|
|
+
|
|
|
+ self.exchange_buy_amount = Decimal(process_item['fromTokenAmountHuman']) # 交易所买入量
|
|
|
+ self.exchange_buy_amount = self.exchange_buy_amount + self.WITHDRAW_FEE # 买入量考虑提现手续费
|
|
|
+
|
|
|
+ self.exchange_buy_order_id = None # 交易所买入id
|
|
|
+ self.exchange_withdrawal_id = None # 交易所提现id
|
|
|
+ self.exchange_withdrawal_amount = None # 交易所提现数量
|
|
|
+ self.actual_profit = Decimal(0) # 實際利潤
|
|
|
|
|
|
# 定义可能的状态
|
|
|
self.STATES = [
|
|
|
"CHECK", # 检查余额、估算gas等
|
|
|
- "SELLING_ON_EXCHANGE", # 正在中心化交易所卖出现货
|
|
|
- "WAITING_SELL_CONFIRM", # 等待现货卖出订单确认
|
|
|
- "BUYING_ON_CHAIN", # 正在链上买入
|
|
|
+ "BUYING_ON_EXCHANGE", # 正在中心化交易所买入现货
|
|
|
+ "WAITING_BUY_CONFIRM", # 等待现货买入订单确认
|
|
|
+ "SELLING_ON_CHAIN", # 正在链上卖出
|
|
|
"WAITING_CHAIN_CONFIRM", # 等待链上交易确认
|
|
|
"WAITING_EXCHANGE_ROLLBACK", # 等待交易所回滚
|
|
|
- # "HEDGING_ON_EXCHANGE", # 正在中心化交易所套保
|
|
|
- # "WAITING_HEDGE_CONFIRM", # 等待套保订单确认
|
|
|
- # "TRANSFERRING_TO_EXCHANGE", # 正在向交易所转账
|
|
|
- # "CLOSING_HEDGE", # 正在平掉套保单
|
|
|
- # "WAITING_CLOSE_HEDGE_CONFIRM", # 等待平掉套保单确认
|
|
|
- "WAITING_TRANSFER_ARRIVE", # 等待交易所充值到账
|
|
|
+ # "WAITING_TRANSFER_ARRIVE", # 等待交易所充值到账
|
|
|
"TRANSFERRING_TO_CHAIN", # 正在向链上转账
|
|
|
"WAITING_WITHDRAWAL_CONFIRM", # 等待链上提现确认
|
|
|
"COMPLETED", # 套利流程完成
|
|
|
@@ -115,13 +116,11 @@ class ArbitrageProcess:
|
|
|
|
|
|
self.STATE_IDLE = "IDLE"
|
|
|
self.STATE_CHECK = "CHECK"
|
|
|
- self.STATE_SELLING_ON_EXCHANGE = "SELLING_ON_EXCHANGE"
|
|
|
- self.STATE_WAITING_SELL_CONFIRM = "WAITING_SELL_CONFIRM"
|
|
|
- self.STATE_BUYING_ON_CHAIN = "BUYING_ON_CHAIN"
|
|
|
+ self.STATE_BUYING_ON_EXCHANGE = "BUYING_ON_EXCHANGE"
|
|
|
+ self.STATE_WAITING_BUY_CONFIRM = "WAITING_BUY_CONFIRM"
|
|
|
+ self.STATE_SELLING_ON_CHAIN = "SELLING_ON_CHAIN"
|
|
|
self.STATE_WAITING_CHAIN_CONFIRM = "WAITING_CHAIN_CONFIRM"
|
|
|
self.STATE_WAITING_EXCHANGE_ROLLBACK = "WAITING_EXCHANGE_ROLLBACK"
|
|
|
- # self.STATE_TRANSFERRING_TO_EXCHANGE = "TRANSFERRING_TO_EXCHANGE"
|
|
|
- self.STATE_WAITING_TRANSFER_ARRIVE = "WAITING_TRANSFER_ARRIVE"
|
|
|
self.STATE_TRANSFERRING_TO_CHAIN = "TRANSFERRING_TO_CHAIN"
|
|
|
self.STATE_WAITING_WITHDRAWAL_CONFIRM = "WAITING_WITHDRAWAL_CONFIRM"
|
|
|
self.STATE_COMPLETED = "COMPLETED"
|
|
|
@@ -150,14 +149,14 @@ class ArbitrageProcess:
|
|
|
if self.current_state == self.STATE_CHECK:
|
|
|
self._execute_check()
|
|
|
|
|
|
- elif self.current_state == self.STATE_SELLING_ON_EXCHANGE:
|
|
|
- self._execute_sell_on_exchange()
|
|
|
+ elif self.current_state == self.STATE_BUYING_ON_EXCHANGE:
|
|
|
+ self._execute_buy_on_exchange()
|
|
|
|
|
|
- elif self.current_state == self.STATE_WAITING_SELL_CONFIRM:
|
|
|
- self._wait_sell_confirm()
|
|
|
+ elif self.current_state == self.STATE_WAITING_BUY_CONFIRM:
|
|
|
+ self._wait_buy_confirm()
|
|
|
|
|
|
- elif self.current_state == self.STATE_BUYING_ON_CHAIN:
|
|
|
- self._execute_buy_on_chain()
|
|
|
+ elif self.current_state == self.STATE_SELLING_ON_CHAIN:
|
|
|
+ self._selling_on_chain()
|
|
|
|
|
|
elif self.current_state == self.STATE_WAITING_CHAIN_CONFIRM:
|
|
|
self._wait_chain_confirm()
|
|
|
@@ -165,9 +164,6 @@ class ArbitrageProcess:
|
|
|
elif self.current_state == self.STATE_WAITING_EXCHANGE_ROLLBACK:
|
|
|
self._wait_exchange_rollback()
|
|
|
|
|
|
- # elif self.current_state == "TRANSFERRING_TO_EXCHANGE":
|
|
|
- # self._execute_transfer_to_exchange()
|
|
|
-
|
|
|
elif self.current_state == self.STATE_WAITING_TRANSFER_ARRIVE:
|
|
|
self._wait_transfer_arrive()
|
|
|
|
|
|
@@ -197,28 +193,37 @@ class ArbitrageProcess:
|
|
|
前置检查,防止低能错误
|
|
|
"""
|
|
|
try:
|
|
|
- # step1,檢查交易所的餘額是否夠用
|
|
|
- # 处理精度
|
|
|
- pseudo_amount_to_sell = self.exchange_sell_amount.quantize(Decimal('1'), rounding=ROUND_DOWN)
|
|
|
-
|
|
|
- # 交易所套保余额判断
|
|
|
- with self.mexc_lock:
|
|
|
- balances = self.mexc_data['account_info']['balances']
|
|
|
-
|
|
|
- for balance in balances:
|
|
|
- if balance['asset'] == self.coin:
|
|
|
- if Decimal(balance['free']) < pseudo_amount_to_sell:
|
|
|
- msg = f"交易所剩余{self.coin}: {balance['free']}, 交易所准备卖出:{pseudo_amount_to_sell}, 不能触发套保交易。"
|
|
|
- logger.info(msg)
|
|
|
- add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
- self._set_state(self.STATE_REJECT)
|
|
|
- return
|
|
|
- else:
|
|
|
- msg = f"交易所剩余{self.coin}: {balance['free']}, 交易所准备卖出:{pseudo_amount_to_sell}, 余额校验通过(可以套保)。"
|
|
|
- logger.info(msg)
|
|
|
- add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
-
|
|
|
- break
|
|
|
+ # # step1,檢查交易所的餘額是否夠用
|
|
|
+ # # 处理精度
|
|
|
+ # pseudo_amount_to_buy = self.exchange_buy_amount.quantize(Decimal('1'), rounding=ROUND_DOWN)
|
|
|
+ #
|
|
|
+ # # 交易所套保余额判断
|
|
|
+ # with self.mexc_lock:
|
|
|
+ # balances = self.mexc_data['account_info']['balances']
|
|
|
+
|
|
|
+ # for balance in balances:
|
|
|
+ # if balance['asset'] == self.coin:
|
|
|
+ # if Decimal(balance['free']) < pseudo_amount_to_buy:
|
|
|
+ # msg = f"交易所剩余{self.coin}: {balance['free']}, 交易所准备买入:{pseudo_amount_to_buy}, 不能触发套保交易。"
|
|
|
+ # logger.info(msg)
|
|
|
+ # add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
+ # self._set_state(self.STATE_REJECT)
|
|
|
+ # return
|
|
|
+ # else:
|
|
|
+ # msg = f"交易所剩余{self.coin}: {balance['free']}, 交易所准备买入:{pseudo_amount_to_buy}, 余额校验通过(可以套保)。"
|
|
|
+ # logger.info(msg)
|
|
|
+ # add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
+
|
|
|
+ # break
|
|
|
+
|
|
|
+ # step1,检查该币对是否有提现权限
|
|
|
+ if not self.WITHDRAW_ENABLE:
|
|
|
+ coin_info_formated = pformat(self.withdraw_info, indent=2)
|
|
|
+ msg = f"{self.coin}不允许提现。\n{coin_info_formated}"
|
|
|
+ logger.info(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
+ self._set_state(self.STATE_REJECT)
|
|
|
+ return
|
|
|
|
|
|
# step2,估算gas
|
|
|
logger.info("獲取區塊信息")
|
|
|
@@ -246,7 +251,7 @@ class ArbitrageProcess:
|
|
|
# step3, 費用與利潤比較
|
|
|
estimated_eth_value = estimated_eth * self.eth_price
|
|
|
estimated_eth_value = estimated_eth_value.quantize(Decimal('1e-2'), rounding=ROUND_DOWN)
|
|
|
- cost = estimated_eth_value + self.WITHDRAW_FEE # 成本
|
|
|
+ cost = estimated_eth_value + self.WITHDRAW_FEE * self.mexc_price # 成本
|
|
|
if self.profit < cost:
|
|
|
msg = f"費用判斷不通過! profit: {self.profit}, eth_value:{estimated_eth_value}, eth: {estimated_eth}, eth_price: {self.eth_price}"
|
|
|
logger.info(msg)
|
|
|
@@ -257,8 +262,8 @@ class ArbitrageProcess:
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
|
|
|
- # step4, 與賬戶eth餘額比對(至少留0.001,不然沒gas了)
|
|
|
- MARGIN = Decimal(0.001)
|
|
|
+ # step4, 與賬戶eth餘額比對(至少留0.002,不然沒gas了)
|
|
|
+ MARGIN = Decimal(0.002)
|
|
|
# 暫時鎖住core_data
|
|
|
with self.core_lock:
|
|
|
eth_balance = self.core_data['eth_balance']
|
|
|
@@ -276,7 +281,7 @@ class ArbitrageProcess:
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
|
|
|
# final, 設定交易狀態,開始交易
|
|
|
- self._set_state(self.STATE_SELLING_ON_EXCHANGE)
|
|
|
+ self._set_state(self.STATE_BUYING_ON_EXCHANGE)
|
|
|
except Exception as e:
|
|
|
exc_traceback = traceback.format_exc()
|
|
|
msg = f"前置檢查未通過\n{exc_traceback}"
|
|
|
@@ -288,47 +293,47 @@ class ArbitrageProcess:
|
|
|
# traceback.print_exc()
|
|
|
|
|
|
# 以下是每个状态对应的具体执行函数
|
|
|
- def _execute_sell_on_exchange(self):
|
|
|
+ def _execute_buy_on_exchange(self):
|
|
|
"""
|
|
|
- 在中心化交易所卖出现货
|
|
|
+ 在中心化交易所买入现货
|
|
|
"""
|
|
|
- msg = "执行:中心化交易所卖出现货..."
|
|
|
+ msg = "执行:中心化交易所买入现货..."
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "pending")
|
|
|
try:
|
|
|
- # 第一步直接卖出,这个数量用固定数量
|
|
|
- pseudo_amount_to_sell = self.exchange_sell_amount
|
|
|
+ # 第一步直接买入,这个数量用固定数量
|
|
|
+ pseudo_amount_to_buy = self.exchange_buy_amount
|
|
|
# 处理精度
|
|
|
- pseudo_amount_to_sell = pseudo_amount_to_sell.quantize(Decimal('1'), rounding=ROUND_DOWN)
|
|
|
+ pseudo_amount_to_buy = pseudo_amount_to_buy.quantize(Decimal('1'), rounding=ROUND_DOWN)
|
|
|
|
|
|
order_params = {
|
|
|
"symbol": self.symbol.replace('_', ''),
|
|
|
- "side": "SELL",
|
|
|
+ "side": "BUY",
|
|
|
"type": "MARKET",
|
|
|
- "quantity": int(pseudo_amount_to_sell),
|
|
|
+ "quantity": int(pseudo_amount_to_buy), # 以【幣】的數量進行買入
|
|
|
}
|
|
|
order_params_formated = pformat(order_params, indent=2)
|
|
|
|
|
|
- exchange_sell_order = mexc.trade.post_order(order_params)
|
|
|
- exchange_sell_order_formated = pformat(exchange_sell_order, indent=2)
|
|
|
+ exchange_buy_order = mexc.trade.post_order(order_params)
|
|
|
+ exchange_buy_order_formated = pformat(exchange_buy_order, indent=2)
|
|
|
|
|
|
- msg = f"交易所现货卖出订单已发送 \n params:{order_params_formated} \n rst: {exchange_sell_order_formated}"
|
|
|
- if 'orderId' not in exchange_sell_order:
|
|
|
+ msg = f"交易所现货买入订单已发送 \n params:{order_params_formated} \n rst: {exchange_buy_order_formated}"
|
|
|
+ if 'orderId' not in exchange_buy_order:
|
|
|
logger.error(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
self._set_state(self.STATE_FAILED)
|
|
|
|
|
|
return
|
|
|
|
|
|
- self.exchange_sell_order_id = exchange_sell_order['orderId']
|
|
|
+ self.exchange_buy_order_id = exchange_buy_order['orderId']
|
|
|
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
|
|
|
- self._set_state(self.STATE_WAITING_SELL_CONFIRM)
|
|
|
+ self._set_state(self.STATE_WAITING_BUY_CONFIRM)
|
|
|
except Exception as e:
|
|
|
exc_traceback = traceback.format_exc()
|
|
|
- msg = f"交易所现货卖出下单失败\n{exc_traceback}"
|
|
|
+ msg = f"交易所现货买入下单失败\n{exc_traceback}"
|
|
|
logger.error(msg)
|
|
|
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
@@ -336,12 +341,12 @@ class ArbitrageProcess:
|
|
|
|
|
|
# traceback.print_exc()
|
|
|
|
|
|
- def _wait_sell_confirm(self):
|
|
|
+ def _wait_buy_confirm(self):
|
|
|
"""
|
|
|
- 等待交易所现货卖出订单确认(完全成交)
|
|
|
+ 等待交易所现货买入订单确认(完全成交)
|
|
|
"""
|
|
|
- exchange_sell_order_id = self.exchange_sell_order_id
|
|
|
- msg = f"等待交易所现货卖出订单确认:{exchange_sell_order_id}"
|
|
|
+ exchange_buy_order_id = self.exchange_buy_order_id
|
|
|
+ msg = f"等待交易所现货买入订单确认:{exchange_buy_order_id}"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "pending")
|
|
|
|
|
|
@@ -352,39 +357,43 @@ class ArbitrageProcess:
|
|
|
while waiting_times > 0:
|
|
|
params = {
|
|
|
"symbol": self.symbol.replace('_', ''),
|
|
|
- "orderId": exchange_sell_order_id
|
|
|
+ "orderId": exchange_buy_order_id
|
|
|
}
|
|
|
order = mexc.trade.get_order(params)
|
|
|
last_order = order
|
|
|
|
|
|
if order['status'] in ["FILLED", "PARTIALLY_CANCELED"]:
|
|
|
+ # 以实际成交数量为准
|
|
|
+ self.exchange_buy_amount = Decimal(order['executedQty'])
|
|
|
+
|
|
|
money = Decimal(order['cummulativeQuoteQty'])
|
|
|
- amount = self.exchange_sell_amount
|
|
|
+ amount = self.exchange_buy_amount
|
|
|
|
|
|
- self.sell_price = money / amount
|
|
|
- self.sell_price = self.sell_price.quantize(Decimal('1e-16'), rounding=ROUND_DOWN)
|
|
|
+ self.buy_value = money
|
|
|
+ self.buy_price = money / amount
|
|
|
+ self.buy_price = self.buy_price.quantize(Decimal('1e-16'), rounding=ROUND_DOWN)
|
|
|
|
|
|
order_formated = pformat(order, indent=2)
|
|
|
- msg = f"交易所现货卖出订单已完成, 价格:{self.sell_price}, money: {money}\n order: {order_formated}"
|
|
|
+ msg = f"交易所现货买入订单已完成, 价格:{self.buy_price}, money: {money}\n order: {order_formated}"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
|
|
|
- self.exchange_withdrawal_amount = money
|
|
|
+ self.exchange_withdrawal_amount = amount
|
|
|
|
|
|
- self._set_state(self.STATE_BUYING_ON_CHAIN)
|
|
|
+ self._set_state(self.STATE_SELLING_ON_CHAIN)
|
|
|
return
|
|
|
else:
|
|
|
time.sleep(1)
|
|
|
waiting_times = waiting_times - 1
|
|
|
|
|
|
last_order_formated = pformat(last_order, indent=2)
|
|
|
- msg = f"交易所现货卖出订单失敗, 最後狀態:\n{last_order_formated}。"
|
|
|
+ msg = f"交易所现货买入订单失敗, 最後狀態:\n{last_order_formated}。"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
self._set_state(self.STATE_FAILED)
|
|
|
except Exception as e:
|
|
|
exc_traceback = traceback.format_exc()
|
|
|
- msg = f"查询交易所现货卖出订单状态时发生错误\n{exc_traceback}"
|
|
|
+ msg = f"查询交易所现货买入订单状态时发生错误\n{exc_traceback}"
|
|
|
logger.error(msg)
|
|
|
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
@@ -392,11 +401,11 @@ class ArbitrageProcess:
|
|
|
|
|
|
# traceback.print_exc()
|
|
|
|
|
|
- def _execute_buy_on_chain(self):
|
|
|
+ def _selling_on_chain(self):
|
|
|
"""
|
|
|
- 在链上执行买入操作
|
|
|
+ 在链上执行卖出操作
|
|
|
"""
|
|
|
- msg = "执行:链上买入操作..."
|
|
|
+ msg = "执行:链上卖出操作..."
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "pending")
|
|
|
try:
|
|
|
@@ -404,13 +413,16 @@ class ArbitrageProcess:
|
|
|
with self.core_lock:
|
|
|
self.tx['nonce'] = self.core_data['nonce']
|
|
|
|
|
|
- # 调用链上客户端执行买入交易
|
|
|
+ # 调用链上客户端执行卖出交易
|
|
|
signed_tx = web3._sign(self.tx, self.gas_limit_multiplier)
|
|
|
self.chain_tx_hash = web3.w3.to_hex(signed_tx.hash)
|
|
|
try:
|
|
|
+ # 主要节点先发交易
|
|
|
web3.w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
|
|
+ # 使用备用节点再发一次交易
|
|
|
+ web3_backup.w3.eth.send_raw_transaction(signed_tx.raw_transaction)
|
|
|
except Exception as e:
|
|
|
- msg = f"据反饋說链上买入失败:{e}, 交易哈希:{self.chain_tx_hash}"
|
|
|
+ msg = f"据反饋說链上卖出失败:{e}, 交易哈希:{self.chain_tx_hash}"
|
|
|
logger.error(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
|
|
|
@@ -452,8 +464,8 @@ class ArbitrageProcess:
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "pending")
|
|
|
try:
|
|
|
- # 給300秒時間進行確認
|
|
|
- waiting_times = 300
|
|
|
+ # 給120秒時間進行確認
|
|
|
+ waiting_times = 120
|
|
|
while waiting_times > 0:
|
|
|
with self.pending_lock:
|
|
|
tx_details = copy.deepcopy(self.pending_data[chain_tx_hash]['tx_details'])
|
|
|
@@ -491,87 +503,24 @@ class ArbitrageProcess:
|
|
|
from_token_amount = Decimal(from_token_details['amount'])
|
|
|
from_token_amount_human = from_token_amount / (Decimal(10) ** self.from_token_decimal)
|
|
|
from_token_amount_human = from_token_amount_human.quantize(Decimal('1e-2'), rounding=ROUND_DOWN)
|
|
|
- self.chain_buy_amount = from_token_amount_human # 存储实际买入数量
|
|
|
|
|
|
to_token_amount = Decimal(to_token_details['amount'])
|
|
|
to_token_amount_human = to_token_amount / (Decimal(10) ** self.to_token_decimal)
|
|
|
|
|
|
- self.buy_price = from_token_amount_human / to_token_amount_human
|
|
|
- self.buy_price = self.buy_price.quantize(Decimal('1e-16'), rounding=ROUND_DOWN)
|
|
|
+ self.sell_value = to_token_amount_human
|
|
|
+ self.sell_price = from_token_amount_human / to_token_amount_human
|
|
|
+ self.sell_price = self.sell_price.quantize(Decimal('1e-16'), rounding=ROUND_DOWN)
|
|
|
|
|
|
# 交易預估利潤百分比計算
|
|
|
rate = self.sell_price / self.buy_price
|
|
|
rate = rate.quantize(Decimal('1e-4'), rounding=ROUND_DOWN)
|
|
|
|
|
|
- msg = f"【比率{rate}】。用{from_token_amount_human}买入{to_token_amount_human},价格{self.buy_price}。"
|
|
|
+ msg = f"【比率{rate}】。用{from_token_amount_human}卖出{to_token_amount_human},价格{self.sell_price}。"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
|
|
|
- # 判斷快速二賣條件
|
|
|
- diff = int(to_token_amount_human - self.exchange_sell_amount)
|
|
|
- value = diff * self.sell_price
|
|
|
- value = value.quantize(Decimal('1e-4'), rounding=ROUND_DOWN)
|
|
|
- if value > 2:
|
|
|
- msg = f"滿足二賣條件,{diff}*{self.sell_price} = {value}"
|
|
|
- logger.info(msg)
|
|
|
- add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
-
|
|
|
- order_params = {
|
|
|
- "symbol": self.symbol.replace('_', ''),
|
|
|
- "side": "SELL",
|
|
|
- "type": "MARKET",
|
|
|
- "quantity": int(diff),
|
|
|
- }
|
|
|
- order_params_formated = pformat(order_params, indent=2)
|
|
|
- exchange_sell_order = mexc.trade.post_order(order_params)
|
|
|
- exchange_sell_order_formated = pformat(exchange_sell_order, indent=2)
|
|
|
-
|
|
|
- if 'orderId' not in exchange_sell_order:
|
|
|
- msg = f"交易所现货二卖下单失败 \n params:{order_params_formated} \n rst: {exchange_sell_order_formated}"
|
|
|
- logger.error(msg)
|
|
|
- add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
- else:
|
|
|
- oid = exchange_sell_order['orderId']
|
|
|
-
|
|
|
- # 查询交易所订单状态
|
|
|
- waiting_times_inner = 30
|
|
|
- last_order = None
|
|
|
- while waiting_times_inner > 0:
|
|
|
- params = {
|
|
|
- "symbol": self.symbol.replace('_', ''),
|
|
|
- "orderId": oid
|
|
|
- }
|
|
|
- order = mexc.trade.get_order(params)
|
|
|
- order_formated = pformat(order, indent=2)
|
|
|
- last_order = order
|
|
|
-
|
|
|
- if order['status'] in ["FILLED", "PARTIALLY_CANCELED"]:
|
|
|
- money = Decimal(order['cummulativeQuoteQty'])
|
|
|
-
|
|
|
- msg = f"交易所现货二卖订单已完成, money: {money}。\n order: {order_formated}"
|
|
|
- logger.info(msg)
|
|
|
- add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
-
|
|
|
- self.exchange_withdrawal_amount = self.exchange_withdrawal_amount + money
|
|
|
-
|
|
|
- break
|
|
|
- else:
|
|
|
- time.sleep(1)
|
|
|
- waiting_times_inner = waiting_times_inner - 1
|
|
|
-
|
|
|
- if waiting_times_inner <= 0:
|
|
|
- last_order_formated = pformat(last_order, indent=2)
|
|
|
-
|
|
|
- msg = f"交易所现货二卖订单失敗, 最後狀態:{last_order_formated}。"
|
|
|
- logger.info(msg)
|
|
|
- add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
- else:
|
|
|
- msg = f"不滿足二賣條件,{diff}*{self.sell_price} = {value}"
|
|
|
- logger.info(msg)
|
|
|
- add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
-
|
|
|
# 計算實際利潤
|
|
|
- actual_profit = value
|
|
|
+ value = self.sell_value - self.buy_value
|
|
|
actual_gas_price = Decimal(tx_details['gasPrice'])
|
|
|
actual_gas_price_gwei = actual_gas_price / Decimal('1e9')
|
|
|
actual_gas_price_gwei = actual_gas_price_gwei.quantize(Decimal('1e-9'), rounding=ROUND_DOWN)
|
|
|
@@ -582,7 +531,7 @@ class ArbitrageProcess:
|
|
|
actual_fee_used = actual_eth * self.eth_price
|
|
|
actual_fee_used = actual_fee_used.quantize(Decimal('1e-4'), rounding=ROUND_DOWN)
|
|
|
|
|
|
- actual_profit = value - actual_fee_used - self.WITHDRAW_FEE
|
|
|
+ actual_profit = value - actual_fee_used - self.WITHDRAW_FEE * self.mexc_price
|
|
|
|
|
|
msg = f"【最終利潤】{actual_profit}{self.base_coin}(已扣除所有手續費、滑點)\
|
|
|
\n鏈上ETH使用: {actual_eth}({actual_fee_used} USD), gas_price: {actual_gas_price_gwei} GWEI, gas_used: {actual_gas_used}\
|
|
|
@@ -591,11 +540,11 @@ class ArbitrageProcess:
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
|
|
|
- self._set_state(self.STATE_COMPLETED)
|
|
|
+ self._set_state(self.STATE_TRANSFERRING_TO_CHAIN)
|
|
|
|
|
|
break
|
|
|
|
|
|
- # 如果300秒都沒確認成功,該交易大概率沒有上鏈
|
|
|
+ # 如果120秒都沒確認成功,該交易大概率沒有上鏈
|
|
|
if waiting_times <= 0:
|
|
|
with self.pending_lock:
|
|
|
response = copy.deepcopy(self.pending_data[chain_tx_hash]['response'])
|
|
|
@@ -618,54 +567,54 @@ class ArbitrageProcess:
|
|
|
"""
|
|
|
市价进行交易所交易回滚
|
|
|
"""
|
|
|
- msg = "执行:中心化交易所买入现货回滚..."
|
|
|
+ msg = "执行:中心化交易所卖出现货回滚..."
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "pending")
|
|
|
try:
|
|
|
# 使用预提现数量进行回滚
|
|
|
- pseudo_amount_to_buy = Decimal(self.exchange_withdrawal_amount)
|
|
|
+ pseudo_amount_to_sell = Decimal(self.exchange_buy_amount)
|
|
|
# 处理精度
|
|
|
- pseudo_amount_to_buy = pseudo_amount_to_buy.quantize(Decimal('1'), rounding=ROUND_DOWN)
|
|
|
+ pseudo_amount_to_sell = pseudo_amount_to_sell.quantize(Decimal('1'), rounding=ROUND_DOWN)
|
|
|
|
|
|
- # 交易所U余额判断
|
|
|
+ # 交易所币余额判断
|
|
|
with self.mexc_lock:
|
|
|
balances = self.mexc_data['account_info']['balances']
|
|
|
for balance in balances:
|
|
|
- if balance['asset'] == self.base_coin:
|
|
|
- pseudo_amount_to_buy = min(Decimal(balance['free']), pseudo_amount_to_buy)
|
|
|
+ if balance['asset'] == self.coin:
|
|
|
+ pseudo_amount_to_sell = min(Decimal(balance['free']), pseudo_amount_to_sell)
|
|
|
|
|
|
- if pseudo_amount_to_buy < Decimal('10'):
|
|
|
- msg = f"交易所剩余{self.base_coin}: {balance['free']}, 小于10, 不能触发回滚交易。"
|
|
|
+ if pseudo_amount_to_sell * self.mexc_price < Decimal('10'):
|
|
|
+ msg = f"交易所剩余{self.coin}: {balance['free']}, 小于10, 不能触发回滚交易。"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
self._set_state(self.STATE_FAILED)
|
|
|
return
|
|
|
else:
|
|
|
- msg = f"交易所剩余{self.base_coin}: {balance['free']}, 交易所准备使用:{pseudo_amount_to_buy}, 余额校验通过(可以回滚)。"
|
|
|
+ msg = f"交易所剩余{self.coin}: {balance['free']}, 交易所准备使用:{pseudo_amount_to_sell}, 余额校验通过(可以回滚)。"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
break
|
|
|
|
|
|
order_params = {
|
|
|
"symbol": self.symbol.replace('_', ''),
|
|
|
- "side": "BUY",
|
|
|
+ "side": "SELL",
|
|
|
"type": "MARKET",
|
|
|
- "quoteOrderQty": int(pseudo_amount_to_buy),
|
|
|
+ "quantity": int(pseudo_amount_to_sell),
|
|
|
}
|
|
|
order_params_formated = pformat(order_params, indent=2)
|
|
|
- exchange_buy_order = mexc.trade.post_order(order_params)
|
|
|
- exchange_buy_order_formated = pformat(exchange_buy_order, indent=2)
|
|
|
- if 'orderId' not in exchange_buy_order:
|
|
|
- msg = f"【回滚】交易所现货买入下单失败\n params:{order_params_formated}\norder: {exchange_buy_order_formated}"
|
|
|
+ exchange_sell_order = mexc.trade.post_order(order_params)
|
|
|
+ exchange_sell_order_formated = pformat(exchange_sell_order, indent=2)
|
|
|
+ if 'orderId' not in exchange_sell_order:
|
|
|
+ msg = f"【回滚】交易所现货卖出下单失败\n params:{order_params_formated}\norder: {exchange_sell_order_formated}"
|
|
|
logger.error(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
self._set_state("FAILED")
|
|
|
|
|
|
return
|
|
|
|
|
|
- exchange_buy_order_id = exchange_buy_order['orderId']
|
|
|
+ exchange_sell_order_id = exchange_sell_order['orderId']
|
|
|
|
|
|
- msg = f"【回滚】交易所现货买入订单已发送, 订单ID: {exchange_buy_order_id}"
|
|
|
+ msg = f"【回滚】交易所现货卖出订单已发送, 订单ID: {exchange_sell_order_id}"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
|
|
|
@@ -675,7 +624,7 @@ class ArbitrageProcess:
|
|
|
while True:
|
|
|
params = {
|
|
|
"symbol": self.symbol.replace('_', ''),
|
|
|
- "orderId": exchange_buy_order_id
|
|
|
+ "orderId": exchange_sell_order_id
|
|
|
}
|
|
|
order = mexc.trade.get_order(params)
|
|
|
order_formated = pformat(order, indent=2)
|
|
|
@@ -683,11 +632,11 @@ class ArbitrageProcess:
|
|
|
|
|
|
if order['status'] == "FILLED":
|
|
|
money = Decimal(order['cummulativeQuoteQty'])
|
|
|
- amount = self.exchange_sell_amount
|
|
|
+ amount = self.exchange_buy_amount
|
|
|
price = money / amount
|
|
|
price = price.quantize(Decimal('1e-8'), rounding=ROUND_DOWN)
|
|
|
|
|
|
- msg = f"【回滚】交易所现货买入订单已完全成交, 价格:{price}。\norder: {order_formated}"
|
|
|
+ msg = f"【回滚】交易所现货卖出订单已完全成交, 价格:{price}。\norder: {order_formated}"
|
|
|
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
@@ -702,7 +651,7 @@ class ArbitrageProcess:
|
|
|
waiting_times = waiting_times - 1
|
|
|
|
|
|
last_query_rst_formated = pformat(last_query_rst, indent=2)
|
|
|
- msg = f"【回滚】回滚交易订单查询超时, 订单ID: {exchange_buy_order_id}\n最终状态:{last_query_rst_formated}"
|
|
|
+ msg = f"【回滚】回滚交易订单查询超时, 订单ID: {exchange_sell_order_id}\n最终状态:{last_query_rst_formated}"
|
|
|
logger.info(msg)
|
|
|
add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
|
|
|
@@ -717,73 +666,106 @@ class ArbitrageProcess:
|
|
|
|
|
|
# traceback.print_exc()
|
|
|
|
|
|
-# 伪代码示例:如何使用这个类
|
|
|
-if __name__ == "__main__":
|
|
|
- import ok_chain_client
|
|
|
- import decimal
|
|
|
- import pprint
|
|
|
-
|
|
|
- CHAIN_ID = 1
|
|
|
- FROM_TOKEN = '0xdAC17F958D2ee523a2206206994597C13D831ec7'
|
|
|
- FROM_TOKEN_AMOUNT_HUMAM = Decimal('20')
|
|
|
- FROM_TOKEN_DECIMAL = 6
|
|
|
- TO_TOKEN = '0xf816507E690f5Aa4E29d164885EB5fa7a5627860'
|
|
|
- USER_WALLET = ''
|
|
|
- USER_EXCHANGE_WALLET = '0xc71835a042F4d870B0F4296cc89cAeb921a9f3DA'
|
|
|
- SYMBOL = "RATO_USDT"
|
|
|
-
|
|
|
- # 询价,注意!!!这里直接把交易所地址当收款方,省去transfer的流程
|
|
|
- data = ok_chain_client.swap(CHAIN_ID,
|
|
|
- FROM_TOKEN_AMOUNT_HUMAM * (10 ** FROM_TOKEN_DECIMAL),
|
|
|
- FROM_TOKEN,
|
|
|
- TO_TOKEN,
|
|
|
- 1,
|
|
|
- USER_WALLET,
|
|
|
- USER_EXCHANGE_WALLET, # 这里直接把交易所地址当收款方,省去transfer的流程!!!
|
|
|
- )
|
|
|
- if data.get('code') != '0' or not data.get('data'):
|
|
|
- pprint.pprint(data)
|
|
|
- pprint.pprint({
|
|
|
- "error": f"OK API错误({1}) - Code:{data.get('code', 'N/A')}, Msg:{data.get('msg', data.get('message', 'N/A')) if isinstance(data, dict) else '格式错误'}"})
|
|
|
-
|
|
|
- raise Exception("")
|
|
|
- d = data['data'][0]
|
|
|
- tx = d['tx']
|
|
|
- router_result = d['routerResult']
|
|
|
- in_dec, out_dec = int(router_result['fromToken']['decimal']), int(router_result['toToken']['decimal'])
|
|
|
- atomic_in_base, atomic_out_target = Decimal(router_result['fromTokenAmount']), Decimal(router_result['toTokenAmount'])
|
|
|
- human_in_base = atomic_in_base / (10 ** in_dec)
|
|
|
- human_out_target = atomic_out_target / (10 ** out_dec)
|
|
|
-
|
|
|
- FROM_TOKEN_AMOUNT_HUMAM = human_in_base
|
|
|
- TO_TOKEN_AMOUNT_HUMAM = human_out_target
|
|
|
-
|
|
|
- pprint.pprint(tx)
|
|
|
-
|
|
|
- # 套利流程执行
|
|
|
- process_item = {
|
|
|
- "stateFlow": [], # 状态流转记录
|
|
|
- }
|
|
|
- ap = ArbitrageProcess(tx, 2, 1.2,
|
|
|
- FROM_TOKEN, TO_TOKEN,
|
|
|
- FROM_TOKEN_AMOUNT_HUMAM, TO_TOKEN_AMOUNT_HUMAM,
|
|
|
- USER_EXCHANGE_WALLET, USER_WALLET,
|
|
|
- SYMBOL, process_item)
|
|
|
-
|
|
|
- # 一般都是从这个流程开始,测试时可以稍作修改、测试后续流程
|
|
|
- ap._set_state(ap.SELLING_ON_EXCHANGE)
|
|
|
-
|
|
|
- # 在主循环中周期性调用 run_arbitrage_step
|
|
|
- while ap.current_state != "COMPLETED" and ap.current_state != "FAILED":
|
|
|
- ap.run_arbitrage_step()
|
|
|
-
|
|
|
- if ap.current_state == ap.STATE_WAITING_TRANSFER_ARRIVE or ap.current_state == ap.STATE_WAITING_WITHDRAWAL_CONFIRM:
|
|
|
- time.sleep(10)
|
|
|
- # else:
|
|
|
- # time.sleep(1)
|
|
|
-
|
|
|
- logger.info(process_item)
|
|
|
- if ap.current_state == "COMPLETED":
|
|
|
- logger.info("套利流程执行成功!")
|
|
|
- else:
|
|
|
- logger.info("套利流程执行失败!")
|
|
|
+ def _execute_transfer_to_chain(self):
|
|
|
+ """
|
|
|
+ 将交易后获得的计价资产(例如USDT)转账回链上
|
|
|
+ """
|
|
|
+ msg = "执行:交易所计价资产转账回链上..."
|
|
|
+ logger.info(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "pending")
|
|
|
+
|
|
|
+ try:
|
|
|
+ pseudo_withdrawal_amount = str(int(float(self.exchange_buy_amount)))
|
|
|
+
|
|
|
+ withdrawal_params = {
|
|
|
+ 'coin': self.coin,
|
|
|
+ 'netWork': 'ETH',
|
|
|
+ 'address': self.user_wallet,
|
|
|
+ 'amount': pseudo_withdrawal_amount
|
|
|
+ }
|
|
|
+ withdrawal_params_formated = pformat(withdrawal_params, indent=2)
|
|
|
+ withdrawal_rst = mexc.wallet.post_withdraw(withdrawal_params)
|
|
|
+ withdrawal_rst_formated = pformat(withdrawal_rst, indent=2)
|
|
|
+ if "id" not in withdrawal_rst:
|
|
|
+ msg = f"交易所提现失败\n參數: {withdrawal_params_formated}\n響應: {withdrawal_rst_formated}"
|
|
|
+ logger.error(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
+
|
|
|
+ self._set_state(self.STATE_FAILED)
|
|
|
+ else:
|
|
|
+ self.exchange_withdrawal_id = withdrawal_rst["id"]
|
|
|
+ msg = f"交易所提现已发送\n{withdrawal_rst_formated}"
|
|
|
+ logger.info(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
+
|
|
|
+ self._set_state(self.STATE_WAITING_WITHDRAWAL_CONFIRM)
|
|
|
+ except Exception as e:
|
|
|
+ msg = f"转账回链上失败: {e}"
|
|
|
+ logger.error(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
+
|
|
|
+ self._set_state(self.STATE_FAILED)
|
|
|
+
|
|
|
+ traceback.print_exc()
|
|
|
+
|
|
|
+ def _wait_withdrawal_confirm(self):
|
|
|
+ """
|
|
|
+ 等待交易所提现到链上确认
|
|
|
+ """
|
|
|
+ exchange_withdrawal_id = self.exchange_withdrawal_id
|
|
|
+
|
|
|
+ msg = f"等待交易所提现确认:{exchange_withdrawal_id}"
|
|
|
+ logger.info(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "pending")
|
|
|
+ try:
|
|
|
+ is_arrived = False
|
|
|
+
|
|
|
+ # 最多等待30分钟
|
|
|
+ waiting_times = 60
|
|
|
+ last_deposit_state = None
|
|
|
+ last_deposit_state_formated = None
|
|
|
+ while waiting_times > 0:
|
|
|
+ with self.mexc_lock:
|
|
|
+ withdraw_list = copy.deepcopy(self.mexc_data['withdraw_list'])
|
|
|
+
|
|
|
+ if not isinstance(withdraw_list, list):
|
|
|
+ msg = f"查询交易所提现状态时发生错误:{withdraw_list}"
|
|
|
+ logger.error(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
+
|
|
|
+ self._set_state("FAILED")
|
|
|
+ return
|
|
|
+
|
|
|
+ for withdrawal in withdraw_list:
|
|
|
+ if withdrawal['id'] != exchange_withdrawal_id:
|
|
|
+ continue
|
|
|
+
|
|
|
+ last_deposit_state = withdrawal
|
|
|
+ last_deposit_state_formated = pformat(last_deposit_state, indent=2)
|
|
|
+
|
|
|
+ if withdrawal['status'] == 7:
|
|
|
+ is_arrived = True
|
|
|
+
|
|
|
+ if is_arrived:
|
|
|
+ msg = f"提现请求已上链:\n{last_deposit_state_formated}"
|
|
|
+ logger.info(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "success")
|
|
|
+
|
|
|
+ self._set_state(self.STATE_COMPLETED)
|
|
|
+ return
|
|
|
+
|
|
|
+ time.sleep(30)
|
|
|
+ waiting_times = waiting_times - 1
|
|
|
+
|
|
|
+ msg = f"等待提现到账超时(超过30分钟):\n{last_deposit_state_formated}"
|
|
|
+ logger.error(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
+
|
|
|
+ self._set_state(self.STATE_FAILED)
|
|
|
+ except Exception as e:
|
|
|
+ msg = f"查询交易所提现状态时发生错误:{e}"
|
|
|
+ logger.error(msg)
|
|
|
+ add_state_flow_entry(self.process_item, self.current_state, msg, "fail")
|
|
|
+ self._set_state(self.STATE_FAILED)
|
|
|
+
|
|
|
+ traceback.print_exc()
|