327 lines
12 KiB
Python
327 lines
12 KiB
Python
import time
|
||
import os
|
||
import asyncio
|
||
import requests
|
||
from datetime import datetime
|
||
from flextrade.flextrade_client import Client
|
||
from flextrade.constants.markets import (
|
||
BASE_MARKET_ETH_USD, BASE_MARKET_BTC_USD, BASE_MARKET_BNB_USD,
|
||
BASE_MARKET_SHIB_USD, BASE_MARKET_PEPE_USD, BASE_MARKET_SUI_USD,
|
||
BASE_MARKET_DOGE_USD, BASE_MARKET_AAVE_USD, BASE_MARKET_HBAR_USD,
|
||
BASE_MARKET_VIRTUAL_USD, BASE_MARKET_ADA_USD, BASE_MARKET_PENDLE_USD,
|
||
BASE_MARKET_TRX_USD, BASE_MARKET_AVAX_USD, BASE_MARKET_UNI_USD,
|
||
BASE_MARKET_SOL_USD, BASE_MARKET_LINK_USD, BASE_MARKET_XRP_USD,
|
||
BASE_MARKET_TON_USD
|
||
)
|
||
from dotenv import load_dotenv
|
||
|
||
load_dotenv()
|
||
|
||
RPC_URL = os.getenv("RPC_URL")
|
||
PRIVATE_KEY = os.getenv("PRIVATE_KEY")
|
||
|
||
# List of all markets to test
|
||
MARKETS = [
|
||
# BASE_MARKET_ETH_USD,
|
||
# BASE_MARKET_BTC_USD,
|
||
# BASE_MARKET_BNB_USD,
|
||
# BASE_MARKET_SHIB_USD,
|
||
# BASE_MARKET_PEPE_USD,
|
||
# BASE_MARKET_SUI_USD,
|
||
# BASE_MARKET_DOGE_USD,
|
||
# BASE_MARKET_AAVE_USD,
|
||
# BASE_MARKET_HBAR_USD,
|
||
# BASE_MARKET_VIRTUAL_USD,
|
||
# BASE_MARKET_ADA_USD,
|
||
# BASE_MARKET_PENDLE_USD,
|
||
# BASE_MARKET_TRX_USD,
|
||
# BASE_MARKET_AVAX_USD,
|
||
# BASE_MARKET_UNI_USD,
|
||
BASE_MARKET_SOL_USD,
|
||
# BASE_MARKET_LINK_USD,
|
||
# BASE_MARKET_XRP_USD,
|
||
# BASE_MARKET_TON_USD
|
||
]
|
||
|
||
# Global variable to store latest prices with timestamps
|
||
latest_prices = {}
|
||
|
||
# Binance API configuration
|
||
BINANCE_API_BASE_URL = "https://api.binance.com"
|
||
|
||
# Mapping FlexTrade markets to Binance symbols
|
||
FLEXTRADE_TO_BINANCE_MAPPING = {
|
||
"ETHUSD": "ETHUSDC",
|
||
"BTCUSD": "BTCUSDC",
|
||
"BNBUSD": "BNBUSDC",
|
||
"SOLUSD": "SOLUSDC",
|
||
"DOGEUSD": "DOGEUSDC",
|
||
"AAVEUSD": "AAVEUSDC",
|
||
"ADAUSD": "ADAUSDC",
|
||
"TRXUSD": "TRXUSDC",
|
||
"AVAXUSD": "AVAXUSDC",
|
||
"UNIUSD": "UNIUSDC",
|
||
"LINKUSD": "LINKUSDC",
|
||
"XRPUSD": "XRPUSDC"
|
||
}
|
||
|
||
def print_market_info(market_info, debug=False):
|
||
if debug:
|
||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||
print('[{0}] {1} : {2:.4f}'.format(timestamp, market_info["market"], market_info["price"]))
|
||
|
||
def get_binance_price(symbol, debug=False):
|
||
"""Fetch current price from Binance API"""
|
||
try:
|
||
url = f"{BINANCE_API_BASE_URL}/api/v3/ticker/price"
|
||
params = {"symbol": symbol}
|
||
|
||
response = requests.get(url, params=params, timeout=10)
|
||
response.raise_for_status()
|
||
|
||
data = response.json()
|
||
price = float(data["price"])
|
||
|
||
if debug:
|
||
timestamp = datetime.now().strftime("%H:%M:%S")
|
||
print(f'[{timestamp}] Binance {symbol}: {price:.4f}')
|
||
|
||
return price
|
||
|
||
except requests.exceptions.RequestException as e:
|
||
if debug:
|
||
print(f"❌ Binance API request error for {symbol}: {e}")
|
||
return None
|
||
except (KeyError, ValueError) as e:
|
||
if debug:
|
||
print(f"❌ Binance API data error for {symbol}: {e}")
|
||
return None
|
||
except Exception as e:
|
||
if debug:
|
||
print(f"❌ Unexpected error fetching Binance price for {symbol}: {e}")
|
||
return None
|
||
|
||
def get_all_binance_prices(debug=False):
|
||
"""Fetch prices for all mapped symbols from Binance"""
|
||
binance_prices = {}
|
||
|
||
if debug:
|
||
print("Fetching prices from Binance API...")
|
||
|
||
for flextrade_market, binance_symbol in FLEXTRADE_TO_BINANCE_MAPPING.items():
|
||
price = get_binance_price(binance_symbol, debug)
|
||
if price is not None:
|
||
current_timestamp = datetime.now()
|
||
binance_prices[flextrade_market] = {
|
||
'price': price,
|
||
'binance_symbol': binance_symbol,
|
||
'timestamp': current_timestamp,
|
||
'timestamp_str': current_timestamp.strftime("%Y-%m-%d %H:%M:%S"),
|
||
'source': 'binance'
|
||
}
|
||
else:
|
||
if debug:
|
||
print(f"⚠️ Failed to fetch price for {flextrade_market} ({binance_symbol})")
|
||
|
||
# Small delay to avoid rate limiting
|
||
time.sleep(0.1)
|
||
|
||
return binance_prices
|
||
|
||
def compare_prices(flextrade_prices, binance_prices, debug=False):
|
||
"""Compare FlexTrade and Binance prices"""
|
||
comparison_results = {}
|
||
|
||
if debug:
|
||
print("\n" + "="*80)
|
||
print("PRICE COMPARISON: FlexTrade vs Binance")
|
||
print("="*80)
|
||
|
||
for market in flextrade_prices:
|
||
if market in binance_prices:
|
||
ft_price = flextrade_prices[market]['price']
|
||
bn_price = binance_prices[market]['price']
|
||
|
||
# Calculate difference
|
||
price_diff = ft_price - bn_price
|
||
price_diff_pct = (price_diff / bn_price) * 100 if bn_price > 0 else 0
|
||
|
||
comparison_results[market] = {
|
||
'flextrade_price': ft_price,
|
||
'binance_price': bn_price,
|
||
'difference': price_diff,
|
||
'difference_pct': price_diff_pct,
|
||
'flextrade_timestamp': flextrade_prices[market]['timestamp_str'],
|
||
'binance_timestamp': binance_prices[market]['timestamp_str']
|
||
}
|
||
|
||
if debug:
|
||
status = "📈" if price_diff > 0 else "📉" if price_diff < 0 else "➡️"
|
||
print(f"{status} {market:<12} | FlexTrade: ${ft_price:>8.4f} | Binance: ${bn_price:>8.4f} | Diff: {price_diff:>+7.4f} ({price_diff_pct:>+6.2f}%)")
|
||
|
||
elif debug:
|
||
print(f"⚠️ {market:<12} | Only available on FlexTrade")
|
||
|
||
# Check for Binance-only prices
|
||
if debug:
|
||
for market in binance_prices:
|
||
if market not in flextrade_prices:
|
||
bn_price = binance_prices[market]['price']
|
||
print(f"ℹ️ {market:<12} | Only available on Binance: ${bn_price:.4f}")
|
||
|
||
print("="*80)
|
||
|
||
return comparison_results
|
||
|
||
|
||
def market_info(debug=False, include_binance=True):
|
||
"""Fetch market information once and update global prices"""
|
||
client = Client(
|
||
eth_private_key=PRIVATE_KEY,
|
||
rpc_url=RPC_URL
|
||
)
|
||
|
||
if debug:
|
||
print("Fetching market data...\n")
|
||
|
||
# Fetch FlexTrade prices
|
||
for market in MARKETS:
|
||
try:
|
||
market_info_data = client.public.get_market_info(market)
|
||
print_market_info(market_info_data, debug)
|
||
|
||
# Store the latest price and timestamp in global dictionary
|
||
market_name = market_info_data["market"]
|
||
current_timestamp = datetime.now()
|
||
latest_prices[market_name] = {
|
||
'price': market_info_data["price"],
|
||
'timestamp': current_timestamp,
|
||
'timestamp_str': current_timestamp.strftime("%Y-%m-%d %H:%M:%S"),
|
||
'source': 'flextrade'
|
||
}
|
||
|
||
time.sleep(15) # Wait 15 seconds between requests to avoid rate limiting
|
||
|
||
except Exception as e:
|
||
if debug:
|
||
print(f"Error fetching market {market}: {e}")
|
||
print('-' * 50)
|
||
# Skip this market and continue with the next one
|
||
continue
|
||
|
||
# Fetch Binance prices and compare if enabled
|
||
if include_binance:
|
||
try:
|
||
binance_prices = get_all_binance_prices(debug)
|
||
|
||
# Store Binance prices in a separate section of latest_prices
|
||
for market, price_data in binance_prices.items():
|
||
binance_key = f"{market}_BINANCE"
|
||
latest_prices[binance_key] = price_data
|
||
|
||
# Compare prices if both sources have data
|
||
flextrade_only = {k: v for k, v in latest_prices.items() if not k.endswith('_BINANCE') and v.get('source') == 'flextrade'}
|
||
if flextrade_only and binance_prices:
|
||
comparison = compare_prices(flextrade_only, binance_prices, debug)
|
||
|
||
# Store comparison results
|
||
latest_prices['_comparison'] = {
|
||
'timestamp': datetime.now(),
|
||
'results': comparison
|
||
}
|
||
|
||
except Exception as e:
|
||
if debug:
|
||
print(f"❌ Error fetching Binance prices: {e}")
|
||
# Continue without Binance data
|
||
|
||
def market_info_loop(debug=False, include_binance=True):
|
||
"""Continuously fetch market information in a loop"""
|
||
if debug:
|
||
print("Starting market data loop - press Ctrl+C to stop")
|
||
print("-" * 50)
|
||
|
||
try:
|
||
while True:
|
||
try:
|
||
market_info(debug, include_binance)
|
||
# if debug:
|
||
# print(f"\nSleeping for 30 seconds...\n")
|
||
# time.sleep(30) # Wait 30 seconds between full cycles
|
||
except Exception as e:
|
||
if debug:
|
||
print(f"Error in market_info cycle: {e}")
|
||
print("Waiting 15 seconds before retry...")
|
||
time.sleep(15) # Wait 15 seconds before retrying on error
|
||
continue
|
||
# except KeyboardInterrupt:
|
||
# if debug:
|
||
# print("\nMarket data loop stopped by user")
|
||
except Exception as e:
|
||
if debug:
|
||
print(f"Fatal error occurred: {e}")
|
||
print("Market data loop stopped")
|
||
|
||
def get_latest_price(market_name):
|
||
"""Get the latest price for a specific market"""
|
||
market_data = latest_prices.get(market_name, None)
|
||
return market_data['price'] if market_data else None
|
||
|
||
def get_latest_price_with_timestamp(market_name):
|
||
"""Get the latest price with timestamp for a specific market"""
|
||
return latest_prices.get(market_name, None)
|
||
|
||
def get_all_latest_prices():
|
||
"""Get all latest prices with timestamps"""
|
||
return latest_prices.copy()
|
||
|
||
def get_all_latest_prices_only():
|
||
"""Get all latest prices without timestamps (backward compatibility)"""
|
||
return {market: data['price'] for market, data in latest_prices.items()}
|
||
|
||
def get_binance_price_for_market(market_name):
|
||
"""Get the latest Binance price for a specific market"""
|
||
binance_key = f"{market_name}_BINANCE"
|
||
market_data = latest_prices.get(binance_key, None)
|
||
return market_data['price'] if market_data else None
|
||
|
||
def get_price_comparison(market_name):
|
||
"""Get price comparison data for a specific market"""
|
||
comparison_data = latest_prices.get('_comparison', {})
|
||
results = comparison_data.get('results', {})
|
||
return results.get(market_name, None)
|
||
|
||
def get_all_price_comparisons():
|
||
"""Get all price comparison data"""
|
||
comparison_data = latest_prices.get('_comparison', {})
|
||
return comparison_data.get('results', {})
|
||
|
||
def get_market_arbitrage_opportunities(min_percentage_diff=0.5):
|
||
"""Find arbitrage opportunities between FlexTrade and Binance"""
|
||
comparisons = get_all_price_comparisons()
|
||
opportunities = []
|
||
|
||
for market, data in comparisons.items():
|
||
abs_diff_pct = abs(data['difference_pct'])
|
||
if abs_diff_pct >= min_percentage_diff:
|
||
opportunity = {
|
||
'market': market,
|
||
'flextrade_price': data['flextrade_price'],
|
||
'binance_price': data['binance_price'],
|
||
'difference_pct': data['difference_pct'],
|
||
'action': 'buy_flextrade_sell_binance' if data['difference_pct'] < 0 else 'buy_binance_sell_flextrade',
|
||
'potential_profit_pct': abs_diff_pct
|
||
}
|
||
opportunities.append(opportunity)
|
||
|
||
# Sort by potential profit percentage (highest first)
|
||
opportunities.sort(key=lambda x: x['potential_profit_pct'], reverse=True)
|
||
return opportunities
|
||
|
||
if __name__ == '__main__':
|
||
# Enable debug when running directly
|
||
print("🚀 Starting market data collection with Binance price comparison...")
|
||
print("📊 Number of FlexTrade markets being monitored:", len(MARKETS))
|
||
print("🔗 Binance symbols mapped:", list(FLEXTRADE_TO_BINANCE_MAPPING.values()))
|
||
print("=" * 80)
|
||
market_info_loop(debug=True, include_binance=True) |