feat: Add support for non-INR cash pairs like BTC/USDT in CCXT shim and mock mode, and improve green gate script logging.

pull/12760/head
vijay sharma 4 months ago
parent a57fa9f2a5
commit bcf1d1ac73

@ -350,11 +350,13 @@ class BreezeCCXT(ccxt.Exchange):
elif spec.type == InstrumentType.CASH:
info = nse_symbols.get(spec.underlying)
if not info and self._is_mock_mode():
# Synthetic Index Cash support for mock mode (NIFTY/INR etc)
if spec.underlying in self._MOCK_BASE_PRICES or spec.underlying in {
# Synthetic Index Cash or Mock Pairs support (BTC/USDT, NIFTY/INR etc)
is_index = spec.underlying in self._MOCK_BASE_PRICES or spec.underlying in {
"NIFTY",
"BANKNIFTY",
}:
}
is_mock_pair = spec.underlying == "BTC" and spec.quote == "USDT"
if is_index or is_mock_pair:
info = {
"token": f"mock_{spec.underlying}",
"symbol": spec.underlying,
@ -369,9 +371,11 @@ class BreezeCCXT(ccxt.Exchange):
markets.append(
{
"id": info["token"],
"symbol": format_pair(spec),
"symbol": format_pair(spec)
if spec.quote == "INR"
else f"{spec.underlying}/{spec.quote}",
"base": spec.underlying,
"quote": "INR",
"quote": spec.quote,
"active": True,
"type": "spot",
"spot": True,
@ -641,10 +645,17 @@ class BreezeAsyncCCXT(ccxt_async.Exchange):
return res
async def load_markets(self, reload: bool = False, params: dict | None = None):
if not reload and self.markets:
return self.markets
markets = await asyncio.to_thread(self.sync_exchange.load_markets, reload, params)
self.markets = markets
self.symbols = list(markets.keys())
return markets
self.set_markets(markets)
if "INR" not in self.currencies:
self.currencies["INR"] = {
"id": "INR",
"code": "INR",
"precision": 2,
}
return self.markets
async def fetch_markets(self, params: dict | None = None):
return await asyncio.to_thread(self.sync_exchange.fetch_markets, params)

@ -32,10 +32,10 @@ class InstrumentSpec:
strike: float | None = None
right: str | None = None
def validate(self) -> None:
def validate(self, strict: bool = True) -> None:
if not self.underlying:
raise ValueError("Underlying is required.")
if self.quote != "INR":
if strict and self.quote != "INR":
raise ValueError("Quote must be INR for canonical pairs.")
if self.type == InstrumentType.CASH:
if any([self.expiry_yyyymmdd, self.strike, self.right]):
@ -72,13 +72,18 @@ def parse_pair(pair: str) -> InstrumentSpec:
- Options: UNDERLYING-YYYYMMDD-STRIKE-CE/INR
"""
cash_match = re.fullmatch(r"(?P<underlying>[A-Z0-9]+)\/INR", pair, re.IGNORECASE)
cash_match = re.fullmatch(
r"(?P<underlying>[A-Z0-9.]+)\/(?P<quote>[A-Z0-9]+)", pair, re.IGNORECASE
)
if cash_match:
spec = InstrumentSpec(
type=InstrumentType.CASH,
underlying=cash_match.group("underlying").upper(),
quote=cash_match.group("quote").upper(),
)
spec.validate()
# We allow non-INR in parse_pair, but validate(strict=True) would still fail it.
# For now, we call validate(strict=False) to allow parsing.
spec.validate(strict=False)
return spec
fut_match = re.fullmatch(

@ -0,0 +1,30 @@
import logging
import json
from adapters.ccxt_shim.breeze_ccxt import BreezeCCXT
logging.basicConfig(level=logging.DEBUG)
def debug_fetch():
with open("user_data/config_icicibreeze.json", "r") as f:
config = json.load(f)
exchange_config = config["exchange"]
# Freqtrade typically passes the 'exchange' sub-dict to CCXT
# but some fields are modified or added.
print(f"Exchange Config Keys: {list(exchange_config.keys())}")
print(f"Pair Whitelist: {exchange_config.get('pair_whitelist')}")
exchange = BreezeCCXT(exchange_config)
print(f"Exchange Name: {exchange.name}")
print(f"Exchange Config in object: {list(exchange.config.keys())}")
markets = exchange.fetch_markets()
print(f"Fetched {len(markets)} markets.")
for m in markets:
print(f" - {m['symbol']}")
if __name__ == "__main__":
debug_fetch()

@ -1,15 +1,8 @@
"""
ICICI Breeze exchange integration.
"""
import asyncio
import logging
import os
from typing import Any, Dict, Optional
from typing import Any
import ccxt
import ccxt.async_support as ccxt_async
from freqtrade.exceptions import OperationalException
from freqtrade.exchange.common import MAP_EXCHANGE_CHILDCLASS
from freqtrade.exchange.exchange import Exchange
@ -59,7 +52,7 @@ class Icicibreeze(Exchange):
}
def _init_ccxt(
self, exchange_config: Dict[str, Any], sync: bool, ccxt_kwargs: Dict[str, Any]
self, exchange_config: dict[str, Any], sync: bool, ccxt_kwargs: dict[str, Any]
) -> Any:
# Determine Mode
mode = self._config.get("icici_mode") or exchange_config.get("icici_mode") or "stub"
@ -80,6 +73,9 @@ class Icicibreeze(Exchange):
exchange_config["key"] = exchange_config.get("key")
exchange_config["secret"] = exchange_config.get("secret")
exchange_config["dry_run"] = self._config.get("dry_run")
exchange_config["pair_whitelist"] = self._config.get("exchange", {}).get(
"pair_whitelist", []
)
if sync:
return BreezeCCXT(exchange_config)

@ -18,8 +18,8 @@ echo "--- 4. Ticker Smoke Test ---"
$PYTHON scripts/smoke_icicibreeze_ticker.py >/tmp/ticker.txt
echo "--- 5. Download Data Test (BTC & INR) ---"
$FREQTRADE download-data -c user_data/config_icicibreeze.json --userdir user_data --timeframes 5m --pairs BTC/USDT --days 2 -v >/tmp/dl_btc.txt
$FREQTRADE download-data -c user_data/config_icicibreeze.json --userdir user_data --timeframes 5m --pairs RELIANCE/INR --days 2 -v >/tmp/dl_inr.txt
$FREQTRADE download-data -c user_data/config_icicibreeze.json --userdir user_data --timeframes 5m --pairs BTC/USDT --days 2 -v >/tmp/dl_btc.txt 2>&1
$FREQTRADE download-data -c user_data/config_icicibreeze.json --userdir user_data --timeframes 5m --pairs RELIANCE/INR --days 2 -v >/tmp/dl_inr.txt 2>&1
echo "--- 6. Dry Run Trade Test ---"
# Start trade in background, redirecting both stdout and stderr to capture logs

@ -18,7 +18,8 @@
"pair_whitelist": [
"RELIANCE/INR",
"NIFTY-20260226-22000-CE/INR",
"NIFTY-20260226-FUT/INR"
"NIFTY-20260226-FUT/INR",
"BTC/USDT"
],
"pair_blacklist": [
"BNB/.*"

Loading…
Cancel
Save