Merge branch 'develop' into pr/samgermain/6558
This commit is contained in:
@@ -49,7 +49,8 @@ ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all"]
|
||||
ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column"]
|
||||
|
||||
ARGS_LIST_PAIRS = ["exchange", "print_list", "list_pairs_print_json", "print_one_column",
|
||||
"print_csv", "base_currencies", "quote_currencies", "list_pairs_all"]
|
||||
"print_csv", "base_currencies", "quote_currencies", "list_pairs_all",
|
||||
"trading_mode"]
|
||||
|
||||
ARGS_TEST_PAIRLIST = ["verbosity", "config", "quote_currencies", "print_one_column",
|
||||
"list_pairs_print_json", "exchange"]
|
||||
@@ -61,15 +62,17 @@ ARGS_BUILD_CONFIG = ["config"]
|
||||
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
|
||||
|
||||
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
|
||||
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes"]
|
||||
|
||||
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "exchange", "trading_mode",
|
||||
"candle_types"]
|
||||
|
||||
ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades"]
|
||||
|
||||
ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs"]
|
||||
ARGS_LIST_DATA = ["exchange", "dataformat_ohlcv", "pairs", "trading_mode"]
|
||||
|
||||
ARGS_DOWNLOAD_DATA = ["pairs", "pairs_file", "days", "new_pairs_days", "include_inactive",
|
||||
"timerange", "download_trades", "exchange", "timeframes",
|
||||
"erase", "dataformat_ohlcv", "dataformat_trades"]
|
||||
"erase", "dataformat_ohlcv", "dataformat_trades", "trading_mode"]
|
||||
|
||||
ARGS_PLOT_DATAFRAME = ["pairs", "indicators1", "indicators2", "plot_limit",
|
||||
"db_url", "trade_source", "export", "exportfilename",
|
||||
|
@@ -104,7 +104,7 @@ def ask_user_config() -> Dict[str, Any]:
|
||||
"type": "select",
|
||||
"name": "exchange_name",
|
||||
"message": "Select exchange",
|
||||
"choices": [
|
||||
"choices": lambda x: [
|
||||
"binance",
|
||||
"binanceus",
|
||||
"bittrex",
|
||||
@@ -114,10 +114,18 @@ def ask_user_config() -> Dict[str, Any]:
|
||||
"kraken",
|
||||
"kucoin",
|
||||
"okx",
|
||||
Separator(),
|
||||
Separator("------------------"),
|
||||
"other",
|
||||
],
|
||||
},
|
||||
{
|
||||
"type": "confirm",
|
||||
"name": "trading_mode",
|
||||
"message": "Do you want to trade Perpetual Swaps (perpetual futures)?",
|
||||
"default": False,
|
||||
"filter": lambda val: 'futures' if val else 'spot',
|
||||
"when": lambda x: x["exchange_name"] in ['binance', 'gateio', 'okx'],
|
||||
},
|
||||
{
|
||||
"type": "autocomplete",
|
||||
"name": "exchange_name",
|
||||
@@ -194,7 +202,13 @@ def ask_user_config() -> Dict[str, Any]:
|
||||
if not answers:
|
||||
# Interrupted questionary sessions return an empty dict.
|
||||
raise OperationalException("User interrupted interactive questions.")
|
||||
|
||||
# Ensure default is set for non-futures exchanges
|
||||
answers['trading_mode'] = answers.get('trading_mode', "spot")
|
||||
answers['margin_mode'] = (
|
||||
'isolated'
|
||||
if answers.get('trading_mode') == 'futures'
|
||||
else ''
|
||||
)
|
||||
# Force JWT token to be a random string
|
||||
answers['api_server_jwt_key'] = secrets.token_hex()
|
||||
|
||||
|
@@ -5,6 +5,7 @@ from argparse import SUPPRESS, ArgumentTypeError
|
||||
|
||||
from freqtrade import __version__, constants
|
||||
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN
|
||||
from freqtrade.enums import CandleType
|
||||
|
||||
|
||||
def check_int_positive(value: str) -> int:
|
||||
@@ -184,7 +185,6 @@ AVAILABLE_CLI_OPTIONS = {
|
||||
'--export',
|
||||
help='Export backtest results (default: trades).',
|
||||
choices=constants.EXPORT_OPTIONS,
|
||||
|
||||
),
|
||||
"exportfilename": Arg(
|
||||
"--export-filename",
|
||||
@@ -361,6 +361,17 @@ AVAILABLE_CLI_OPTIONS = {
|
||||
nargs='+',
|
||||
metavar='BASE_CURRENCY',
|
||||
),
|
||||
"trading_mode": Arg(
|
||||
'--trading-mode',
|
||||
help='Select Trading mode',
|
||||
choices=constants.TRADING_MODES,
|
||||
),
|
||||
"candle_types": Arg(
|
||||
'--candle-types',
|
||||
help='Select candle type to use',
|
||||
choices=[c.value for c in CandleType],
|
||||
nargs='+',
|
||||
),
|
||||
# Script options
|
||||
"pairs": Arg(
|
||||
'-p', '--pairs',
|
||||
|
@@ -8,7 +8,7 @@ from freqtrade.configuration import TimeRange, setup_utils_configuration
|
||||
from freqtrade.data.converter import convert_ohlcv_format, convert_trades_format
|
||||
from freqtrade.data.history import (convert_trades_to_ohlcv, refresh_backtest_ohlcv_data,
|
||||
refresh_backtest_trades_data)
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.enums import CandleType, RunMode, TradingMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
from freqtrade.exchange.exchange import market_is_active
|
||||
@@ -64,6 +64,8 @@ def start_download_data(args: Dict[str, Any]) -> None:
|
||||
try:
|
||||
|
||||
if config.get('download_trades'):
|
||||
if config.get('trading_mode') == 'futures':
|
||||
raise OperationalException("Trade download not supported for futures.")
|
||||
pairs_not_available = refresh_backtest_trades_data(
|
||||
exchange, pairs=expanded_pairs, datadir=config['datadir'],
|
||||
timerange=timerange, new_pairs_days=config['new_pairs_days'],
|
||||
@@ -81,7 +83,9 @@ def start_download_data(args: Dict[str, Any]) -> None:
|
||||
exchange, pairs=expanded_pairs, timeframes=config['timeframes'],
|
||||
datadir=config['datadir'], timerange=timerange,
|
||||
new_pairs_days=config['new_pairs_days'],
|
||||
erase=bool(config.get('erase')), data_format=config['dataformat_ohlcv'])
|
||||
erase=bool(config.get('erase')), data_format=config['dataformat_ohlcv'],
|
||||
trading_mode=config.get('trading_mode', 'spot'),
|
||||
)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
sys.exit("SIGINT received, aborting ...")
|
||||
@@ -133,9 +137,11 @@ def start_convert_data(args: Dict[str, Any], ohlcv: bool = True) -> None:
|
||||
"""
|
||||
config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE)
|
||||
if ohlcv:
|
||||
convert_ohlcv_format(config,
|
||||
convert_from=args['format_from'], convert_to=args['format_to'],
|
||||
erase=args['erase'])
|
||||
candle_types = [CandleType.from_string(ct) for ct in config.get('candle_types', ['spot'])]
|
||||
for candle_type in candle_types:
|
||||
convert_ohlcv_format(config,
|
||||
convert_from=args['format_from'], convert_to=args['format_to'],
|
||||
erase=args['erase'], candle_type=candle_type)
|
||||
else:
|
||||
convert_trades_format(config,
|
||||
convert_from=args['format_from'], convert_to=args['format_to'],
|
||||
@@ -154,17 +160,26 @@ def start_list_data(args: Dict[str, Any]) -> None:
|
||||
from freqtrade.data.history.idatahandler import get_datahandler
|
||||
dhc = get_datahandler(config['datadir'], config['dataformat_ohlcv'])
|
||||
|
||||
paircombs = dhc.ohlcv_get_available_data(config['datadir'])
|
||||
paircombs = dhc.ohlcv_get_available_data(
|
||||
config['datadir'],
|
||||
config.get('trading_mode', TradingMode.SPOT)
|
||||
)
|
||||
|
||||
if args['pairs']:
|
||||
paircombs = [comb for comb in paircombs if comb[0] in args['pairs']]
|
||||
|
||||
print(f"Found {len(paircombs)} pair / timeframe combinations.")
|
||||
groupedpair = defaultdict(list)
|
||||
for pair, timeframe in sorted(paircombs, key=lambda x: (x[0], timeframe_to_minutes(x[1]))):
|
||||
groupedpair[pair].append(timeframe)
|
||||
for pair, timeframe, candle_type in sorted(
|
||||
paircombs,
|
||||
key=lambda x: (x[0], timeframe_to_minutes(x[1]), x[2])
|
||||
):
|
||||
groupedpair[(pair, candle_type)].append(timeframe)
|
||||
|
||||
if groupedpair:
|
||||
print(tabulate([(pair, ', '.join(timeframes)) for pair, timeframes in groupedpair.items()],
|
||||
headers=("Pair", "Timeframe"),
|
||||
tablefmt='psql', stralign='right'))
|
||||
print(tabulate([
|
||||
(pair, ', '.join(timeframes), candle_type)
|
||||
for (pair, candle_type), timeframes in groupedpair.items()
|
||||
],
|
||||
headers=("Pair", "Timeframe", "Type"),
|
||||
tablefmt='psql', stralign='right'))
|
||||
|
@@ -132,7 +132,7 @@ def start_list_markets(args: Dict[str, Any], pairs_only: bool = False) -> None:
|
||||
try:
|
||||
pairs = exchange.get_markets(base_currencies=base_currencies,
|
||||
quote_currencies=quote_currencies,
|
||||
pairs_only=pairs_only,
|
||||
tradable_only=pairs_only,
|
||||
active_only=active_only)
|
||||
# Sort the pairs/markets by symbol
|
||||
pairs = dict(sorted(pairs.items()))
|
||||
@@ -152,15 +152,19 @@ def start_list_markets(args: Dict[str, Any], pairs_only: bool = False) -> None:
|
||||
if quote_currencies else ""))
|
||||
|
||||
headers = ["Id", "Symbol", "Base", "Quote", "Active",
|
||||
*(['Is pair'] if not pairs_only else [])]
|
||||
"Spot", "Margin", "Future", "Leverage"]
|
||||
|
||||
tabular_data = []
|
||||
for _, v in pairs.items():
|
||||
tabular_data.append({'Id': v['id'], 'Symbol': v['symbol'],
|
||||
'Base': v['base'], 'Quote': v['quote'],
|
||||
'Active': market_is_active(v),
|
||||
**({'Is pair': exchange.market_is_tradable(v)}
|
||||
if not pairs_only else {})})
|
||||
tabular_data = [{
|
||||
'Id': v['id'],
|
||||
'Symbol': v['symbol'],
|
||||
'Base': v['base'],
|
||||
'Quote': v['quote'],
|
||||
'Active': market_is_active(v),
|
||||
'Spot': 'Spot' if exchange.market_is_spot(v) else '',
|
||||
'Margin': 'Margin' if exchange.market_is_margin(v) else '',
|
||||
'Future': 'Future' if exchange.market_is_future(v) else '',
|
||||
'Leverage': exchange.get_max_leverage(v['symbol'], 20)
|
||||
} for _, v in pairs.items()]
|
||||
|
||||
if (args.get('print_one_column', False) or
|
||||
args.get('list_pairs_print_json', False) or
|
||||
|
Reference in New Issue
Block a user