441 lines
19 KiB
Python
441 lines
19 KiB
Python
import time
|
|
from datetime import datetime
|
|
import threading
|
|
from market import (
|
|
market_info_loop,
|
|
get_latest_price,
|
|
get_all_latest_prices,
|
|
get_latest_price_with_timestamp,
|
|
get_binance_price_for_market,
|
|
get_price_comparison,
|
|
get_market_arbitrage_opportunities
|
|
)
|
|
from order import create_market_order, create_trigger_order, update_trigger_order, cancel_trigger_order
|
|
from flextrade.constants.markets import BASE_MARKET_ETH_USD, BASE_MARKET_SOL_USD, BASE_MARKET_BTC_USD
|
|
from flextrade.enum import Action
|
|
|
|
# Validate Action enum is properly imported
|
|
print(f"✅ Action enum imported: BUY={Action.BUY} (boolean), SELL={Action.SELL} (boolean)")
|
|
|
|
# Configuration
|
|
ENABLE_AUTO_HEDGE = False # Set to True to enable automatic hedging
|
|
ENABLE_TRIGGER_ORDERS = False # Set to True to enable trigger order functionality
|
|
sol_hedge_price = 161 # Example price for SOL auto-hedging
|
|
SUB_ACCOUNT_ID = 1 # Example sub-account ID for testing
|
|
# Global storage for active orders
|
|
active_orders = {
|
|
"trigger_orders": [], # List of {order_id, market, account_id, action, size, trigger_price}
|
|
"market_orders": [] # List of market order transactions
|
|
}
|
|
|
|
# Track executed hedges to prevent duplicates
|
|
executed_hedges = {
|
|
"sol_hedge_executed": False,
|
|
"last_hedge_price": 0
|
|
}
|
|
|
|
def start_market_data_thread(debug=True, include_binance=True):
|
|
"""Start market data collection in a separate thread"""
|
|
market_thread = threading.Thread(target=lambda: market_info_loop(debug, include_binance), daemon=True)
|
|
market_thread.start()
|
|
return market_thread
|
|
|
|
def store_trigger_order(result, market, account_id, action, size, trigger_price):
|
|
"""Store trigger order information for later management"""
|
|
if result and 'order' in result and 'orderIndex' in result['order']:
|
|
order_info = {
|
|
'order_id': result['order']['orderIndex'],
|
|
'market': market,
|
|
'account_id': account_id,
|
|
'action': action,
|
|
'size': size,
|
|
'trigger_price': trigger_price,
|
|
'tx_hash': result['tx'].hex(),
|
|
'created_at': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
}
|
|
active_orders["trigger_orders"].append(order_info)
|
|
print(f"📝 Stored trigger order info: ID {order_info['order_id']}")
|
|
return order_info
|
|
return None
|
|
|
|
def get_active_trigger_orders():
|
|
"""Get list of all active trigger orders"""
|
|
return active_orders["trigger_orders"].copy()
|
|
|
|
def remove_trigger_order(order_id):
|
|
"""Remove a trigger order from active list"""
|
|
active_orders["trigger_orders"] = [
|
|
order for order in active_orders["trigger_orders"]
|
|
if order['order_id'] != order_id
|
|
]
|
|
print(f"📝 Removed trigger order ID {order_id} from active list")
|
|
|
|
def safe_action_to_string(action):
|
|
"""Convert action to string safely, handling both enum and boolean cases"""
|
|
if hasattr(action, 'name'):
|
|
return action.name
|
|
elif action is True or action == 1:
|
|
return "BUY"
|
|
elif action is False or action == 0:
|
|
return "SELL"
|
|
else:
|
|
return str(action)
|
|
|
|
def handle_market_order(account_id, market, action, size):
|
|
"""
|
|
Handle market order creation with error handling
|
|
|
|
Args:
|
|
account_id (int): Account ID to trade with
|
|
market (str): Market constant (e.g., BASE_MARKET_ETH_USD)
|
|
action (Action): Action.BUY or Action.SELL
|
|
size (float): Order size
|
|
"""
|
|
try:
|
|
print(f"\n--- Creating Market Order ---")
|
|
|
|
# Convert action to string safely
|
|
action_name = safe_action_to_string(action)
|
|
print(f"Account: {account_id}, Market: {market}, Action: {action_name}, Size: {size}")
|
|
|
|
result = create_market_order(account_id, market, action, size)
|
|
|
|
if result:
|
|
print(f"✅ Market order successful!")
|
|
print(f"Transaction: {result['tx'].hex()}")
|
|
else:
|
|
print("❌ Market order failed!")
|
|
|
|
print("--- Order Complete ---\n")
|
|
return result
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error handling market order: {e}")
|
|
return None
|
|
|
|
|
|
def handle_trigger_order(account_id, market, action, size, trigger_price, trigger_above=True):
|
|
"""
|
|
Handle trigger order creation with error handling
|
|
|
|
Args:
|
|
account_id (int): Account ID to trade with
|
|
market (str): Market constant (e.g., BASE_MARKET_ETH_USD)
|
|
action (Action): Action.BUY or Action.SELL
|
|
size (float): Order size
|
|
trigger_price (float): Price at which the order should trigger
|
|
trigger_above (bool): True to trigger when price goes above, False for below
|
|
"""
|
|
try:
|
|
print(f"\n--- Creating Trigger Order ---")
|
|
|
|
# Convert action to string safely
|
|
action_name = safe_action_to_string(action)
|
|
trigger_direction = "above" if trigger_above else "below"
|
|
print(f"Account: {account_id}, Market: {market}, Action: {action_name}, Size: {size}")
|
|
print(f"Trigger: {trigger_direction} ${trigger_price:.2f}")
|
|
|
|
result = create_trigger_order(account_id, market, action, size, trigger_price, trigger_above)
|
|
|
|
if result:
|
|
print(f"✅ Trigger order successful!")
|
|
print(f"Transaction: {result['tx'].hex()}")
|
|
print(f"Order ID: {result['order']['orderIndex']}")
|
|
# Store the order for later management
|
|
store_trigger_order(result, market, account_id, action, size, trigger_price)
|
|
else:
|
|
print("❌ Trigger order failed!")
|
|
|
|
print("--- Order Complete ---\n")
|
|
return result
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error handling trigger order: {e}")
|
|
return None
|
|
|
|
|
|
def handle_update_trigger_order(account_id, order_id, action, size, trigger_price, trigger_above=True):
|
|
"""
|
|
Handle trigger order update with error handling
|
|
|
|
Args:
|
|
account_id (int): Account ID
|
|
order_id (int): Order ID to update
|
|
action (Action): Action.BUY or Action.SELL
|
|
size (float): New order size
|
|
trigger_price (float): New trigger price
|
|
trigger_above (bool): True to trigger when price goes above, False for below
|
|
"""
|
|
try:
|
|
print(f"\n--- Updating Trigger Order ---")
|
|
trigger_direction = "above" if trigger_above else "below"
|
|
action_name = safe_action_to_string(action)
|
|
print(f"Order ID: {order_id}, Action: {action_name}, Size: {size}")
|
|
print(f"New trigger: {trigger_direction} ${trigger_price:.2f}")
|
|
|
|
result = update_trigger_order(account_id, order_id, action, size, trigger_price, trigger_above)
|
|
|
|
if result:
|
|
print(f"✅ Trigger order updated successfully!")
|
|
print(f"Transaction: {result['tx'].hex()}")
|
|
else:
|
|
print("❌ Trigger order update failed!")
|
|
|
|
print("--- Update Complete ---\n")
|
|
return result
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error updating trigger order: {e}")
|
|
return None
|
|
|
|
|
|
def handle_cancel_trigger_order(account_id, market, order_id):
|
|
"""
|
|
Handle trigger order cancellation with error handling
|
|
|
|
Args:
|
|
account_id (int): Account ID
|
|
market (str): Market constant
|
|
order_id (int): Order ID to cancel
|
|
"""
|
|
try:
|
|
print(f"\n--- Cancelling Trigger Order ---")
|
|
print(f"Order ID: {order_id}, Market: {market}")
|
|
|
|
result = cancel_trigger_order(account_id, market, order_id)
|
|
|
|
if result:
|
|
print(f"✅ Trigger order cancelled successfully!")
|
|
print(f"Transaction: {result['tx'].hex()}")
|
|
# Remove from active orders list
|
|
remove_trigger_order(order_id)
|
|
else:
|
|
print("❌ Trigger order cancellation failed!")
|
|
|
|
print("--- Cancellation Complete ---\n")
|
|
return result
|
|
|
|
except Exception as e:
|
|
print(f"❌ Error cancelling trigger order: {e}")
|
|
return None
|
|
|
|
|
|
def smart_market_order(market_name, market_constant, action, size, price_threshold=None):
|
|
"""
|
|
Create a market order with optional price threshold checking
|
|
|
|
Args:
|
|
market_name (str): Market name for price lookup (e.g., "ETHUSD")
|
|
market_constant (str): Market constant for order (e.g., BASE_MARKET_ETH_USD)
|
|
action (Action): Action.BUY or Action.SELL
|
|
size (float): Order size
|
|
price_threshold (float): Optional price threshold for conditional orders
|
|
|
|
Returns:
|
|
dict: Order result or None if conditions not met
|
|
"""
|
|
current_price_data = get_latest_price_with_timestamp(market_name)
|
|
|
|
if not current_price_data:
|
|
print(f"❌ No price data available for {market_name}")
|
|
return None
|
|
|
|
current_price = current_price_data['price']
|
|
print(f"📊 Current {market_name} price: ${current_price:.2f}")
|
|
|
|
# Check price threshold if provided
|
|
if price_threshold:
|
|
if action == Action.BUY and current_price > price_threshold:
|
|
print(f"⏸️ Price ${current_price:.2f} above buy threshold ${price_threshold:.2f} - order not placed")
|
|
return None
|
|
elif action == Action.SELL and current_price < price_threshold:
|
|
print(f"⏸️ Price ${current_price:.2f} below sell threshold ${price_threshold:.2f} - order not placed")
|
|
return None
|
|
|
|
print(f"✅ Price conditions met, placing order...")
|
|
return handle_market_order(SUB_ACCOUNT_ID, market_constant, action, size)
|
|
|
|
def smart_trigger_order(market_name, market_constant, action, size, trigger_price, trigger_above=True, offset_percentage=None):
|
|
"""
|
|
Create a trigger order with smart pricing based on current market conditions
|
|
|
|
Args:
|
|
market_name (str): Market name for price lookup (e.g., "ETHUSD")
|
|
market_constant (str): Market constant for order (e.g., BASE_MARKET_ETH_USD)
|
|
action (Action): Action.BUY or Action.SELL
|
|
size (float): Order size
|
|
trigger_price (float): Price at which the order should trigger
|
|
trigger_above (bool): True to trigger when price goes above, False for below
|
|
offset_percentage (float): Optional percentage offset from current price for automatic trigger calculation
|
|
|
|
Returns:
|
|
dict: Order result or None if conditions not met
|
|
"""
|
|
current_price_data = get_latest_price_with_timestamp(market_name)
|
|
|
|
if not current_price_data:
|
|
print(f"❌ No price data available for {market_name}")
|
|
return None
|
|
|
|
current_price = current_price_data['price']
|
|
print(f"📊 Current {market_name} price: ${current_price:.2f}")
|
|
|
|
# Calculate trigger price based on offset if provided
|
|
if offset_percentage:
|
|
if trigger_above:
|
|
calculated_trigger = current_price * (1 + offset_percentage / 100)
|
|
else:
|
|
calculated_trigger = current_price * (1 - offset_percentage / 100)
|
|
|
|
print(f"🧮 Calculated trigger price: ${calculated_trigger:.2f} ({offset_percentage:+.1f}% from current)")
|
|
trigger_price = calculated_trigger
|
|
|
|
# Validate trigger price makes sense
|
|
trigger_direction = "above" if trigger_above else "below"
|
|
if trigger_above and trigger_price <= current_price:
|
|
print(f"⚠️ Warning: Trigger price ${trigger_price:.2f} is not above current price ${current_price:.2f}")
|
|
elif not trigger_above and trigger_price >= current_price:
|
|
print(f"⚠️ Warning: Trigger price ${trigger_price:.2f} is not below current price ${current_price:.2f}")
|
|
|
|
print(f"✅ Creating trigger order: {trigger_direction} ${trigger_price:.2f}")
|
|
return handle_trigger_order(SUB_ACCOUNT_ID, market_constant, action, size, trigger_price, trigger_above)
|
|
|
|
|
|
|
|
def main():
|
|
print("Starting app with market data collection...")
|
|
print("-" * 40)
|
|
|
|
# Start market data collection in background thread with Binance comparison
|
|
start_market_data_thread(debug=False, include_binance=True)
|
|
|
|
|
|
# Give some time for initial market data to be collected
|
|
time.sleep(10)
|
|
|
|
try:
|
|
while True:
|
|
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
print(f"[{current_time}] update!")
|
|
|
|
# Show some sample prices with timestamps (FlexTrade and Binance)
|
|
eth_data = get_latest_price_with_timestamp("ETHUSD")
|
|
btc_data = get_latest_price_with_timestamp("BTCUSD")
|
|
sol_data = get_latest_price_with_timestamp("SOLUSD")
|
|
|
|
# Get Binance prices for comparison
|
|
eth_binance = get_latest_price_with_timestamp("ETHUSD_BINANCE")
|
|
btc_binance = get_latest_price_with_timestamp("BTCUSD_BINANCE")
|
|
sol_binance = get_latest_price_with_timestamp("SOLUSD_BINANCE")
|
|
|
|
if eth_data or eth_binance:
|
|
ft_price = f"${eth_data['price']:.2f}" if eth_data else "N/A"
|
|
bn_price = f"${eth_binance['price']:.2f}" if eth_binance else "N/A"
|
|
print(f"ETH/USD: FlexTrade={ft_price} | Binance={bn_price}")
|
|
|
|
if btc_data or btc_binance:
|
|
ft_price = f"${btc_data['price']:.2f}" if btc_data else "N/A"
|
|
bn_price = f"${btc_binance['price']:.2f}" if btc_binance else "N/A"
|
|
print(f"BTC/USD: FlexTrade={ft_price} | Binance={bn_price}")
|
|
|
|
if sol_data or sol_binance:
|
|
ft_price = f"${sol_data['price']:.2f}" if sol_data else "N/A"
|
|
bn_price = f"${sol_binance['price']:.2f}" if sol_binance else "N/A"
|
|
ft_time = sol_data['timestamp_str'] if sol_data else "N/A"
|
|
bn_time = sol_binance['timestamp_str'] if sol_binance else "N/A"
|
|
print(f"SOL/USD: FlexTrade={ft_price} ({ft_time}) | Binance={bn_price} ({bn_time})")
|
|
|
|
# Show price difference if both available
|
|
if sol_data and sol_binance:
|
|
price_diff = sol_data['price'] - sol_binance['price']
|
|
diff_pct = (price_diff / sol_binance['price']) * 100 if sol_binance['price'] > 0 else 0
|
|
diff_emoji = "📈" if price_diff > 0 else "📉" if price_diff < 0 else "➡️"
|
|
print(f"SOL Difference: {diff_emoji} {price_diff:+.4f} ({diff_pct:+.2f}%)")
|
|
|
|
# Show total number of markets with prices
|
|
all_prices = get_all_latest_prices()
|
|
flextrade_markets = len([k for k in all_prices.keys() if not k.endswith('_BINANCE') and k != '_comparison'])
|
|
binance_markets = len([k for k in all_prices.keys() if k.endswith('_BINANCE')])
|
|
print(f"Total markets tracked: FlexTrade={flextrade_markets}, Binance={binance_markets}")
|
|
|
|
# Show arbitrage opportunities if available
|
|
try:
|
|
arbitrage_ops = get_market_arbitrage_opportunities(min_percentage_diff=0.5)
|
|
if arbitrage_ops:
|
|
print(f"\n🎯 Found {len(arbitrage_ops)} arbitrage opportunities (>0.5% difference):")
|
|
for i, op in enumerate(arbitrage_ops[:3]): # Show top 3
|
|
action_emoji = "📈" if "buy_flextrade" in op['action'] else "📉"
|
|
print(f" {i+1}. {action_emoji} {op['market']}: {op['potential_profit_pct']:.2f}% potential profit")
|
|
print(f" FlexTrade: ${op['flextrade_price']:.4f} | Binance: ${op['binance_price']:.4f}")
|
|
print(f" Strategy: {op['action'].replace('_', ' ').title()}")
|
|
else:
|
|
print("🎯 No significant arbitrage opportunities found (threshold: 0.5%)")
|
|
|
|
# Show smaller differences for informational purposes
|
|
small_ops = get_market_arbitrage_opportunities(min_percentage_diff=0.1)
|
|
if small_ops:
|
|
best_small = small_ops[0] # Show best small opportunity
|
|
diff_emoji = "📈" if best_small['difference_pct'] > 0 else "📉"
|
|
print(f" Best small difference: {diff_emoji} {best_small['market']} ({best_small['potential_profit_pct']:.2f}%)")
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ Error checking arbitrage opportunities: {e}")
|
|
|
|
print() # Extra line for readability
|
|
|
|
# Auto-hedging logic
|
|
if ENABLE_AUTO_HEDGE:
|
|
# Market order examples - immediate execution
|
|
# if eth_data and eth_data['price'] < 3000: # Buy ETH if price drops below $3000
|
|
# print("🤖 Auto-hedge triggered: ETH price below $3000")
|
|
# smart_market_order("ETHUSD", BASE_MARKET_ETH_USD, Action.BUY, 0.01)
|
|
|
|
# SOL hedging logic with duplicate prevention
|
|
if (sol_data and sol_data['price'] > sol_hedge_price and
|
|
not executed_hedges["sol_hedge_executed"]): # Only execute once
|
|
|
|
print(f"🤖 Auto-hedge triggered: SOL price above ${sol_hedge_price}")
|
|
|
|
# Execute market order to buy SOL
|
|
market_result = smart_market_order("SOLUSD", BASE_MARKET_SOL_USD, Action.BUY, 100.0)
|
|
|
|
if market_result:
|
|
print("🤖 Auto-hedge triggered: SOL stop-loss trigger")
|
|
time.sleep(10)
|
|
# Create stop-loss trigger order
|
|
trigger_result = smart_trigger_order("SOLUSD", BASE_MARKET_SOL_USD, Action.SELL, 100.0, 0, trigger_above=False, offset_percentage=5)
|
|
|
|
if trigger_result:
|
|
# Mark hedge as executed
|
|
executed_hedges["sol_hedge_executed"] = True
|
|
executed_hedges["last_hedge_price"] = sol_data['price']
|
|
print(f"✅ SOL hedge completed at price ${sol_data['price']:.2f}")
|
|
|
|
# Reset hedge flag if price drops significantly below hedge price
|
|
if (sol_data and sol_data['price'] < (sol_hedge_price * 0.95) and
|
|
executed_hedges["sol_hedge_executed"]):
|
|
print(f"🔄 Resetting SOL hedge flag - price dropped to ${sol_data['price']:.2f}")
|
|
executed_hedges["sol_hedge_executed"] = False
|
|
|
|
|
|
|
|
# Trigger order examples - conditional execution
|
|
# Create stop-loss orders:
|
|
# if eth_data and eth_data['price'] > 3500: # Set stop-loss 5% below current ETH price
|
|
# print("🤖 Setting ETH stop-loss trigger")
|
|
# smart_trigger_order("ETHUSD", BASE_MARKET_ETH_USD, Action.SELL, 0.01, 0, trigger_above=False, offset_percentage=5)
|
|
|
|
# Create take-profit orders:
|
|
# if btc_data and btc_data['price'] < 50000: # Set take-profit 10% above current BTC price
|
|
# print("🤖 Setting BTC take-profit trigger")
|
|
# smart_trigger_order("BTCUSD", BASE_MARKET_BTC_USD, Action.SELL, 0.001, 0, trigger_above=True, offset_percentage=10)
|
|
|
|
# Sleep for 60 seconds (1 minute)
|
|
time.sleep(60)
|
|
|
|
except Exception as e:
|
|
print(f"An error occurred: {e}")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|