sfccxt
Version:
A JavaScript / Python / PHP cryptocurrency trading library with support for 130+ exchanges
966 lines (871 loc) • 92.9 kB
JavaScript
// ---------------------------------------------------------------------------
// Usage: npm run transpile
// ---------------------------------------------------------------------------
"use strict";
const fs = require ('fs')
, { promisify } = require("util")
, log = require ('ololog').unlimited
, _ = require ('ansicolor').nice
, errors = require ('../js/base/errors.js')
, functions = require ('../js/base/functions.js')
, { fork } = require ('child_process')
, os = require ('os')
, {
unCamelCase,
precisionConstants,
safeString,
} = functions
, { basename } = require ('path')
, {
createFolderRecursively,
replaceInFile,
overwriteFile,
} = require ('./fs.js')
, baseExchangeJsFile = './js/base/Exchange.js'
, Exchange = require ('.' + baseExchangeJsFile)
, tsFilename = './ccxt.d.ts'
, pythonCodingUtf8 = '# -*- coding: utf-8 -*-'
const exchangeIds = require("../exchanges.json").ids;
class Transpiler {
getCommonRegexes () {
return [
[ /\.deepExtend\s/g, '.deep_extend'],
[ /\.safeFloat2\s/g, '.safe_float_2'],
[ /\.safeInteger2\s/g, '.safe_integer_2'],
[ /\.safeIntegerProduct2\s/g, '.safe_integer_product_2'],
[ /\.safeTimestamp2\s/g, '.safe_timestamp_2'],
[ /\.safeString2\s/g, '.safe_string_2'],
[ /\.safeNumber2\s/g, '.safe_number_2'],
[ /\.safeStringLower2\s/g, '.safe_string_lower_2'],
[ /\.safeStringUpper2\s/g, '.safe_string_upper_2'],
[ /\.safeValue2\s/g, '.safe_value_2'],
[ /\.safeNumber\s/g, '.safe_number'],
[ /\.safeFloat\s/g, '.safe_float'],
[ /\.safeInteger\s/g, '.safe_integer'],
[ /\.safeIntegerProduct\s/g, '.safe_integer_product'],
[ /\.safeTimestamp\s/g, '.safe_timestamp'],
[ /\.safeString\s/g, '.safe_string'],
[ /\.safeStringLower\s/g, '.safe_string_lower'],
[ /\.safeStringUpper\s/g, '.safe_string_upper'],
[ /\.safeValue\s/g, '.safe_value'],
[ /\.safeFloatN\s/g, '.safe_float_n'],
[ /\.safeIntegerN\s/g, '.safe_integer_n'],
[ /\.safeIntegerProductN\s/g, '.safe_integer_product_n'],
[ /\.safeTimestampN\s/g, '.safe_timestamp_n'],
[ /\.safeStringN\s/g, '.safe_string_n'],
[ /\.safeNumberN\s/g, '.safe_number_n'],
[ /\.safeStringLowerN\s/g, '.safe_string_lower_n'],
[ /\.safeStringUpperN\s/g, '.safe_string_upper_n'],
[ /\.safeValueN\s/g, '.safe_value_n'],
[ /\.inArray\s/g, '.in_array'],
[ /\.toArray\s/g, '.to_array'],
[ /\.isEmpty\s/g, '.is_empty'],
[ /\.arrayConcat\s/g, '.array_concat'],
[ /\.binaryConcat\s/g, '.binary_concat'],
[ /\.binaryConcatArray\s/g, '.binary_concat_array'],
[ /\.binaryToString\s/g, '.binary_to_string' ],
[ /\.precisionFromString\s/g, '.precision_from_string'],
[ /\.parsePrecision\s/g, '.parse_precision'],
[ /\.parseNumber\s/g, '.parse_number'],
[ /\.implodeHostname\s/g, '.implode_hostname'],
[ /\.implodeParams\s/g, '.implode_params'],
[ /\.extractParams\s/g, '.extract_params'],
[ /\.safeBalance\s/g, '.safe_balance'],
[ /\.parseAccounts\s/g, '.parse_accounts' ],
[ /\.parseAccount\s/g, '.parse_account' ],
[ /\.parseBalance\s/g, '.parse_balance'],
[ /\.parseBorrowInterest\s/g, '.parse_borrow_interest'],
[ /\.parseFundingRateHistories\s/g, '.parse_funding_rate_histories'],
[ /\.parseFundingRateHistory\s/g, '.parse_funding_rate_history'],
[ /\.parseOHLCVs\s/g, '.parse_ohlcvs'],
[ /\.parseOHLCV\s/g, '.parse_ohlcv'],
[ /\.parseDate\s/g, '.parse_date'],
[ /\.parseDepositAddresses\s/g, '.parse_deposit_addresses'],
[ /\.parseDepositAddress\s/g, '.parse_deposit_address'],
[ /\.parseMarketLeverageTiers\s/g, '.parse_market_leverage_tiers'],
[ /\.parseLeverageTiers\s/g, '.parse_leverage_tiers'],
[ /\.parseLedgerEntry\s/g, '.parse_ledger_entry'],
[ /\.parseLedger\s/g, '.parse_ledger'],
[ /\.parseTickers\s/g, '.parse_tickers'],
[ /\.parseTicker\s/g, '.parse_ticker'],
[ /\.parseTimeframe\s/g, '.parse_timeframe'],
[ /\.parseTradesData\s/g, '.parse_trades_data'],
[ /\.parseTrades\s/g, '.parse_trades'],
[ /\.parseTrade\s/g, '.parse_trade'],
[ /\.parseTradingFees\s/g, '.parse_trading_fees'],
[ /\.parseTradingFee\s/g, '.parse_trading_fee'],
[ /\.parseTradingViewOHLCV\s/g, '.parse_trading_view_ohlcv'],
[ /\.convertTradingViewToOHLCV\s/g, '.convert_trading_view_to_ohlcv'],
[ /\.parseTransactions\s/g, '.parse_transactions'],
[ /\.parseTransaction\s/g, '.parse_transaction'],
[ /\.parseTransfers\s/g, '.parse_transfers'],
[ /\.parseTransfer\s/g, '.parse_transfer'],
[ /\.parseOrderBook\s/g, '.parse_order_book'],
[ /\.parseBidsAsks\s/g, '.parse_bids_asks'],
[ /\.parseBidAsk\s/g, '.parse_bid_ask'],
[ /\.parseOpenInterests\s/g, '.parse_open_interests'],
[ /\.parseOpenInterest\s/g, '.parse_open_interest'],
[ /\.parseBidAsk\s/g, '.parse_bid_ask'],
[ /\.parseOrders\s/g, '.parse_orders'],
[ /\.parseOrderStatus\s/g, '.parse_order_status'],
[ /\.parseOrder\s/g, '.parse_order'],
[ /\.parseJson\s/g, '.parse_json'],
[ /\.parseAccountPosition\s/g, '.parse_account_position' ],
[ /\.parsePositionRisk\s/g, '.parse_position_risk' ],
[ /\.parsePositions\s/g, '.parse_positions' ],
[ /\.parsePosition\s/g, '.parse_position' ],
[ /\.parseIncome\s/g, '.parse_income' ],
[ /\.parseIncomes\s/g, '.parse_incomes' ],
[ /\.parseFundingRates\s/g, '.parse_funding_rates' ],
[ /\.parseFundingRate\s/g, '.parse_funding_rate' ],
[ /\.parseMarginModification\s/g, '.parse_margin_modification' ],
[ /\.filterByArray\s/g, '.filter_by_array'],
[ /\.filterByValueSinceLimit\s/g, '.filter_by_value_since_limit'],
[ /\.filterBySymbolSinceLimit\s/g, '.filter_by_symbol_since_limit'],
[ /\.filterByCurrencySinceLimit\s/g, '.filter_by_currency_since_limit'],
[ /\.filterBySinceLimit\s/g, '.filter_by_since_limit'],
[ /\.filterBySymbol\s/g, '.filter_by_symbol'],
[ /\.getVersionString\s/g, '.get_version_string'],
[ /\.indexBy\s/g, '.index_by'],
[ /\.sortBy\s/g, '.sort_by'],
[ /\.sortBy2\s/g, '.sort_by_2'],
[ /\.filterBy\s/g, '.filter_by'],
[ /\.groupBy\s/g, '.group_by'],
[ /\.marketSymbols\s/g, '.market_symbols'],
[ /\.marketIds\s/g, '.market_ids'],
[ /\.marketId\s/g, '.market_id'],
[ /\.fetchFundingFee\s/g, '.fetch_funding_fee'],
[ /\.fetchFundingFees\s/g, '.fetch_funding_fees'],
[ /\.fetchTradingLimits\s/g, '.fetch_trading_limits'],
[ /\.fetchTransactionFee\s/g, '.fetch_transaction_fee'],
[ /\.fetchTransactionFees\s/g, '.fetch_transaction_fees'],
[ /\.fetchTradingFees\s/g, '.fetch_trading_fees'],
[ /\.fetchTradingFee\s/g, '.fetch_trading_fee'],
[ /\.fetchFees\s/g, '.fetch_fees'],
[ /\.fetchOHLCVC\s/g, '.fetch_ohlcvc'],
[ /\.fetchOHLCV\s/g, '.fetch_ohlcv'],
[ /\.buildOHLCVC\s/g, '.build_ohlcvc'],
[ /\.fetchL2OrderBook\s/g, '.fetch_l2_order_book'],
[ /\.fetchOrderBook\s/g, '.fetch_order_book'],
[ /\.fetchMyTrades\s/g, '.fetch_my_trades'],
[ /\.fetchOrderStatus\s/g, '.fetch_order_status'],
[ /\.fetchOpenOrders\s/g, '.fetch_open_orders'],
[ /\.fetchOpenOrder\s/g, '.fetch_open_order'],
[ /\.fetchOrders\s/g, '.fetch_orders'],
[ /\.fetchOrderTrades\s/g, '.fetch_order_trades'],
[ /\.fetchOrder\s/g, '.fetch_order'],
[ /\.fetchBalance\s/g, '.fetch_balance'],
[ /\.fetchTotalBalance\s/g, '.fetch_total_balance'],
[ /\.fetchUsedBalance\s/g, '.fetch_used_balance'],
[ /\.fetchFreeBalance\s/g, '.fetch_free_balance'],
[ /\.fetchPartialBalance\s/g, '.fetch_partial_balance'],
[ /\.fetchPermissions\s/g, '.fetch_permissions'],
[ /\.fetchBidsAsks\s/g, '.fetch_bids_asks'],
[ /\.fetchTickers\s/g, '.fetch_tickers'],
[ /\.fetchTicker\s/g, '.fetch_ticker'],
[ /\.fetchCurrencies\s/g, '.fetch_currencies'],
[ /\.fetchStatus\s/g, '.fetch_status'],
[ /\.numberToString\s/g, '.number_to_string' ],
[ /\.decimalToPrecision\s/g, '.decimal_to_precision'],
[ /\.priceToPrecision\s/g, '.price_to_precision'],
[ /\.amountToPrecision\s/g, '.amount_to_precision'],
[ /\.amountToLots\s/g, '.amount_to_lots'],
[ /\.feeToPrecision\s/g, '.fee_to_precision'],
[ /\.currencyToPrecision\s/g, '.currency_to_precision'],
[ /\.costToPrecision\s/g, '.cost_to_precision'],
[ /\.commonCurrencyCode\s/g, '.common_currency_code'],
[ /\.getDefaultOptions\s/g, '.get_default_options'],
[ /\.loadAccounts\s/g, '.load_accounts'],
[ /\.fetchAccounts\s/g, '.fetch_accounts'],
[ /\.loadFees\s/g, '.load_fees'],
[ /\.loadMarkets\s/g, '.load_markets'],
[ /\.loadTimeDifference\s/g, '.load_time_difference'],
[ /\.fetchMarkets\s/g, '.fetch_markets'],
[ /\.fetchMarketLeverageTiers\s/g, '.fetch_market_leverage_tiers'],
[ /\.fetchLeverageTiers\s/g, '.fetch_leverage_tiers'],
[ /\.appendInactiveMarkets\s/g, '.append_inactive_markets'],
[ /\.fetchCategories\s/g, '.fetch_categories'],
[ /\.calculateFee\s/g, '.calculate_fee'],
[ /\.createOrder\s/g, '.create_order'],
[ /\.createPostOnlyOrder\s/g, '.create_post_only_order'],
[ /\.createStopOrder\s/g, '.create_stop_order'],
[ /\.createStopLimitOrder\s/g, '.create_stop_limit_order'],
[ /\.createStopMarketOrder\s/g, '.create_stop_market_order'],
[ /\.editLimitBuyOrder\s/g, '.edit_limit_buy_order'],
[ /\.editLimitSellOrder\s/g, '.edit_limit_sell_order'],
[ /\.editLimitOrder\s/g, '.edit_limit_order'],
[ /\.editOrder\s/g, '.edit_order'],
[ /\.encodeURIComponent\s/g, '.encode_uri_component'],
[ /\.throwExceptionOnError\s/g, '.throw_exception_on_error'],
[ /\.handleErrors\s/g, '.handle_errors'],
[ /\.handleWithdrawTagAndParams\s/g, '.handle_withdraw_tag_and_params'],
[ /\.checkRequiredCredentials\s/g, '.check_required_credentials'],
[ /\.checkRequiredDependencies\s/g, '.check_required_dependencies'],
[ /\.checkAddress\s/g, '.check_address'],
[ /\.convertTradingViewToOHLCV\s/g, '.convert_trading_view_to_ohlcv'],
[ /\.convertOHLCVToTradingView\s/g, '.convert_ohlcv_to_trading_view'],
[ /\.signBodyWithSecret\s/g, '.sign_body_with_secret'],
[ /\.isJsonEncodedObject\s/g, '.is_json_encoded_object'],
[ /\.setSandboxMode\s/g, '.set_sandbox_mode'],
[ /\.safeCurrencyCode\s/g, '.safe_currency_code'],
[ /\.safeCurrency\s/g, '.safe_currency'],
[ /\.safeSymbol\s/g, '.safe_symbol'],
[ /\.safeMarket\s/g, '.safe_market'],
[ /\.safeOrder\s/g, '.safe_order'],
[ /\.safeTicker\s/g, '.safe_ticker'],
[ /\.roundTimeframe\s/g, '.round_timeframe'],
[ /\.calculateRateLimiterCost\s/g, '.calculate_rate_limiter_cost' ],
[ /\.findBroadlyMatchedKey\s/g, '.find_broadly_matched_key' ],
[ /\.throwBroadlyMatchedException\s/g, '.throw_broadly_matched_exception' ],
[ /\.throwExactlyMatchedException\s/g, '.throw_exactly_matched_exception' ],
[ /\.getNetwork\s/g, '.get_network' ],
[ /\.findTimeframe\s/g, '.find_timeframe'],
[ /errorHierarchy/g, 'error_hierarchy'],
[ /\.base16ToBinary/g, '.base16_to_binary'],
[ /\'use strict\';?\s+/g, '' ],
[ /\.urlencodeNested\s/g, '.urlencode_nested' ],
[ /\.urlencodeWithArrayRepeat\s/g, '.urlencode_with_array_repeat' ],
[ /\.call\s*\(this, /g, '(' ],
[ /\.getSupportedMapping\s/g, '.get_supported_mapping'],
[ /\.fetchBorrowRates\s/g, '.fetch_borrow_rates'],
[ /\.fetchBorrowRate\s/g, '.fetch_borrow_rate'],
[ /\.handleMarketTypeAndParams\s/g, '.handle_market_type_and_params'],
[ /\.checkOrderArguments\s/g, '.check_order_arguments'],
[ /\.isPostOnly\s/g, '.is_post_only'],
[ /\.reduceFeesByCurrency\s/g, '.reduce_fees_by_currency'],
[ /\.omitZero\s/g, '.omit_zero'],
]
}
getPythonRegexes () {
return [
[ /Array\.isArray\s*\(([^\)]+)\)/g, 'isinstance($1, list)' ],
[ /([^\(\s]+)\s+instanceof\s+String/g, 'isinstance($1, str)' ],
[ /([^\(\s]+)\s+instanceof\s+([^\)\s]+)/g, 'isinstance($1, $2)' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'undefined\'/g, '$1[$2] is None' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'undefined\'/g, '$1[$2] is not None' ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'undefined\'/g, '$1 is None' ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'undefined\'/g, '$1 is not None' ],
[ /typeof\s+(.+?)\s+\=\=\=?\s+\'undefined\'/g, '$1 is None' ],
[ /typeof\s+(.+?)\s+\!\=\=?\s+\'undefined\'/g, '$1 is not None' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'number\'/g, "isinstance($1[$2], numbers.Real)" ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'number\'/g, "(not isinstance($1[$2], numbers.Real))" ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'number\'/g, "isinstance($1, numbers.Real)" ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'number\'/g, "(not isinstance($1, numbers.Real))" ],
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+undefined/g, '$1[$2] is None' ],
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+undefined/g, '$1[$2] is not None' ],
[ /([^\s]+)\s+\=\=\=?\s+undefined/g, '$1 is None' ],
[ /([^\s]+)\s+\!\=\=?\s+undefined/g, '$1 is not None' ],
[ /(.+?)\s+\=\=\=?\s+undefined/g, '$1 is None' ],
[ /(.+?)\s+\!\=\=?\s+undefined/g, '$1 is not None' ],
//
// too broad, have to rewrite these cause they don't work
//
// [ /([^\s]+)\s+\=\=\=?\s+true/g, 'isinstance($1, bool) and ($1 is True)' ],
// [ /([^\s]+)\s+\!\=\=?\s+true/g, 'isinstance($1, bool) and ($1 is not True)' ],
// [ /([^\s]+)\s+\=\=\=?\s+false/g, 'isinstance($1, bool) and ($1 is False)' ],
// [ /([^\s]+)\s+\!\=\=?\s+false/g, 'isinstance($1, bool) and ($1 is not False)' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'string\'/g, 'isinstance($1[$2], str)' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'string\'/g, 'not isinstance($1[$2], str)' ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'string\'/g, 'isinstance($1, str)' ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'string\'/g, 'not isinstance($1, str)' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'object\'/g, 'isinstance($1[$2], dict)' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'object\'/g, 'not isinstance($1[$2], dict)' ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'object\'/g, 'isinstance($1, dict)' ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'object\'/g, 'not isinstance($1, dict)' ],
[ /undefined/g, 'None' ],
[ /\=\=\=?/g, '==' ],
[ /\!\=\=?/g, '!=' ],
[ /this\.stringToBinary\s*\((.*)\)/g, '$1' ],
[ /\.shift\s*\(\)/g, '.pop(0)' ],
[ /Number\.MAX_SAFE_INTEGER/g, 'float(\'inf\')'],
[ /function\s*(\w+\s*\([^)]+\))\s*{/g, 'def $1:'],
// [ /\.replaceAll\s*\(([^)]+)\)/g, '.replace($1)' ], // still not a part of the standard
[ /assert\s*\((.+)\);/g, 'assert $1'],
[ /Promise\.all\s*\(([^\)]+)\)/g, 'asyncio.gather(*$1)' ],
[ /Precise\.stringAdd\s/g, 'Precise.string_add' ],
[ /Precise\.stringMul\s/g, 'Precise.string_mul' ],
[ /Precise\.stringDiv\s/g, 'Precise.string_div' ],
[ /Precise\.stringSub\s/g, 'Precise.string_sub' ],
[ /Precise\.stringAbs\s/g, 'Precise.string_abs' ],
[ /Precise\.stringNeg\s/g, 'Precise.string_neg' ],
[ /Precise\.stringMod\s/g, 'Precise.string_mod' ],
[ /Precise\.stringEquals\s/g, 'Precise.string_equals' ],
[ /Precise\.stringEq\s/g, 'Precise.string_eq' ],
[ /Precise\.stringMin\s/g, 'Precise.string_min' ],
[ /Precise\.stringMax\s/g, 'Precise.string_max' ],
[ /Precise\.stringGt\s/g, 'Precise.string_gt' ],
[ /Precise\.stringGe\s/g, 'Precise.string_ge' ],
[ /Precise\.stringLt\s/g, 'Precise.string_lt' ],
[ /Precise\.stringLe\s/g, 'Precise.string_le' ],
[ /\.padEnd\s/g, '.ljust'],
[ /\.padStart\s/g, '.rjust' ],
// insert common regexes in the middle (critical)
].concat (this.getCommonRegexes ()).concat ([
// [ /this\.urlencode\s/g, '_urlencode.urlencode ' ], // use self.urlencode instead
[ /this\./g, 'self.' ],
[ /([^a-zA-Z\'])this([^a-zA-Z])/g, '$1self$2' ],
[ /\[\s*([^\]]+)\s\]\s=/g, '$1 =' ],
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\[\s*([^\]]+)\s\]/g, '$1$2' ],
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\{\s*([^\}]+)\s\}\s\=\s([^\;]+)/g, '$1$2 = (lambda $2: ($2))(**$3)' ],
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ],
[ /Object\.keys\s*\((.*)\)\.length/g, '$1' ],
[ /Object\.keys\s*\((.*)\)/g, 'list($1.keys())' ],
[ /Object\.values\s*\((.*)\)/g, 'list($1.values())' ],
[ /\[([^\]]+)\]\.join\s*\(([^\)]+)\)/g, "$2.join([$1])" ],
[ /hash \(([^,]+)\, \'(sha[0-9])\'/g, "hash($1, '$2'" ],
[ /hmac \(([^,]+)\, ([^,]+)\, \'(md5)\'/g, 'hmac($1, $2, hashlib.$3' ],
[ /hmac \(([^,]+)\, ([^,]+)\, \'(sha[0-9]+)\'/g, 'hmac($1, $2, hashlib.$3' ],
[ /throw new ([\S]+) \((.*)\)/g, 'raise $1($2)'],
[ /throw ([\S]+)/g, 'raise $1'],
[ /try {/g, 'try:'],
[ /\}\s+catch \(([\S]+)\) {/g, 'except Exception as $1:'],
[ /([\s\(])extend(\s)/g, '$1self.extend$2' ],
[ /\} else if/g, 'elif' ],
[ /else if/g, 'elif' ],
[ /if\s+\((.*)\)\s+\{/g, 'if $1:' ],
[ /if\s+\((.*)\)\s*[\n]/g, "if $1:\n" ],
[ /\}\s*else\s*\{/g, 'else:' ],
[ /else\s*[\n]/g, "else:\n" ],
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(?:\<=|\>=|<|>)\s*(.*)\.length\s*\;[^\)]+\)\s*{/g, 'for $1 in range($2, len($3)):'],
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(?:\<=|\>=|<|>)\s*(.*)\s*\;[^\)]+\)\s*{/g, 'for $1 in range($2, $3):'],
[ /\s\|\|\s/g, ' or ' ],
[ /\s\&\&\s/g, ' and ' ],
[ /\!([^\s\='"])/g, 'not $1'],
[ /\.push\s*\(([\s\S]+?)\);/g, '.append($1);' ],
[ /^(\s*}\s*$)+/gm, '' ],
[ /\;(\s+?\/\/.+?)/g, '$1' ],
[ /\;$/gm, '' ],
[ /\.toUpperCase\s*/g, '.upper' ],
[ /\.toLowerCase\s*/g, '.lower' ],
[ /(\b)String(\b)/g, '$1str$2'],
[ /JSON\.stringify\s*/g, 'json.dumps' ],
[ /JSON\.parse\s*/g, "json.loads" ],
// [ /([^\(\s]+)\.includes\s+\(([^\)]+)\)/g, '$2 in $1' ],
// [ /\'%([^\']+)\'\.sprintf\s*\(([^\)]+)\)/g, "'{:$1}'.format($2)" ],
[ /([^\s]+)\.toFixed\s*\(([0-9]+)\)/g, "format($1, '.$2f')" ],
[ /([^\s]+)\.toFixed\s*\(([^\)]+)\)/g, "format($1, '.' + str($2) + 'f')" ],
[ /parseFloat\s*/g, 'float'],
[ /parseInt\s*/g, 'int'],
[ /self\[([^\]+]+)\]/g, 'getattr(self, $1)' ],
[ /Math\.floor\s*\(([^\)]+)\)/g, 'int(math.floor($1))' ],
[ /Math\.abs\s*\(([^\)]+)\)/g, 'abs($1)' ],
[ /Math\.pow\s*\(([^\)]+)\)/g, 'math.pow($1)' ],
[ /Math\.round\s*\(([^\)]+)\)/g, 'int(round($1))' ],
[ /Math\.ceil\s*\(([^\)]+)\)/g, 'int(math.ceil($1))' ],
[ /Math\.log/g, 'math.log' ],
[ /([a-zA-Z0-9_\.]*\([^\)]+\)|[^\s]+)\s+\?\s*([^\:]+)\s+\:\s*([^\n]+)/g, '$2 if $1 else $3'],
[ /([^\s]+)\.slice \(([^\,\)]+)\,\s?([^\)]+)\)/g, '$1[$2:$3]' ],
[ /([^\s]+)\.slice \(([^\)\:]+)\)/g, '$1[$2:]' ],
[ /([^\s(:]+)\.length/g, 'len($1)' ],
[ /(^|\s)\/\//g, '$1#' ],
[ /([^\n\s]) #/g, '$1 #' ], // PEP8 E261
[ /\.indexOf/g, '.find'],
[ /(\s|\()true/g, '$1True'],
[ /(\s|\()false/g, '$1False'],
[ /([^\s]+\s*\(\))\.toString\s+\(\)/g, 'str($1)' ],
[ /([^\s]+)\.toString \(\)/g, 'str($1)' ],
[ /([^\s]+)\.join\s*\(\s*([^\)\[\]]+?)\s*\)/g, '$2.join($1)' ],
[ /Math\.(max|min)\s/g, '$1' ],
[ / = new /g, ' = ' ], // python does not have a 'new' keyword
[ /console\.log\s/g, 'print' ],
[ /process\.exit\s+/g, 'sys.exit' ],
[ /(while \(.*\)) {/, '$1\:' ], // While loops replace bracket with :
[ /([^:+=\/\*\s-]+) \(/g, '$1(' ], // PEP8 E225 remove whitespaces before left ( round bracket
[ /\sand\(/g, ' and (' ],
[ /\sor\(/g, ' or (' ],
[ /\snot\(/g, ' not (' ],
[ /\[ /g, '[' ], // PEP8 E201 remove whitespaces after left [ square bracket
[ /\{ /g, '{' ], // PEP8 E201 remove whitespaces after left { bracket
[ /(?<=[^\s#]) \]/g, ']' ], // PEP8 E202 remove whitespaces before right ] square bracket
[ /(?<=[^\s#]) \}/g, '}' ], // PEP8 E202 remove whitespaces before right } bracket
[ /([^a-z])(elif|if|or|else)\(/g, '$1$2 \(' ], // a correction for PEP8 E225 side-effect for compound and ternary conditionals
[ /\!\=\sTrue/g, 'is not True' ], // a correction for PEP8 E712, it likes "is not True", not "!= True"
[ /\=\=\sTrue/g, 'is True' ], // a correction for PEP8 E712, it likes "is True", not "== True"
[ /\sdelete\s/g, ' del ' ],
[ /(?<!#.+)null/, 'None' ],
[ /\/\*\*/, '\"\"\"' ], // Doc strings
[ / \*\//, '\"\"\"' ], // Doc strings
[ /\[([^\[\]]*)\]\{@link (.*)\}/g, '`$1 <$2>`' ], // docstring item with link
[ /\s+\* @method/g, '' ], // docstring @method
[ /(\s+) \* @description (.*)/g, '$1$2' ], // docstring description
[ /\s+\* @name .*/g, '' ], // docstring @name
[ /(\s+) \* @see( .*)/g, '$1see$2' ], // docstring @see
[ /(\s+ \* @(param|returns) {[^}]*)string([^}]*}.*)/g, '$1str$3' ], // docstring type conversion
[ /(\s+ \* @(param|returns) {[^}]*)object([^}]*}.*)/g, '$1dict$3' ], // doctstrubg type conversion
[ /(\s+) \* @returns ([^\{])/g, '$1:returns: $2' ], // docstring return
[ /(\s+) \* @returns \{(.+)\}/g, '$1:returns $2:' ], // docstring return
[ /(\s+ \* @param \{[\]\[\|a-zA-Z]+\} )([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+) (.*)/g, '$1$2[\'$3\'] $4' ], // docstring params.anything
[ /(\s+) \* @([a-z]+) \{([\]\[a-zA-Z\|]+)\} ([a-zA-Z0-9_\-\.\[\]\']+)/g, '$1:$2 $3 $4:' ], // docstring param
])
}
getPython2Regexes () {
return [
[ /.+asyncio\.gather.+\n/g, '' ], // remove line entirely
[ /(\s)await(\s)/g, '$1' ]
]
}
getPHPSyncRegexes () {
return [
// delete await, the following regex does not pick up multiline await calls
[ /\bAsync\\await\((.+)\);/g, '$1;' ],
// hence the following regex is added with a dotAll modifier 's'
// and a non greedy match for the calls not picked up by the previous regex
[ /\bAsync\\await\((.+?)\);/gs, '$1;' ],
[ /.+Promise\\all.+\n/g, '' ], // remove line entirely
[ /\byield(?: from)?\s+/g, '' ], // delete yield from
]
}
getPHPRegexes () {
return [
//
// Curly-braces are used for both dictionaries in the code as well as for the url-imploded params.
// For example: https://docs.ccxt.com/en/latest/manual.html#implicit-api-methods
//
// There's a conflict between the curly braces that have to be converted from dictionaries to PHP-arrays and
// the curly braces used for url-imploded params that should not be touched.
//
// The transpiler takes all non-spaced strings in curly braces {likeThis} and converts them to ~likeThis~.
// That is done to avoid changing the curly braces into the array() in PHP.
// This way we protect the url-imploded params from being touched by the regexes that will follow.
// That conversion is done first-thing, at the very early stage of transpilation.
// The regexes are applied in the order they're listed, top-down.
//
// A dictionary in curly braces will never have those curly braces attached to the contents of the dictionary.
// There will always be a space like { 'a': b, 'c': d }.
// Hence, the remaining non-converted curly-brace dictionaries will have to be converted to arrays in PHP.
// That is done in the middle of the transpilation process.
//
// The last step is to convert those "saved embedded/imploded url-params substitutions" from ~likeThis~ back to {likeThis}.
// That is done at the very last regex steps.
// All of that is a workaround for PHP-arrays vs dictionaries vs url-imploded params in other langs.
//
[ /\{([\]\[\|a-zA-Z0-9_-]+?)\}/g, '~$1~' ], // resolve the "arrays vs url params" conflict (both are in {}-brackets)
[ /\[([^\]\[]*)\]\{(@link .*)\}/g, '~$2 $1~' ], // docstring item with link
[ /\s+\* @method/g, '' ], // docstring @method
[ /(\s+)\* @description (.*)/g, '$1\* $2' ], // docstring description
[ /\s+\* @name .*/g, '' ], // docstring @name
[ /(\s+)\* @returns/g, '$1\* @return' ], // docstring return
[ /\!Array\.isArray\s*\(([^\)]+)\)/g, "gettype($1) !== 'array' || array_keys($1) !== array_keys(array_keys($1))" ],
[ /Array\.isArray\s*\(([^\)]+)\)/g, "gettype($1) === 'array' && array_keys($1) === array_keys(array_keys($1))" ],
[ /([^\(\s]+)\s+instanceof\s+String/g, 'is_string($1)' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'undefined\'/g, '$1[$2] === null' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'undefined\'/g, '$1[$2] !== null' ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'undefined\'/g, '$1 === null' ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'undefined\'/g, '$1 !== null' ],
[ /typeof\s+(.+?)\s+\=\=\=?\s+\'undefined\'/g, '$1 === null' ],
[ /typeof\s+(.+?)\s+\!\=\=?\s+\'undefined\'/g, '$1 !== null' ],
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+undefined/g, '$1[$2] === null' ],
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+undefined/g, '$1[$2] !== null' ],
[ /([^\s]+)\s+\=\=\=?\s+undefined/g, '$1 === null' ],
[ /([^\s]+)\s+\!\=\=?\s+undefined/g, '$1 !== null' ],
[ /(.+?)\s+\=\=\=?\s+undefined/g, '$1 === null' ],
[ /(.+?)\s+\!\=\=?\s+undefined/g, '$1 !== null' ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'string\'/g, "gettype($1[$2]) === 'string'" ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'string\'/g, "gettype($1[$2]) !== 'string'" ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'string\'/g, "gettype($1) === 'string'" ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'string\'/g, "gettype($1) !== 'string'" ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'object\'/g, "gettype($1[$2]) === 'array'" ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'object\'/g, "gettype($1[$2]) !== 'array'" ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'object\'/g, "gettype($1) === 'array'" ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'object\'/g, "gettype($1) !== 'array'" ],
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'number\'/g, "(is_float($1[$2]) || is_int($1[$2]))" ], // same as above but for number
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'number\'/g, "!(is_float($1[$2]) || is_int($1[$2]))" ],
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'number\'/g, "(is_float($1) || is_int($1))" ],
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'number\'/g, "!(is_float($1) || is_int($1))" ],
[ /undefined/g, 'null' ],
[ /\} else if/g, '} elseif' ],
[ /this\.extend\s/g, 'array_merge' ],
[ /this\.stringToBinary\s*\((.*)\)/g, '$1' ],
[ /this\.stringToBase64\s/g, 'base64_encode' ],
[ /this\.binaryToBase16\s/g, 'bin2hex' ],
[ /this\.base64ToBinary\s/g, 'base64_decode' ],
[ /this\.base64ToString\s/g, 'base64_decode' ],
[ /Promise\.all\s*\(([^\)]+)\)/g, 'Promise\\all($1)' ],
// deepExtend is commented for PHP because it does not overwrite linear arrays
// a proper \ccxt\Exchange::deep_extend() base method is implemented instead
// [ /this\.deepExtend\s/g, 'array_replace_recursive'],
[ /(\w+)\.shift\s*\(\)/g, 'array_shift($1)' ],
[ /(\w+)\.pop\s*\(\)/g, 'array_pop($1)' ],
[ /Number\.MAX_SAFE_INTEGER/g, 'PHP_INT_MAX' ],
[ /Precise\.stringAdd\s/g, 'Precise::string_add' ],
[ /Precise\.stringDiv\s/g, 'Precise::string_div' ],
[ /Precise\.stringMul\s/g, 'Precise::string_mul' ],
[ /Precise\.stringSub\s/g, 'Precise::string_sub' ],
[ /Precise\.stringAbs\s/g, 'Precise::string_abs' ],
[ /Precise\.stringNeg\s/g, 'Precise::string_neg' ],
[ /Precise\.stringMod\s/g, 'Precise::string_mod' ],
[ /Precise\.stringEquals\s/g, 'Precise::string_equals' ],
[ /Precise\.stringEq\s/g, 'Precise::string_eq' ],
[ /Precise\.stringMin\s/g, 'Precise::string_min' ],
[ /Precise\.stringMax\s/g, 'Precise::string_max' ],
[ /Precise\.stringGt\s/g, 'Precise::string_gt' ],
[ /Precise\.stringGe\s/g, 'Precise::string_ge' ],
[ /Precise\.stringLt\s/g, 'Precise::string_lt' ],
[ /Precise\.stringLe\s/g, 'Precise::string_le' ],
[ /(\w+)\.padEnd\s*\(([^,]+),\s*([^)]+)\)/g, 'str_pad($1, $2, $3, STR_PAD_RIGHT)' ],
[ /(\w+)\.padStart\s*\(([^,]+),\s*([^)]+)\)/g, 'str_pad($1, $2, $3, STR_PAD_LEFT)' ],
// insert common regexes in the middle (critical)
].concat (this.getCommonRegexes ()).concat ([
[ /this\./g, '$this->' ],
[ / this;/g, ' $this;' ],
[ /([^'])this_\./g, '$1$this_->' ],
[ /([^'])\{\}/g, '$1array()' ],
[ /([^'])\[\]/g, '$1array()' ],
// add {}-array syntax conversions up to 20 levels deep on the same line
]).concat ([ ... Array (20) ].map (x => [ /\{([^\n\}]+)\}/g, 'array($1)' ] )).concat ([
[ /\[\s*([^\]]+)\s\]\s=/g, 'list($1) =' ],
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\[\s*([^\]]+)\s\]/g, '$1list($2)' ],
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\{\s*([^\}]+)\s\}/g, '$1array_values(list($2))' ],
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ],
[ /Object\.keys\s*\((.*)\)\.length/g, '$1' ],
[ /Object\.keys\s*\((.*)\)/g, 'is_array($1) ? array_keys($1) : array()' ],
[ /Object\.values\s*\((.*)\)/g, 'is_array($1) ? array_values($1) : array()' ],
[ /([^\s]+\s*\(\))\.toString \(\)/g, '(string) $1' ],
[ /([^\s]+)\.toString \(\)/g, '(string) $1' ],
[ /throw new Error \((.*)\)/g, 'throw new \\Exception($1)' ],
[ /throw new ([\S]+) \((.*)\)/g, 'throw new $1($2)' ],
[ /throw ([\S]+)\;/g, 'throw $$$1;' ],
[ '([^a-z]+) (' + Object.keys (errors).join ('|') + ')([^\\s])', "$1 '\\\\ccxt\\\\$2'$3" ],
[ /\}\s+catch \(([\S]+)\) {/g, '} catch (Exception $$$1) {' ],
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(\<=|\>=|<|>)\s*(.*)\.length\s*\;([^\)]+)\)\s*{/g, 'for ($1 = $2; $1 $3 count($4);$5) {' ],
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(\<=|\>=|<|>)\s*(.*)\s*\;([^\)]+)\)\s*{/g, 'for ($1 = $2; $1 $3 $4;$5) {' ],
[ /([^\s]+)\.length\;/g, 'count($1);' ],
[ /\.push\s*\(([\s\S]+?)\)\;/g, '[] = $1;' ],
[ /\sawait\s+([^;]+);/g, ' Async\\await($1);' ],
[ /([\S])\: /g, '$1 => ' ],
// add {}-array syntax conversions up to 20 levels deep
]).concat ([ ... Array (20) ].map (x => [ /\{([^\{]+?)\}([^\s])/g, 'array($1)$2' ])).concat ([
[ /\[\s*([^\]]+?)\s*\]\.join\s*\(\s*([^\)]+?)\s*\)/g, "implode($2, array($1))" ],
// add []-array syntax conversions up to 20 levels deep
]).concat ([ ... Array (20) ].map (x => [ /\[(\s[^\]]+?\s)\]/g, 'array($1)' ])).concat ([
[ /(\b)String(\b)/g, "$1'strval'$2"],
[ /JSON\.stringify/g, 'json_encode' ],
[ /JSON\.parse\s+\(([^\)]+)\)/g, 'json_decode($1, $$as_associative_array = true)' ],
// [ /\'([^\']+)\'\.sprintf\s*\(([^\)]+)\)/g, "sprintf ('$1', $2)" ],
[ /([^\s]+)\.toFixed\s*\(([0-9]+)\)/g, "sprintf('%.$2f', $1)" ],
[ /([^\s]+)\.toFixed\s*\(([^\)]+)\)/g, "sprintf('%.' . $2 . 'f', $1)" ],
[ /parseFloat\s/g, 'floatval'],
[ /parseInt\s/g, 'intval'],
[ / \+ (?!\d)/g, ' . ' ],
[ / \+\= (?!\d)/g, ' .= ' ],
[ /([^\s\(]+(?:\s*\(.+\))?)\.toUpperCase\s*\(\)/g, 'strtoupper($1)' ],
[ /([^\s\(]+(?:\s*\(.+\))?)\.toLowerCase\s*\(\)/g, 'strtolower($1)' ],
// [ /([^\s\(]+(?:\s*\(.+\))?)\.replaceAll\s*\(([^)]+)\)/g, 'str_replace($2, $1)' ], // still not a part of the standard in Node.js 13
[ /([^\s\(]+(?:\s*\(.+\))?)\.replace\s*\(([^)]+)\)/g, 'str_replace($2, $1)' ],
[ /this\[([^\]+]+)\]/g, '$$this->$$$1' ],
[ /([^\s\(]+).slice \(([^\)\:,]+)\)/g, 'mb_substr($1, $2)' ],
[ /([^\s\(]+).slice \(([^\,\)]+)\,\s*([^\)]+)\)/g, 'mb_substr($1, $2, $3 - $2)' ],
[ /([^\s\(]+).split \(('[^']*'|[^\,]+?)\)/g, 'explode($2, $1)' ],
[ /([^\s\(]+)\.length/g, 'strlen($1)' ],
[ /Math\.floor\s*\(([^\)]+)\)/g, '(int) floor($1)' ],
[ /Math\.abs\s*\(([^\)]+)\)/g, 'abs($1)' ],
[ /Math\.round\s*\(([^\)]+)\)/g, '(int) round($1)' ],
[ /Math\.ceil\s*\(([^\)]+)\)/g, '(int) ceil($1)' ],
[ /Math\.pow\s*\(([^\)]+)\)/g, 'pow($1)' ],
[ /Math\.log/g, 'log' ],
[ /([^\(\s]+)\s+%\s+([^\s\,\;\)]+)/g, 'fmod($1, $2)' ],
[ /\(([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\>\=\s*0\)/g, '(mb_strpos($1, $2) !== false)' ],
[ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\>\=\s*0/g, 'mb_strpos($1, $2) !== false' ],
[ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\<\s*0/g, 'mb_strpos($1, $2) === false' ],
[ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)/g, 'mb_strpos($1, $2)' ],
[ /\(([^\s\(]+)\sin\s([^\)]+)\)/g, '(is_array($2) && array_key_exists($1, $2))' ],
[ /([^\s]+)\.join\s*\(\s*([^\)]+?)\s*\)/g, 'implode($2, $1)' ],
[ 'new ccxt\\.', 'new \\ccxt\\' ], // a special case for test_exchange_datetime_functions.php (and for other files, maybe)
[ /Math\.(max|min)/g, '$1' ],
[ /console\.log/g, 'var_dump'],
[ /process\.exit/g, 'exit'],
[ /super\./g, 'parent::'],
[ /\sdelete\s([^\n]+)\;/g, ' unset($1);' ],
[ /\~([\]\[\|@\.\s+\:\/#\-a-zA-Z0-9_-]+?)\~/g, '{$1}' ], // resolve the "arrays vs url params" conflict (both are in {}-brackets)
[ /(\s+ \* @(param|return) {[^}]*)object([^}]*}.*)/g, '$1array$3' ], // docstring type conversion
])
}
getBaseClass () {
return new Exchange ()
}
getBaseMethods () {
const baseExchange = this.getBaseClass ()
let object = baseExchange
let properties = []
while (object !== Object.prototype) {
properties = properties.concat (Object.getOwnPropertyNames (object))
object = Object.getPrototypeOf (object)
}
return properties.filter (x => typeof baseExchange[x] === 'function')
}
getPythonBaseMethods () {
return this.getBaseMethods ()
}
getPHPBaseMethods () {
return this.getBaseMethods ()
}
//-------------------------------------------------------------------------
// the following common headers are used for transpiled tests
getPythonPreamble () {
return [
"import os",
"import sys",
"",
"root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))",
"sys.path.append(root)",
"",
"# ----------------------------------------------------------------------------",
"",
"# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
"# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
"",
"# ----------------------------------------------------------------------------",
"",
].join ("\n")
}
getPHPPreamble (include = true) {
return [
"<?php",
"namespace ccxt;",
include ? "include_once (__DIR__.'/../../ccxt.php');" : "",
"// ----------------------------------------------------------------------------",
"",
"// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
"// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
"",
"// -----------------------------------------------------------------------------",
"",
].join ("\n")
}
// ------------------------------------------------------------------------
// a helper to apply an array of regexes and substitutions to text
// accepts an array like [ [ regex, substitution ], ... ]
regexAll (text, array) {
for (const i in array) {
let regex = array[i][0]
const flags = (typeof regex === 'string') ? 'g' : undefined
regex = new RegExp (regex, flags)
text = text.replace (regex, array[i][1])
}
return text
}
// ========================================================================
// one-time helpers
createPythonClassDeclaration (className, baseClass) {
return 'class ' + className + '(' + baseClass + '):'
}
createPythonHeader () {
return [
pythonCodingUtf8,
"",
"# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
"# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
"",
]
}
createPythonClassHeader (imports, bodyAsString) {
const header = this.createPythonHeader ()
return header.concat (imports);
}
createPythonClassImports (baseClass, async = false) {
const baseClasses = {
'Exchange': 'base.exchange',
}
async = (async ? '.async_support' : '')
return [
(baseClass.indexOf ('ccxt.') === 0) ?
('import ccxt' + async + ' as ccxt') :
('from ccxt' + async + '.' + safeString (baseClasses, baseClass, baseClass) + ' import ' + baseClass) ]
}
createPythonClass (className, baseClass, body, methods, async = false) {
let bodyAsString = body.join ("\n")
const {
imports,
asyncioImports,
libraries,
errorImports,
precisionImports
} = this.createPythonImports(baseClass, bodyAsString, async)
let header = this.createPythonClassHeader (imports, bodyAsString)
header = header.concat (asyncioImports, libraries, errorImports, precisionImports)
// transpile camelCase base method names to underscore base method names
const baseMethods = this.getPythonBaseMethods ()
methods = methods.concat (baseMethods)
for (let method of methods) {
const regex = new RegExp ('(self|super\\([^)]+\\))\\.(' + method + ')([^a-zA-Z0-9_])', 'g')
bodyAsString = bodyAsString.replace (regex, (match, p1, p2, p3) => (p1 + '.' + unCamelCase (p2) + p3))
}
header.push ("\n\n" + this.createPythonClassDeclaration (className, baseClass))
const footer = [
'', // footer (last empty line)
]
const result = header.join ("\n") + "\n" + bodyAsString + "\n" + footer.join ('\n')
return result
}
createPythonImports (baseClass, bodyAsString, async = false) {
async = (async ? '.async_support' : '')
const pythonStandardLibraries = {
'hashlib': 'hashlib',
'math': 'math',
'json.loads': 'json',
'json.dumps': 'json',
'sys': 'sys',
}
const imports = this.createPythonClassImports (baseClass, async)
const libraries = []
for (let library in pythonStandardLibraries) {
const regex = new RegExp ("[^\\'\\\"a-zA-Z]" + library + "[^\\'\\\"a-zA-Z]")
if (bodyAsString.match (regex)){
const importStatement = 'import ' + pythonStandardLibraries[library];
if (!libraries.includes(importStatement)) {
libraries.push (importStatement)
}
}
}
if (bodyAsString.match (/numbers\.(Real|Integral)/)) {
libraries.push ('import numbers')
}
const errorImports = []
for (let error in errors) {
const regex = new RegExp ("[^\\'\"]" + error + "[^\\'\"]")
if (bodyAsString.match (regex)) {
errorImports.push ('from ccxt.base.errors import ' + error)
}
}
const precisionImports = []
for (let constant in precisionConstants) {
if (bodyAsString.indexOf (constant) >= 0) {
precisionImports.push ('from ccxt.base.decimal_to_precision import ' + constant)
}
}
if (bodyAsString.match (/[\s(]Precise/)) {
precisionImports.push ('from ccxt.base.precise import Precise')
}
const asyncioImports = []
if (bodyAsString.match (/asyncio/)) {
asyncioImports.push ('import asyncio')
}
return {
imports,
asyncioImports,
libraries,
errorImports,
precisionImports
}
}
// ========================================================================
// exchange capabilities ordering
sortExchangeCapabilities (code) {
const lineBreak = '\n';
const capabilitiesObjectRegex = /(?<='has': {[\n])([^|})]*)(?=\n(\s+}))/;
const found = capabilitiesObjectRegex.exec (code);
if (found === null) {
return false // capabilities not found
}
let capabilities = found[0].split (lineBreak);
const sortingOrder = {
'CORS': 'undefined,',
'spot': 'true,',
'margin': 'undefined,',
'swap': 'undefined,',
'future': 'undefined,',
'option': 'undefined,',
// then everything else
}
const features = {}
let indentation = ' ' // 16 spaces
for (let i = 0; i < capabilities.length; i++) {
const capability = capabilities[i]
const match = capability.match (/(\s+)\'(.+)\': (.+)$/)
if (match) {
indentation = match[1]
const feature = match[2]
const value = match[3]
features[feature] = value
}
}
let keys = Object.keys (features)
keys.sort ((a, b) => a.localeCompare (b))
const allKeys = Object.keys (sortingOrder).concat (keys)
for (let i = 0; i < allKeys.length; i++) {
const key = allKeys[i]
sortingOrder[key] = (key in features) ? features[key] : sortingOrder[key]
}
const result = Object.entries (sortingOrder).map (([ key, value ]) => indentation + "'" + key + "': " + value).join (lineBreak)
if (result === found[0]) {
return false
}
return code.replace (capabilitiesObjectRegex, result)
}
// ------------------------------------------------------------------------
createPHPClassDeclaration (className, baseClass) {
return 'class ' + className + ' extends ' + baseClass + ' {'
}
createPHPClassHeader (className, baseClass, bodyAsString, namespace) {
return [
"<?php",
"",
"namespace " + namespace + ";",
"",
"// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
"// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
"",
"use Exception; // a common import",
]
}
createPHPClass (className, baseClass, body, methods, async = false) {
let bodyAsString = body.join ("\n")
let header = this.createPHPClassHeader (className, baseClass, bodyAsString, async ? 'ccxt\\async' : 'ccxt')
const errorImports = []
if (async) {
for (let error in errors) {
const regex = new RegExp ("[^'\"]" + error + "[^'\"]")
if (bodyAsString.match (regex)) {
errorImports.push ('use ccxt\\' + error + ';')
}
}
}
const precisionImports = []
const libraryImports = []
if (async) {
if (bodyAsString.match (/[\s(]Precise/)) {
precisionImports.push ('use ccxt\\Precise;')
}
if (bodyAsString.match (/Async\\await/)) {
libraryImports.push ('use React\\Async;')
}
if (bodyAsString.match (/Promise\\all/)) {
libraryImports.push ('use React\\Promise;')
}
}
header = header.concat (errorImports).concat (precisionImports).concat (libraryImports)
// transpile camelCase base method names to underscore base method names
const baseMethods = this.getPHPBaseMethods ()
methods = methods.concat (baseMethods)
for (let method of methods) {
let regex = new RegExp ('\\$this->(' + method + ')\\s?(\\(|[^a-zA-Z0-9_])', 'g')
bodyAsString = bodyAsString.replace (regex,
(match, p1, p2) => {
return ((p2 === '(') ?
('$this->' + unCamelCase (p1) + p2) : // support direct php calls
("array($this, '" + unCamelCase (p1) + "')" + p2)) // as well as passing instance methods as callables
})
regex = new RegExp ('parent::(' + method + ')\\s?(\\(|[^a-zA-Z0-9_])', 'g')
bodyAsString = bodyAsString.replace (regex,
(match, p1, p2) => {
return ((p2 === '(') ?
('parent::' + unCamelCase (p1) + p2) : // support direct php calls
("array($this, '" + unCamelCase (p1) + "')" + p2)) // as well as passing instance methods as callables
})
}
header.push ("\n" + this.createPHPClassDeclaration (className, baseClass))
const footer = [
"}\n",
]
const result = header.join ("\n") + "\n" + bodyAsString + "\n" + footer.join ('\n')
return result
}
// ========================================================================
transpileJavaScriptToPython3 ({ js, className, removeEmptyLines }) {
// transpile JS → Python 3
let python3Body = this.regexAll (js, this.getPythonRegexes ())
if (removeEmptyLines) {
python3Body = python3Body.replace (/$\s*$/gm, '')
}
const strippedPython3BodyWithoutComments = python3Body.replace (/^[\s]+#.+$/gm, '')
if (!strippedPython3BodyWithoutComments.match(/[^\s]/)) {