feat: fix asset swap logic for Unified Trading Accounts (v1.5.6)
This commit is contained in:
@ -86,10 +86,11 @@ class DatabaseManager:
|
||||
|
||||
class PingPongBot:
|
||||
def __init__(self, config_path="config/ping_pong_config.yaml"):
|
||||
self.version = "1.5.5"
|
||||
self.version = "1.5.6"
|
||||
with open(config_path, 'r') as f:
|
||||
self.config = yaml.safe_load(f)
|
||||
|
||||
# Explicitly load from ENV to ensure they are available
|
||||
self.api_key = os.getenv("BYBIT_API_KEY") or os.getenv("API_KEY")
|
||||
self.api_secret = os.getenv("BYBIT_API_SECRET") or os.getenv("API_SECRET")
|
||||
|
||||
@ -103,6 +104,7 @@ class PingPongBot:
|
||||
timeout=10
|
||||
)
|
||||
|
||||
# Initialize DB with explicit credentials
|
||||
self.db = DatabaseManager(
|
||||
host=os.getenv('DB_HOST', '20.20.20.20'),
|
||||
port=os.getenv('DB_PORT', 5433),
|
||||
@ -111,6 +113,7 @@ class PingPongBot:
|
||||
password=os.getenv('DB_PASSWORD', '')
|
||||
)
|
||||
|
||||
# Base settings
|
||||
raw_symbol = self.config['symbol'].upper()
|
||||
self.base_coin = raw_symbol.replace("USDT", "").replace("USDC", "").replace("USD", "")
|
||||
self.db_symbol = self.base_coin
|
||||
@ -143,6 +146,7 @@ class PingPongBot:
|
||||
self.start_time = datetime.now()
|
||||
self.console = Console()
|
||||
|
||||
# Fixed Parameters from Config
|
||||
self.partial_exit_pct = float(self.config.get('partial_exit_pct', 0.15))
|
||||
self.min_val_usd = float(self.config.get('min_position_value_usd', 15.0))
|
||||
self.pos_size_margin = float(self.config.get('pos_size_margin', 20.0))
|
||||
@ -154,12 +158,14 @@ class PingPongBot:
|
||||
return series.ewm(alpha=alpha, adjust=False).mean()
|
||||
|
||||
def calculate_indicators(self, df):
|
||||
# RSI
|
||||
rsi_cfg = self.config['rsi']
|
||||
delta = df['close'].diff()
|
||||
gain = delta.where(delta > 0, 0)
|
||||
loss = -delta.where(delta < 0, 0)
|
||||
df['rsi'] = 100 - (100 / (1 + (self.rma(gain, rsi_cfg['period']) / self.rma(loss, rsi_cfg['period']))))
|
||||
|
||||
# Hurst
|
||||
hurst_cfg = self.config['hurst']
|
||||
mcl = hurst_cfg['period'] / 2
|
||||
mcl_2 = int(round(mcl / 2))
|
||||
@ -179,6 +185,7 @@ class PingPongBot:
|
||||
return df
|
||||
|
||||
async def update_direction(self):
|
||||
"""Logic Point I: 1D MA44 check and Point II: Asset/Perp selection"""
|
||||
try:
|
||||
logger.info(f"Checking direction based on SMA(44, 1D) for {self.db_symbol}...")
|
||||
candles_1d = await self.db.get_candles(self.db_symbol, "1d", limit=100)
|
||||
@ -206,7 +213,9 @@ class PingPongBot:
|
||||
if self.direction is not None:
|
||||
await self.close_all_positions()
|
||||
|
||||
await self.swap_assets(new_direction)
|
||||
# Update settings before swap to ensure we trade the right symbol/category if needed
|
||||
old_symbol = self.symbol
|
||||
old_category = self.category
|
||||
|
||||
self.direction = new_direction
|
||||
if self.direction == "long":
|
||||
@ -215,10 +224,12 @@ class PingPongBot:
|
||||
self.settle_coin = self.base_coin
|
||||
else:
|
||||
self.category = "linear"
|
||||
# BTCPERP is the Bybit symbol for USDC Linear Perpetual (BTCUSDC display name)
|
||||
self.symbol = "BTCPERP" if self.base_coin == "BTC" else f"{self.base_coin}USDC"
|
||||
self.settle_coin = "USDC"
|
||||
|
||||
# Perform swap
|
||||
await self.swap_assets(new_direction)
|
||||
|
||||
logger.info(f"Bot configured for {self.direction.upper()} | Symbol: {self.symbol} | Category: {self.category}")
|
||||
self.last_candle_time = None
|
||||
return True
|
||||
@ -242,30 +253,38 @@ class PingPongBot:
|
||||
logger.error(f"Error closing positions: {e}")
|
||||
|
||||
async def swap_assets(self, target_direction):
|
||||
"""Point II: Exchange BTC/USDC on Spot market using UNIFIED account type"""
|
||||
try:
|
||||
logger.info(f"Swapping assets for {target_direction.upper()} mode...")
|
||||
spot_symbol = f"{self.base_coin}USDC"
|
||||
|
||||
balance = await asyncio.to_thread(self.session.get_wallet_balance, category="spot", coin=f"{self.base_coin},USDC")
|
||||
# Use accountType='UNIFIED' for UTA accounts
|
||||
balance = await asyncio.to_thread(self.session.get_wallet_balance, accountType="UNIFIED", coin=f"{self.base_coin},USDC")
|
||||
|
||||
coins = {c['coin']: float(c['walletBalance']) for c in balance['result']['list'][0]['coin']}
|
||||
logger.info(f"Current Balances: {coins}")
|
||||
|
||||
if target_direction == "short":
|
||||
# SHORT: Need USDC, Sell BTC
|
||||
btc_bal = coins.get(self.base_coin, 0)
|
||||
if btc_bal > 0.0001:
|
||||
logger.info(f"Spot: Selling {btc_bal} {self.base_coin} for USDC")
|
||||
await asyncio.to_thread(self.session.place_order,
|
||||
res = await asyncio.to_thread(self.session.place_order,
|
||||
category="spot", symbol=spot_symbol, side="Sell", orderType="Market", qty=str(btc_bal)
|
||||
)
|
||||
logger.info(f"Swap Result: {res['retMsg']}")
|
||||
else:
|
||||
# LONG: Need BTC, Buy BTC with USDC
|
||||
usdc_bal = coins.get("USDC", 0)
|
||||
if usdc_bal > 1.0:
|
||||
logger.info(f"Spot: Buying {self.base_coin} with {usdc_bal} USDC")
|
||||
await asyncio.to_thread(self.session.place_order,
|
||||
res = await asyncio.to_thread(self.session.place_order,
|
||||
category="spot", symbol=spot_symbol, side="Buy", orderType="Market",
|
||||
qty=str(usdc_bal), marketUnit="quote"
|
||||
)
|
||||
logger.info(f"Swap Result: {res['retMsg']}")
|
||||
|
||||
await asyncio.sleep(2)
|
||||
await asyncio.sleep(3) # Wait for spot settlement
|
||||
except Exception as e:
|
||||
logger.error(f"Asset Swap Error: {e}")
|
||||
|
||||
@ -281,8 +300,7 @@ class PingPongBot:
|
||||
active = [p for p in pos['result']['list'] if float(p.get('size', 0)) > 0]
|
||||
self.position = active[0] if active else None
|
||||
|
||||
target_coin = self.settle_coin
|
||||
wallet = await asyncio.to_thread(self.session.get_wallet_balance, category=self.category, accountType="UNIFIED", coin=target_coin)
|
||||
wallet = await asyncio.to_thread(self.session.get_wallet_balance, category=self.category, accountType="UNIFIED", coin=self.settle_coin)
|
||||
if wallet['retCode'] == 0:
|
||||
res_list = wallet['result']['list']
|
||||
if res_list:
|
||||
|
||||
Reference in New Issue
Block a user