Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions env_example.txt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ LOG_FILE=trading_log.csv

TIMEZONE=Asia/Shanghai

# Ticker映射配置 (对冲模式专用)
# 用于解决不同交易所使用不同ticker名称的问题
# 格式: {"源交易所_to_目标交易所": {"源ticker": "目标ticker"}}
# 例子: Extended使用"EUR",Lighter使用"EURUSD"
# 注意: lighter上交易EUR要使用Isolated保证金模式,请先手动切换
TICKER_MAPPING={"extended_to_lighter": {"EUR":"EURUSD","GBP":"GBPUSD","JPY":"JPYUSD","AUD":"AUDUSD"}, "backpack_to_lighter": {"EUR":"EURUSD"}, "grvt_to_lighter": {"EUR":"EURUSD"}}

# EdgeX API Endpoints
EDGEX_BASE_URL=https://pro.edgex.exchange
EDGEX_WS_URL=wss://quote.edgex.exchange
2 changes: 1 addition & 1 deletion exchanges/extended.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from helpers.logger import TradingLogger

from x10.perpetual.trading_client import PerpetualTradingClient
from x10.perpetual.configuration import STARKNET_MAINNET_CONFIG
from x10.perpetual.configuration import MAINNET_CONFIG as STARKNET_MAINNET_CONFIG
from x10.perpetual.accounts import StarkPerpetualAccount
from x10.perpetual.orders import TimeInForce, OrderSide

Expand Down
61 changes: 57 additions & 4 deletions hedge/hedge_mode_ext.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ def __init__(self, ticker: str, order_quantity: Decimal, fill_timeout: int = 5,
self.extended_vault = os.getenv('EXTENDED_VAULT')
self.extended_stark_key_private = os.getenv('EXTENDED_STARK_KEY_PRIVATE')
self.extended_stark_key_public = os.getenv('EXTENDED_STARK_KEY_PUBLIC')

# Load ticker mapping configuration
self._load_ticker_mapping()
self.extended_api_key = os.getenv('EXTENDED_API_KEY')

def shutdown(self, signum=None, frame=None):
Expand Down Expand Up @@ -179,6 +182,52 @@ def shutdown(self, signum=None, frame=None):
except Exception:
pass

def _load_ticker_mapping(self):
"""Load ticker mapping configuration from environment variables."""
# Default fallback mapping for common currency pairs
default_mapping = {
"extended_to_lighter": {
"EUR": "EURUSD",
"GBP": "GBPUSD",
"JPY": "JPYUSD",
"AUD": "AUDUSD"
}
}

try:
# Try to load from environment variable
ticker_mapping_str = os.getenv('TICKER_MAPPING')

if ticker_mapping_str:
self.ticker_mapping = json.loads(ticker_mapping_str)
self.logger.info(f"✅ Loaded ticker mapping from .env: {self.ticker_mapping}")
else:
self.ticker_mapping = default_mapping
self.logger.info(f"⚠️ TICKER_MAPPING not found in .env, using default mapping: {default_mapping}")

except json.JSONDecodeError as e:
self.ticker_mapping = default_mapping
self.logger.error(f"⚠️ Error parsing TICKER_MAPPING JSON: {e}, using default mapping: {default_mapping}")
except Exception as e:
self.ticker_mapping = default_mapping
self.logger.error(f"⚠️ Unexpected error loading ticker mapping: {e}, using default mapping: {default_mapping}")

def get_lighter_ticker(self):
"""Get the corresponding Lighter ticker for the current ticker."""
mapping_key = "extended_to_lighter"

# Check if mapping exists for this exchange combination
if mapping_key in self.ticker_mapping:
exchange_mapping = self.ticker_mapping[mapping_key]
if self.ticker in exchange_mapping:
lighter_ticker = exchange_mapping[self.ticker]
self.logger.info(f"🔄 Ticker mapping: Extended '{self.ticker}' → Lighter '{lighter_ticker}'")
return lighter_ticker

# If no mapping found, use original ticker and log warning
self.logger.warning(f"⚠️ No ticker mapping found for Extended '{self.ticker}' to Lighter, using original ticker")
return self.ticker

def _initialize_csv_file(self):
"""Initialize CSV file with headers if it doesn't exist."""
if not os.path.exists(self.csv_filename):
Expand Down Expand Up @@ -551,6 +600,9 @@ def get_lighter_market_config(self) -> Tuple[int, int, int, Decimal]:
url = f"{self.lighter_base_url}/api/v1/orderBooks"
headers = {"accept": "application/json"}

# Get the mapped ticker for Lighter
lighter_ticker = self.get_lighter_ticker()

try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
Expand All @@ -564,15 +616,16 @@ def get_lighter_market_config(self) -> Tuple[int, int, int, Decimal]:
raise Exception("Unexpected response format")

for market in data["order_books"]:
if market["symbol"] == self.ticker:
if market["symbol"] == lighter_ticker:
price_multiplier = pow(10, market["supported_price_decimals"])
return (market["market_id"],
pow(10, market["supported_size_decimals"]),
self.logger.info(f"✅ Found Lighter market config for {lighter_ticker}: market_id={market['market_id']}")
return (market["market_id"],
pow(10, market["supported_size_decimals"]),
price_multiplier,
Decimal("1") / (Decimal("10") ** market["supported_price_decimals"])
)

raise Exception(f"Ticker {self.ticker} not found")
raise Exception(f"Ticker {lighter_ticker} not found")

except Exception as e:
self.logger.error(f"⚠️ Error getting market config: {e}")
Expand Down