ccxt-look 1.81.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cache/eslintcache +1 -0
- package/.dockerignore +6 -0
- package/.eslintignore +1 -0
- package/.gitattributes +5 -0
- package/.readthedocs.yaml +16 -0
- package/CONTRIBUTING.md +1049 -0
- package/LICENSE.txt +21 -0
- package/README.md +537 -0
- package/SECURITY.md +5 -0
- package/build/cleanup-old-tags.js +94 -0
- package/build/countries.js +256 -0
- package/build/export-exchanges.js +520 -0
- package/build/fs.js +51 -0
- package/build/transpile.js +1772 -0
- package/build/vss.js +78 -0
- package/ccxt.browser.js +7 -0
- package/ccxt.d.ts +692 -0
- package/ccxt.js +171 -0
- package/cleanup.sh +2 -0
- package/composer-install.sh +20 -0
- package/dist/ccxt.browser.js +208383 -0
- package/gource.sh +3 -0
- package/index.html +7 -0
- package/js/.eslintrc +87 -0
- package/js/aax.js +2686 -0
- package/js/ascendex.js +2584 -0
- package/js/base/.eslintrc.js +43 -0
- package/js/base/Exchange.js +2371 -0
- package/js/base/Precise.js +283 -0
- package/js/base/errorHierarchy.js +47 -0
- package/js/base/errors.js +55 -0
- package/js/base/functions/crypto.js +158 -0
- package/js/base/functions/encode.js +118 -0
- package/js/base/functions/generic.js +270 -0
- package/js/base/functions/misc.js +138 -0
- package/js/base/functions/number.js +329 -0
- package/js/base/functions/platform.js +38 -0
- package/js/base/functions/string.js +21 -0
- package/js/base/functions/throttle.js +79 -0
- package/js/base/functions/time.js +210 -0
- package/js/base/functions/type.js +66 -0
- package/js/base/functions.js +28 -0
- package/js/bequant.js +32 -0
- package/js/bibox.js +1407 -0
- package/js/bigone.js +1366 -0
- package/js/binance.js +5652 -0
- package/js/binancecoinm.js +46 -0
- package/js/binanceus.js +46 -0
- package/js/binanceusdm.js +49 -0
- package/js/bit2c.js +535 -0
- package/js/bitbank.js +842 -0
- package/js/bitbay.js +16 -0
- package/js/bitbns.js +1073 -0
- package/js/bitcoincom.js +15 -0
- package/js/bitfinex.js +1433 -0
- package/js/bitfinex2.js +2025 -0
- package/js/bitflyer.js +840 -0
- package/js/bitforex.js +614 -0
- package/js/bitget.js +2397 -0
- package/js/bithumb.js +980 -0
- package/js/bitmart.js +2516 -0
- package/js/bitmex.js +1809 -0
- package/js/bitopro.js +1443 -0
- package/js/bitpanda.js +1782 -0
- package/js/bitrue.js +1747 -0
- package/js/bitso.js +1062 -0
- package/js/bitstamp.js +1757 -0
- package/js/bitstamp1.js +343 -0
- package/js/bittrex.js +1876 -0
- package/js/bitvavo.js +1579 -0
- package/js/bkex.js +1233 -0
- package/js/bl3p.js +346 -0
- package/js/blockchaincom.js +969 -0
- package/js/btcalpha.js +680 -0
- package/js/btcbox.js +477 -0
- package/js/btcmarkets.js +1022 -0
- package/js/btctradeua.js +466 -0
- package/js/btcturk.js +734 -0
- package/js/buda.js +946 -0
- package/js/bw.js +1265 -0
- package/js/bybit.js +3372 -0
- package/js/bytetrade.js +1336 -0
- package/js/cdax.js +1646 -0
- package/js/cex.js +1410 -0
- package/js/coinbase.js +1342 -0
- package/js/coinbaseprime.js +31 -0
- package/js/coinbasepro.js +1466 -0
- package/js/coincheck.js +755 -0
- package/js/coinex.js +3400 -0
- package/js/coinfalcon.js +880 -0
- package/js/coinmate.js +794 -0
- package/js/coinone.js +816 -0
- package/js/coinspot.js +345 -0
- package/js/crex24.js +1636 -0
- package/js/cryptocom.js +1832 -0
- package/js/currencycom.js +1748 -0
- package/js/delta.js +1547 -0
- package/js/deribit.js +2148 -0
- package/js/digifinex.js +1585 -0
- package/js/eqonex.js +1660 -0
- package/js/exmo.js +1670 -0
- package/js/fairdesk.js +1231 -0
- package/js/flowbtc.js +35 -0
- package/js/fmfwio.js +34 -0
- package/js/ftx.js +2751 -0
- package/js/ftxus.js +38 -0
- package/js/gateio.js +4174 -0
- package/js/gemini.js +1397 -0
- package/js/hitbtc.js +1343 -0
- package/js/hitbtc3.js +2329 -0
- package/js/hollaex.js +1486 -0
- package/js/huobi.js +5706 -0
- package/js/huobijp.js +1710 -0
- package/js/huobipro.js +18 -0
- package/js/idex.js +1439 -0
- package/js/independentreserve.js +649 -0
- package/js/indodax.js +742 -0
- package/js/itbit.js +722 -0
- package/js/kraken.js +2179 -0
- package/js/kucoin.js +2571 -0
- package/js/kucoinfutures.js +1771 -0
- package/js/kuna.js +809 -0
- package/js/latoken.js +1445 -0
- package/js/lbank.js +760 -0
- package/js/liquid.js +1432 -0
- package/js/luno.js +873 -0
- package/js/lykke.js +1147 -0
- package/js/mercado.js +771 -0
- package/js/mexc.js +3151 -0
- package/js/ndax.js +2233 -0
- package/js/novadax.js +1318 -0
- package/js/oceanex.js +816 -0
- package/js/okcoin.js +3841 -0
- package/js/okex.js +16 -0
- package/js/okex5.js +16 -0
- package/js/okx.js +4795 -0
- package/js/paymium.js +498 -0
- package/js/phemex.js +2957 -0
- package/js/poloniex.js +1674 -0
- package/js/probit.js +1346 -0
- package/js/qtrade.js +1588 -0
- package/js/ripio.js +1061 -0
- package/js/static_dependencies/BN/bn.js +3526 -0
- package/js/static_dependencies/README.md +1 -0
- package/js/static_dependencies/crypto-js/crypto-js.js +5988 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/base.js +375 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/edwards.js +433 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/index.js +8 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/mont.js +180 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curve/short.js +938 -0
- package/js/static_dependencies/elliptic/lib/elliptic/curves.js +204 -0
- package/js/static_dependencies/elliptic/lib/elliptic/ec/index.js +240 -0
- package/js/static_dependencies/elliptic/lib/elliptic/ec/key.js +119 -0
- package/js/static_dependencies/elliptic/lib/elliptic/ec/signature.js +24 -0
- package/js/static_dependencies/elliptic/lib/elliptic/eddsa/index.js +145 -0
- package/js/static_dependencies/elliptic/lib/elliptic/eddsa/key.js +100 -0
- package/js/static_dependencies/elliptic/lib/elliptic/eddsa/signature.js +65 -0
- package/js/static_dependencies/elliptic/lib/elliptic/precomputed/secp256k1.js +780 -0
- package/js/static_dependencies/elliptic/lib/elliptic/utils.js +214 -0
- package/js/static_dependencies/elliptic/lib/elliptic.js +22 -0
- package/js/static_dependencies/elliptic/lib/hmac-drbg/hmac-drbg.js +114 -0
- package/js/static_dependencies/fetch-ponyfill/fetch-node.js +39 -0
- package/js/static_dependencies/node-fetch/index.js +1564 -0
- package/js/static_dependencies/node-rsa/NodeRSA.js +223 -0
- package/js/static_dependencies/node-rsa/asn1/ber/errors.js +13 -0
- package/js/static_dependencies/node-rsa/asn1/ber/index.js +21 -0
- package/js/static_dependencies/node-rsa/asn1/ber/reader.js +262 -0
- package/js/static_dependencies/node-rsa/asn1/ber/types.js +36 -0
- package/js/static_dependencies/node-rsa/asn1/index.js +17 -0
- package/js/static_dependencies/node-rsa/encryptEngines/js.js +34 -0
- package/js/static_dependencies/node-rsa/formats/components.js +71 -0
- package/js/static_dependencies/node-rsa/formats/formats.js +31 -0
- package/js/static_dependencies/node-rsa/formats/pkcs1.js +148 -0
- package/js/static_dependencies/node-rsa/formats/pkcs8.js +187 -0
- package/js/static_dependencies/node-rsa/libs/jsbn.js +1252 -0
- package/js/static_dependencies/node-rsa/libs/rsa.js +147 -0
- package/js/static_dependencies/node-rsa/schemes/pkcs1.js +176 -0
- package/js/static_dependencies/node-rsa/schemes/schemes.js +21 -0
- package/js/static_dependencies/node-rsa/utils.js +98 -0
- package/js/static_dependencies/qs/formats.js +18 -0
- package/js/static_dependencies/qs/index.js +11 -0
- package/js/static_dependencies/qs/parse.js +242 -0
- package/js/static_dependencies/qs/stringify.js +269 -0
- package/js/static_dependencies/qs/utils.js +230 -0
- package/js/stex.js +1925 -0
- package/js/test/.eslintrc.js +42 -0
- package/js/test/Exchange/test.balance.js +61 -0
- package/js/test/Exchange/test.borrowRate.js +32 -0
- package/js/test/Exchange/test.currency.js +52 -0
- package/js/test/Exchange/test.fetchBalance.js +23 -0
- package/js/test/Exchange/test.fetchBorrowInterest.js +59 -0
- package/js/test/Exchange/test.fetchBorrowRate.js +32 -0
- package/js/test/Exchange/test.fetchBorrowRates.js +28 -0
- package/js/test/Exchange/test.fetchClosedOrders.js +32 -0
- package/js/test/Exchange/test.fetchCurrencies.js +35 -0
- package/js/test/Exchange/test.fetchDeposits.js +31 -0
- package/js/test/Exchange/test.fetchFundingFees.js +19 -0
- package/js/test/Exchange/test.fetchFundingRateHistory.js +40 -0
- package/js/test/Exchange/test.fetchL2OrderBook.js +23 -0
- package/js/test/Exchange/test.fetchLedger.js +42 -0
- package/js/test/Exchange/test.fetchLeverageTiers.js +33 -0
- package/js/test/Exchange/test.fetchMarketLeverageTiers.js +22 -0
- package/js/test/Exchange/test.fetchMarkets.js +33 -0
- package/js/test/Exchange/test.fetchMyTrades.js +42 -0
- package/js/test/Exchange/test.fetchOHLCV.js +46 -0
- package/js/test/Exchange/test.fetchOpenOrders.js +36 -0
- package/js/test/Exchange/test.fetchOrderBook.js +25 -0
- package/js/test/Exchange/test.fetchOrderBooks.js +35 -0
- package/js/test/Exchange/test.fetchOrders.js +41 -0
- package/js/test/Exchange/test.fetchPositions.js +47 -0
- package/js/test/Exchange/test.fetchStatus.js +35 -0
- package/js/test/Exchange/test.fetchTicker.js +38 -0
- package/js/test/Exchange/test.fetchTickers.js +49 -0
- package/js/test/Exchange/test.fetchTrades.js +39 -0
- package/js/test/Exchange/test.fetchTradingFee.js +18 -0
- package/js/test/Exchange/test.fetchTradingFees.js +22 -0
- package/js/test/Exchange/test.fetchTransactions.js +31 -0
- package/js/test/Exchange/test.fetchWithdrawals.js +31 -0
- package/js/test/Exchange/test.ledgerItem.js +46 -0
- package/js/test/Exchange/test.leverageTier.js +33 -0
- package/js/test/Exchange/test.loadMarkets.js +35 -0
- package/js/test/Exchange/test.market.js +129 -0
- package/js/test/Exchange/test.ohlcv.js +33 -0
- package/js/test/Exchange/test.order.js +62 -0
- package/js/test/Exchange/test.orderbook.js +61 -0
- package/js/test/Exchange/test.position.js +21 -0
- package/js/test/Exchange/test.throttle.js +94 -0
- package/js/test/Exchange/test.ticker.js +95 -0
- package/js/test/Exchange/test.trade.js +68 -0
- package/js/test/Exchange/test.tradingFee.js +34 -0
- package/js/test/Exchange/test.transaction.js +35 -0
- package/js/test/base/.eslintrc +38 -0
- package/js/test/base/functions/test.crypto.js +110 -0
- package/js/test/base/functions/test.datetime.js +62 -0
- package/js/test/base/functions/test.generic.js +152 -0
- package/js/test/base/functions/test.number.js +362 -0
- package/js/test/base/functions/test.time.js +56 -0
- package/js/test/base/functions/test.type.js +53 -0
- package/js/test/base/test.base.js +193 -0
- package/js/test/errors/test.InsufficientFunds.js +86 -0
- package/js/test/errors/test.InvalidNonce.js +64 -0
- package/js/test/errors/test.InvalidOrder.js +35 -0
- package/js/test/errors/test.OrderNotFound.js +39 -0
- package/js/test/test.js +426 -0
- package/js/test/test.timeout_hang.js +12 -0
- package/js/therock.js +1431 -0
- package/js/tidebit.js +632 -0
- package/js/tidex.js +939 -0
- package/js/timex.js +1283 -0
- package/js/upbit.js +1622 -0
- package/js/vcc.js +1353 -0
- package/js/wavesexchange.js +2185 -0
- package/js/wazirx.js +732 -0
- package/js/whitebit.js +1352 -0
- package/js/woo.js +1577 -0
- package/js/xena.js +1948 -0
- package/js/yobit.js +1129 -0
- package/js/zaif.js +647 -0
- package/js/zb.js +4088 -0
- package/js/zipmex.js +40 -0
- package/js/zonda.js +1497 -0
- package/multilang.sh +159 -0
- package/package.json +591 -0
- package/postinstall.js +103 -0
@@ -0,0 +1,1772 @@
|
|
1
|
+
// ---------------------------------------------------------------------------
|
2
|
+
// Usage: npm run transpile
|
3
|
+
// ---------------------------------------------------------------------------
|
4
|
+
|
5
|
+
"use strict";
|
6
|
+
|
7
|
+
const fs = require ('fs')
|
8
|
+
, log = require ('ololog').unlimited
|
9
|
+
, _ = require ('ansicolor').nice
|
10
|
+
, errors = require ('../js/base/errors.js')
|
11
|
+
, functions = require ('../js/base/functions.js')
|
12
|
+
, {
|
13
|
+
unCamelCase,
|
14
|
+
precisionConstants,
|
15
|
+
safeString,
|
16
|
+
unique,
|
17
|
+
} = functions
|
18
|
+
, { basename } = require ('path')
|
19
|
+
, {
|
20
|
+
createFolderRecursively,
|
21
|
+
replaceInFile,
|
22
|
+
overwriteFile,
|
23
|
+
} = require ('./fs.js')
|
24
|
+
, Exchange = require ('../js/base/Exchange.js')
|
25
|
+
, tsFilename = './ccxt.d.ts'
|
26
|
+
|
27
|
+
class Transpiler {
|
28
|
+
|
29
|
+
getCommonRegexes () {
|
30
|
+
return [
|
31
|
+
|
32
|
+
[ /\.deepExtend\s/g, '.deep_extend'],
|
33
|
+
[ /\.safeFloat2\s/g, '.safe_float_2'],
|
34
|
+
[ /\.safeInteger2\s/g, '.safe_integer_2'],
|
35
|
+
[ /\.safeIntegerProduct2\s/g, '.safe_integer_product_2'],
|
36
|
+
[ /\.safeTimestamp2\s/g, '.safe_timestamp_2'],
|
37
|
+
[ /\.safeString2\s/g, '.safe_string_2'],
|
38
|
+
[ /\.safeNumber2\s/g, '.safe_number_2'],
|
39
|
+
[ /\.safeStringLower2\s/g, '.safe_string_lower_2'],
|
40
|
+
[ /\.safeStringUpper2\s/g, '.safe_string_upper_2'],
|
41
|
+
[ /\.safeValue2\s/g, '.safe_value_2'],
|
42
|
+
[ /\.safeFloat\s/g, '.safe_float'],
|
43
|
+
[ /\.safeInteger\s/g, '.safe_integer'],
|
44
|
+
[ /\.safeIntegerProduct\s/g, '.safe_integer_product'],
|
45
|
+
[ /\.safeTimestamp\s/g, '.safe_timestamp'],
|
46
|
+
[ /\.safeString\s/g, '.safe_string'],
|
47
|
+
[ /\.safeStringLower\s/g, '.safe_string_lower'],
|
48
|
+
[ /\.safeStringUpper\s/g, '.safe_string_upper'],
|
49
|
+
[ /\.safeValue\s/g, '.safe_value'],
|
50
|
+
[ /\.inArray\s/g, '.in_array'],
|
51
|
+
[ /\.toArray\s/g, '.to_array'],
|
52
|
+
[ /\.isEmpty\s/g, '.is_empty'],
|
53
|
+
[ /\.arrayConcat\s/g, '.array_concat'],
|
54
|
+
[ /\.binaryConcat\s/g, '.binary_concat'],
|
55
|
+
[ /\.binaryConcatArray\s/g, '.binary_concat_array'],
|
56
|
+
[ /\.binaryToString\s/g, '.binary_to_string' ],
|
57
|
+
[ /\.precisionFromString\s/g, '.precision_from_string'],
|
58
|
+
[ /\.parsePrecision\s/g, '.parse_precision'],
|
59
|
+
[ /\.implodeHostname\s/g, '.implode_hostname'],
|
60
|
+
[ /\.implodeParams\s/g, '.implode_params'],
|
61
|
+
[ /\.extractParams\s/g, '.extract_params'],
|
62
|
+
[ /\.safeBalance\s/g, '.safe_balance'],
|
63
|
+
[ /\.parseBalance\s/g, '.parse_balance'],
|
64
|
+
[ /\.parseOHLCVs\s/g, '.parse_ohlcvs'],
|
65
|
+
[ /\.parseOHLCV\s/g, '.parse_ohlcv'],
|
66
|
+
[ /\.parseDate\s/g, '.parse_date'],
|
67
|
+
[ /\.parseLedgerEntry\s/g, '.parse_ledger_entry'],
|
68
|
+
[ /\.parseLedger\s/g, '.parse_ledger'],
|
69
|
+
[ /\.parseTickers\s/g, '.parse_tickers'],
|
70
|
+
[ /\.parseTicker\s/g, '.parse_ticker'],
|
71
|
+
[ /\.parseTimeframe\s/g, '.parse_timeframe'],
|
72
|
+
[ /\.parseTradesData\s/g, '.parse_trades_data'],
|
73
|
+
[ /\.parseTrades\s/g, '.parse_trades'],
|
74
|
+
[ /\.parseTrade\s/g, '.parse_trade'],
|
75
|
+
[ /\.parseTradingViewOHLCV\s/g, '.parse_trading_view_ohlcv'],
|
76
|
+
[ /\.convertTradingViewToOHLCV\s/g, '.convert_trading_view_to_ohlcv'],
|
77
|
+
[ /\.parseTransaction\s/g, '.parse_transaction'],
|
78
|
+
[ /\.parseTransactions\s/g, '.parse_transactions'],
|
79
|
+
[ /\.parseOrderBook\s/g, '.parse_order_book'],
|
80
|
+
[ /\.parseBidsAsks\s/g, '.parse_bids_asks'],
|
81
|
+
[ /\.parseBidAsk\s/g, '.parse_bid_ask'],
|
82
|
+
[ /\.parseOrders\s/g, '.parse_orders'],
|
83
|
+
[ /\.parseOrderStatus\s/g, '.parse_order_status'],
|
84
|
+
[ /\.parseOrder\s/g, '.parse_order'],
|
85
|
+
[ /\.parseJson\s/g, '.parse_json'],
|
86
|
+
[ /\.filterByArray\s/g, '.filter_by_array'],
|
87
|
+
[ /\.filterBySymbolSinceLimit\s/g, '.filter_by_symbol_since_limit'],
|
88
|
+
[ /\.filterBySinceLimit\s/g, '.filter_by_since_limit'],
|
89
|
+
[ /\.filterBySymbol\s/g, '.filter_by_symbol'],
|
90
|
+
[ /\.getVersionString\s/g, '.get_version_string'],
|
91
|
+
[ /\.indexBy\s/g, '.index_by'],
|
92
|
+
[ /\.sortBy\s/g, '.sort_by'],
|
93
|
+
[ /\.sortBy2\s/g, '.sort_by_2'],
|
94
|
+
[ /\.filterBy\s/g, '.filter_by'],
|
95
|
+
[ /\.groupBy\s/g, '.group_by'],
|
96
|
+
[ /\.marketIds\s/g, '.market_ids'],
|
97
|
+
[ /\.marketId\s/g, '.market_id'],
|
98
|
+
[ /\.fetchFundingFees\s/g, '.fetch_funding_fees'],
|
99
|
+
[ /\.fetchTradingFees\s/g, '.fetch_trading_fees'],
|
100
|
+
[ /\.fetchTradingFee\s/g, '.fetch_trading_fee'],
|
101
|
+
[ /\.fetchFees\s/g, '.fetch_fees'],
|
102
|
+
[ /\.fetchL2OrderBook\s/g, '.fetch_l2_order_book'],
|
103
|
+
[ /\.fetchOrderBook\s/g, '.fetch_order_book'],
|
104
|
+
[ /\.fetchMyTrades\s/g, '.fetch_my_trades'],
|
105
|
+
[ /\.fetchOrderStatus\s/g, '.fetch_order_status'],
|
106
|
+
[ /\.fetchOpenOrders\s/g, '.fetch_open_orders'],
|
107
|
+
[ /\.fetchOpenOrder\s/g, '.fetch_open_order'],
|
108
|
+
[ /\.fetchOrders\s/g, '.fetch_orders'],
|
109
|
+
[ /\.fetchOrderTrades\s/g, '.fetch_order_trades'],
|
110
|
+
[ /\.fetchOrder\s/g, '.fetch_order'],
|
111
|
+
[ /\.fetchBidsAsks\s/g, '.fetch_bids_asks'],
|
112
|
+
[ /\.fetchTickers\s/g, '.fetch_tickers'],
|
113
|
+
[ /\.fetchTicker\s/g, '.fetch_ticker'],
|
114
|
+
[ /\.fetchCurrencies\s/g, '.fetch_currencies'],
|
115
|
+
[ /\.numberToString\s/g, '.number_to_string' ],
|
116
|
+
[ /\.decimalToPrecision\s/g, '.decimal_to_precision'],
|
117
|
+
[ /\.priceToPrecision\s/g, '.price_to_precision'],
|
118
|
+
[ /\.amountToPrecision\s/g, '.amount_to_precision'],
|
119
|
+
[ /\.amountToLots\s/g, '.amount_to_lots'],
|
120
|
+
[ /\.feeToPrecision\s/g, '.fee_to_precision'],
|
121
|
+
[ /\.currencyToPrecision\s/g, '.currency_to_precision'],
|
122
|
+
[ /\.costToPrecision\s/g, '.cost_to_precision'],
|
123
|
+
[ /\.commonCurrencyCode\s/g, '.common_currency_code'],
|
124
|
+
[ /\.loadAccounts\s/g, '.load_accounts'],
|
125
|
+
[ /\.loadFees\s/g, '.load_fees'],
|
126
|
+
[ /\.loadMarkets\s/g, '.load_markets'],
|
127
|
+
[ /\.loadTimeDifference\s/g, '.load_time_difference'],
|
128
|
+
[ /\.fetchMarkets\s/g, '.fetch_markets'],
|
129
|
+
[ /\.appendInactiveMarkets\s/g, '.append_inactive_markets'],
|
130
|
+
[ /\.fetchCategories\s/g, '.fetch_categories'],
|
131
|
+
[ /\.calculateFee\s/g, '.calculate_fee'],
|
132
|
+
[ /\.createOrder\s/g, '.create_order'],
|
133
|
+
[ /\.createPostOnlyOrder\s/g, '.create_post_only_order'],
|
134
|
+
[ /\.createStopOrder\s/g, '.create_stop_order'],
|
135
|
+
[ /\.createStopLimitOrder\s/g, '.create_stop_limit_order'],
|
136
|
+
[ /\.createStopMarketOrder\s/g, '.create_stop_market_order'],
|
137
|
+
[ /\.editLimitBuyOrder\s/g, '.edit_limit_buy_order'],
|
138
|
+
[ /\.editLimitSellOrder\s/g, '.edit_limit_sell_order'],
|
139
|
+
[ /\.editLimitOrder\s/g, '.edit_limit_order'],
|
140
|
+
[ /\.editOrder\s/g, '.edit_order'],
|
141
|
+
[ /\.encodeURIComponent\s/g, '.encode_uri_component'],
|
142
|
+
[ /\.throwExceptionOnError\s/g, '.throw_exception_on_error'],
|
143
|
+
[ /\.handleErrors\s/g, '.handle_errors'],
|
144
|
+
[ /\.checkRequiredCredentials\s/g, '.check_required_credentials'],
|
145
|
+
[ /\.checkRequiredDependencies\s/g, '.check_required_dependencies'],
|
146
|
+
[ /\.checkAddress\s/g, '.check_address'],
|
147
|
+
[ /\.convertTradingViewToOHLCV\s/g, '.convert_trading_view_to_ohlcv'],
|
148
|
+
[ /\.convertOHLCVToTradingView\s/g, '.convert_ohlcv_to_trading_view'],
|
149
|
+
[ /\.signBodyWithSecret\s/g, '.sign_body_with_secret'],
|
150
|
+
[ /\.isJsonEncodedObject\s/g, '.is_json_encoded_object'],
|
151
|
+
[ /\.setSandboxMode\s/g, '.set_sandbox_mode'],
|
152
|
+
[ /\.safeCurrencyCode\s/g, '.safe_currency_code'],
|
153
|
+
[ /\.safeCurrency\s/g, '.safe_currency'],
|
154
|
+
[ /\.safeSymbol\s/g, '.safe_symbol'],
|
155
|
+
[ /\.safeMarket\s/g, '.safe_market'],
|
156
|
+
[ /\.safeOrder\s/g, '.safe_order'],
|
157
|
+
[ /\.safeTicker\s/g, '.safe_ticker'],
|
158
|
+
[ /\.roundTimeframe\s/g, '.round_timeframe'],
|
159
|
+
[ /\.calculateRateLimiterCost\s/g, '.calculate_rate_limiter_cost' ],
|
160
|
+
[ /\.parseAccountPosition\s/g, '.parse_account_position' ],
|
161
|
+
[ /\.parsePositionRisk\s/g, '.parse_position_risk' ],
|
162
|
+
[ /\.parseIncome\s/g, '.parse_income' ],
|
163
|
+
[ /\.parseIncomes\s/g, '.parse_incomes' ],
|
164
|
+
[ /\.parseFundingRate\s/g, '.parse_funding_rate' ],
|
165
|
+
[ /\.findBroadlyMatchedKey\s/g, '.find_broadly_matched_key' ],
|
166
|
+
[ /\.throwBroadlyMatchedException\s/g, '.throw_broadly_matched_exception' ],
|
167
|
+
[ /\.throwExactlyMatchedException\s/g, '.throw_exactly_matched_exception' ],
|
168
|
+
[ /\.getNetwork\s/g, '.get_network' ],
|
169
|
+
[ /\.findTimeframe\s/g, '.find_timeframe'],
|
170
|
+
[ /errorHierarchy/g, 'error_hierarchy'],
|
171
|
+
[ /\.base16ToBinary/g, '.base16_to_binary'],
|
172
|
+
[ /\'use strict\';?\s+/g, '' ],
|
173
|
+
[ /\.urlencodeNested\s/g, '.urlencode_nested' ],
|
174
|
+
[ /\.urlencodeWithArrayRepeat\s/g, '.urlencode_with_array_repeat' ],
|
175
|
+
[ /\.call\s*\(this, /g, '(' ],
|
176
|
+
[ /\.getSupportedMapping\s/g, '.get_supported_mapping'],
|
177
|
+
[ /\.fetchBorrowRate\s/g, '.fetch_borrow_rate'],
|
178
|
+
[ /\.handleMarketTypeAndParams\s/g, '.handle_market_type_and_params'],
|
179
|
+
[ /\.isPostOnly\s/g, '.is_post_only'],
|
180
|
+
]
|
181
|
+
}
|
182
|
+
|
183
|
+
getPythonRegexes () {
|
184
|
+
|
185
|
+
return [
|
186
|
+
[ /Array\.isArray\s*\(([^\)]+)\)/g, 'isinstance($1, list)' ],
|
187
|
+
[ /([^\(\s]+)\s+instanceof\s+String/g, 'isinstance($1, str)' ],
|
188
|
+
[ /([^\(\s]+)\s+instanceof\s+([^\)\s]+)/g, 'isinstance($1, $2)' ],
|
189
|
+
|
190
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'undefined\'/g, '$1[$2] is None' ],
|
191
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'undefined\'/g, '$1[$2] is not None' ],
|
192
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'undefined\'/g, '$1 is None' ],
|
193
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'undefined\'/g, '$1 is not None' ],
|
194
|
+
[ /typeof\s+(.+?)\s+\=\=\=?\s+\'undefined\'/g, '$1 is None' ],
|
195
|
+
[ /typeof\s+(.+?)\s+\!\=\=?\s+\'undefined\'/g, '$1 is not None' ],
|
196
|
+
|
197
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'number\'/g, "isinstance($1[$2], numbers.Real)" ],
|
198
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'number\'/g, "(not isinstance($1[$2], numbers.Real))" ],
|
199
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'number\'/g, "isinstance($1, numbers.Real)" ],
|
200
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'number\'/g, "(not isinstance($1, numbers.Real))" ],
|
201
|
+
|
202
|
+
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+undefined/g, '$1[$2] is None' ],
|
203
|
+
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+undefined/g, '$1[$2] is not None' ],
|
204
|
+
[ /([^\s]+)\s+\=\=\=?\s+undefined/g, '$1 is None' ],
|
205
|
+
[ /([^\s]+)\s+\!\=\=?\s+undefined/g, '$1 is not None' ],
|
206
|
+
[ /(.+?)\s+\=\=\=?\s+undefined/g, '$1 is None' ],
|
207
|
+
[ /(.+?)\s+\!\=\=?\s+undefined/g, '$1 is not None' ],
|
208
|
+
//
|
209
|
+
// too broad, have to rewrite these cause they don't work
|
210
|
+
//
|
211
|
+
// [ /([^\s]+)\s+\=\=\=?\s+true/g, 'isinstance($1, bool) and ($1 is True)' ],
|
212
|
+
// [ /([^\s]+)\s+\!\=\=?\s+true/g, 'isinstance($1, bool) and ($1 is not True)' ],
|
213
|
+
// [ /([^\s]+)\s+\=\=\=?\s+false/g, 'isinstance($1, bool) and ($1 is False)' ],
|
214
|
+
// [ /([^\s]+)\s+\!\=\=?\s+false/g, 'isinstance($1, bool) and ($1 is not False)' ],
|
215
|
+
|
216
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'string\'/g, 'isinstance($1[$2], str)' ],
|
217
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'string\'/g, 'not isinstance($1[$2], str)' ],
|
218
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'string\'/g, 'isinstance($1, str)' ],
|
219
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'string\'/g, 'not isinstance($1, str)' ],
|
220
|
+
|
221
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'object\'/g, 'isinstance($1[$2], dict)' ],
|
222
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'object\'/g, 'not isinstance($1[$2], dict)' ],
|
223
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'object\'/g, 'isinstance($1, dict)' ],
|
224
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'object\'/g, 'not isinstance($1, dict)' ],
|
225
|
+
|
226
|
+
[ /undefined/g, 'None' ],
|
227
|
+
[ /\=\=\=?/g, '==' ],
|
228
|
+
[ /\!\=\=?/g, '!=' ],
|
229
|
+
[ /this\.stringToBinary\s*\((.*)\)/g, '$1' ],
|
230
|
+
[ /\.shift\s*\(\)/g, '.pop(0)' ],
|
231
|
+
[ /Number\.MAX_SAFE_INTEGER/g, 'float(\'inf\')'],
|
232
|
+
[ /function\s*(\w+\s*\([^)]+\))\s*{/g, 'def $1:'],
|
233
|
+
// [ /\.replaceAll\s*\(([^)]+)\)/g, '.replace($1)' ], // still not a part of the standard
|
234
|
+
[ /assert\s*\((.+)\);/g, 'assert $1'],
|
235
|
+
[ /Promise\.all\s*\(([^\)]+)\)/g, 'asyncio.gather(*$1)' ],
|
236
|
+
[ /Precise\.stringAdd\s/g, 'Precise.string_add' ],
|
237
|
+
[ /Precise\.stringMul\s/g, 'Precise.string_mul' ],
|
238
|
+
[ /Precise\.stringDiv\s/g, 'Precise.string_div' ],
|
239
|
+
[ /Precise\.stringSub\s/g, 'Precise.string_sub' ],
|
240
|
+
[ /Precise\.stringAbs\s/g, 'Precise.string_abs' ],
|
241
|
+
[ /Precise\.stringNeg\s/g, 'Precise.string_neg' ],
|
242
|
+
[ /Precise\.stringMod\s/g, 'Precise.string_mod' ],
|
243
|
+
[ /Precise\.stringEquals\s/g, 'Precise.string_equals' ],
|
244
|
+
[ /Precise\.stringEq\s/g, 'Precise.string_eq' ],
|
245
|
+
[ /Precise\.stringMin\s/g, 'Precise.string_min' ],
|
246
|
+
[ /Precise\.stringMax\s/g, 'Precise.string_max' ],
|
247
|
+
[ /Precise\.stringGt\s/g, 'Precise.string_gt' ],
|
248
|
+
[ /Precise\.stringGe\s/g, 'Precise.string_ge' ],
|
249
|
+
[ /Precise\.stringLt\s/g, 'Precise.string_lt' ],
|
250
|
+
[ /Precise\.stringLe\s/g, 'Precise.string_le' ],
|
251
|
+
|
252
|
+
// insert common regexes in the middle (critical)
|
253
|
+
].concat (this.getCommonRegexes ()).concat ([
|
254
|
+
|
255
|
+
// [ /this\.urlencode\s/g, '_urlencode.urlencode ' ], // use self.urlencode instead
|
256
|
+
[ /this\./g, 'self.' ],
|
257
|
+
[ /([^a-zA-Z\'])this([^a-zA-Z])/g, '$1self$2' ],
|
258
|
+
[ /\[\s*([^\]]+)\s\]\s=/g, '$1 =' ],
|
259
|
+
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\[\s*([^\]]+)\s\]/g, '$1$2' ],
|
260
|
+
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\{\s*([^\}]+)\s\}\s\=\s([^\;]+)/g, '$1$2 = (lambda $2: ($2))(**$3)' ],
|
261
|
+
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ],
|
262
|
+
[ /Object\.keys\s*\((.*)\)\.length/g, '$1' ],
|
263
|
+
[ /Object\.keys\s*\((.*)\)/g, 'list($1.keys())' ],
|
264
|
+
[ /Object\.values\s*\((.*)\)/g, 'list($1.values())' ],
|
265
|
+
[ /\[([^\]]+)\]\.join\s*\(([^\)]+)\)/g, "$2.join([$1])" ],
|
266
|
+
[ /hash \(([^,]+)\, \'(sha[0-9])\'/g, "hash($1, '$2'" ],
|
267
|
+
[ /hmac \(([^,]+)\, ([^,]+)\, \'(md5)\'/g, 'hmac($1, $2, hashlib.$3' ],
|
268
|
+
[ /hmac \(([^,]+)\, ([^,]+)\, \'(sha[0-9]+)\'/g, 'hmac($1, $2, hashlib.$3' ],
|
269
|
+
[ /throw new ([\S]+) \((.*)\)/g, 'raise $1($2)'],
|
270
|
+
[ /throw ([\S]+)/g, 'raise $1'],
|
271
|
+
[ /try {/g, 'try:'],
|
272
|
+
[ /\}\s+catch \(([\S]+)\) {/g, 'except Exception as $1:'],
|
273
|
+
[ /([\s\(])extend(\s)/g, '$1self.extend$2' ],
|
274
|
+
[ /\} else if/g, 'elif' ],
|
275
|
+
[ /else if/g, 'elif' ],
|
276
|
+
[ /if\s+\((.*)\)\s+\{/g, 'if $1:' ],
|
277
|
+
[ /if\s+\((.*)\)\s*[\n]/g, "if $1:\n" ],
|
278
|
+
[ /\}\s*else\s*\{/g, 'else:' ],
|
279
|
+
[ /else\s*[\n]/g, "else:\n" ],
|
280
|
+
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(?:\<=|\>=|<|>)\s*(.*)\.length\s*\;[^\)]+\)\s*{/g, 'for $1 in range($2, len($3)):'],
|
281
|
+
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(?:\<=|\>=|<|>)\s*(.*)\s*\;[^\)]+\)\s*{/g, 'for $1 in range($2, $3):'],
|
282
|
+
[ /\s\|\|\s/g, ' or ' ],
|
283
|
+
[ /\s\&\&\s/g, ' and ' ],
|
284
|
+
[ /\!([^\s\='"])/g, 'not $1'],
|
285
|
+
[ /\.push\s*\(([\s\S]+?)\);/g, '.append($1);' ],
|
286
|
+
[ /^(\s*}\s*$)+/gm, '' ],
|
287
|
+
[ /\;(\s+?\/\/.+?)/g, '$1' ],
|
288
|
+
[ /\;$/gm, '' ],
|
289
|
+
[ /\.toUpperCase\s*/g, '.upper' ],
|
290
|
+
[ /\.toLowerCase\s*/g, '.lower' ],
|
291
|
+
[ /JSON\.stringify\s*/g, 'json.dumps' ],
|
292
|
+
[ /JSON\.parse\s*/g, "json.loads" ],
|
293
|
+
// [ /([^\(\s]+)\.includes\s+\(([^\)]+)\)/g, '$2 in $1' ],
|
294
|
+
// [ /\'%([^\']+)\'\.sprintf\s*\(([^\)]+)\)/g, "'{:$1}'.format($2)" ],
|
295
|
+
[ /([^\s]+)\.toFixed\s*\(([0-9]+)\)/g, "'{:.$2f}'.format($1)" ],
|
296
|
+
[ /([^\s]+)\.toFixed\s*\(([^\)]+)\)/g, "('{:.' + str($2) + 'f}').format($1)" ],
|
297
|
+
[ /parseFloat\s*/g, 'float'],
|
298
|
+
[ /parseInt\s*/g, 'int'],
|
299
|
+
[ /self\[([^\]+]+)\]/g, 'getattr(self, $1)' ],
|
300
|
+
[ /Math\.floor\s*\(([^\)]+)\)/g, 'int(math.floor($1))' ],
|
301
|
+
[ /Math\.abs\s*\(([^\)]+)\)/g, 'abs($1)' ],
|
302
|
+
[ /Math\.pow\s*\(([^\)]+)\)/g, 'math.pow($1)' ],
|
303
|
+
[ /Math\.round\s*\(([^\)]+)\)/g, 'int(round($1))' ],
|
304
|
+
[ /Math\.ceil\s*\(([^\)]+)\)/g, 'int(math.ceil($1))' ],
|
305
|
+
[ /Math\.log/g, 'math.log' ],
|
306
|
+
[ /([a-zA-Z0-9_\.]*\([^\)]+\)|[^\s]+)\s+\?\s*([^\:]+)\s+\:\s*([^\n]+)/g, '$2 if $1 else $3'],
|
307
|
+
[ /([^\s]+)\.slice \(([^\,\)]+)\,\s?([^\)]+)\)/g, '$1[$2:$3]' ],
|
308
|
+
[ /([^\s]+)\.slice \(([^\)\:]+)\)/g, '$1[$2:]' ],
|
309
|
+
[ /([^\s(:]+)\.length/g, 'len($1)' ],
|
310
|
+
[ /(^|\s)\/\//g, '$1#' ],
|
311
|
+
[ /([^\n\s]) #/g, '$1 #' ], // PEP8 E261
|
312
|
+
[ /\.indexOf/g, '.find'],
|
313
|
+
[ /(\s|\()true/g, '$1True'],
|
314
|
+
[ /(\s|\()false/g, '$1False'],
|
315
|
+
[ /([^\s]+\s*\(\))\.toString\s+\(\)/g, 'str($1)' ],
|
316
|
+
[ /([^\s]+)\.toString \(\)/g, 'str($1)' ],
|
317
|
+
[ /([^\s]+)\.join\s*\(\s*([^\)\[\]]+?)\s*\)/g, '$2.join($1)' ],
|
318
|
+
[ /Math\.(max|min)\s/g, '$1' ],
|
319
|
+
[ / = new /g, ' = ' ], // python does not have a 'new' keyword
|
320
|
+
[ /console\.log\s/g, 'print' ],
|
321
|
+
[ /process\.exit\s+/g, 'sys.exit' ],
|
322
|
+
[ /(while \(.*\)) {/, '$1\:' ], // While loops replace bracket with :
|
323
|
+
[ /([^:+=\/\*\s-]+) \(/g, '$1(' ], // PEP8 E225 remove whitespaces before left ( round bracket
|
324
|
+
[ /\sand\(/g, ' and (' ],
|
325
|
+
[ /\sor\(/g, ' or (' ],
|
326
|
+
[ /\snot\(/g, ' not (' ],
|
327
|
+
[ /\[ /g, '[' ], // PEP8 E201 remove whitespaces after left [ square bracket
|
328
|
+
[ /\{ /g, '{' ], // PEP8 E201 remove whitespaces after left { bracket
|
329
|
+
[ /(?<=[^\s#]) \]/g, ']' ], // PEP8 E202 remove whitespaces before right ] square bracket
|
330
|
+
[ /(?<=[^\s#]) \}/g, '}' ], // PEP8 E202 remove whitespaces before right } bracket
|
331
|
+
[ /([^a-z])(elif|if|or|else)\(/g, '$1$2 \(' ], // a correction for PEP8 E225 side-effect for compound and ternary conditionals
|
332
|
+
[ /\!\=\sTrue/g, 'is not True' ], // a correction for PEP8 E712, it likes "is not True", not "!= True"
|
333
|
+
[ /\=\=\sTrue/g, 'is True' ], // a correction for PEP8 E712, it likes "is True", not "== True"
|
334
|
+
[ /\sdelete\s/g, ' del ' ],
|
335
|
+
[ /(?<!#.+)null/, 'None' ],
|
336
|
+
[ /\/\*\*/, '\"\"\"' ], // Doc strings
|
337
|
+
[ / \*\//, '\"\"\"' ], // Doc strings
|
338
|
+
[ /\[(.*)\]\{@link (.*)\}/g, '`$1 <$2>`' ], // docstring item with link
|
339
|
+
[ /\s+\* @method/g, '' ], // docstring @method
|
340
|
+
[ /(\s+) \* @description (.*)/g, '$1$2' ], // docstring description
|
341
|
+
[ /\s+\* @name .*/g, '' ], // docstring @name
|
342
|
+
[ /(\s+) \* @returns/g, '$1:returns:' ], // docstring return
|
343
|
+
[ /(\s+ \* @param \{[a-z]+\} )([a-zA-Z0-9_-]+)\.([a-zA-Z0-9_-]+) (.*)/g, '$1$2[\'$3\'] $4' ], // docstring params.anything
|
344
|
+
[ /(\s+) \* @([a-z]+) \{([a-z]+)\} ([a-zA-Z0-9_\-\.\[\]\']+)/g, '$1:$2 $3 $4:' ], // docstring param
|
345
|
+
])
|
346
|
+
}
|
347
|
+
|
348
|
+
getPython2Regexes () {
|
349
|
+
return [
|
350
|
+
[ /.+asyncio\.gather.+\n/g, '' ], // remove line entirely
|
351
|
+
[ /(\s)await(\s)/g, '$1' ]
|
352
|
+
]
|
353
|
+
}
|
354
|
+
|
355
|
+
getSyncPHPRegexes () {
|
356
|
+
return [
|
357
|
+
[ /.+(\$[a-zA-Z0-9_]+)\s*=\s*yield\s+\1;\n/g, '' ], // delete yield all promises line
|
358
|
+
[ /\byield /g, '' ]
|
359
|
+
]
|
360
|
+
}
|
361
|
+
|
362
|
+
getPHPRegexes () {
|
363
|
+
return [
|
364
|
+
//
|
365
|
+
// Curly-braces are used for both dictionaries in the code as well as for the url-imploded params.
|
366
|
+
// For example: https://docs.ccxt.com/en/latest/manual.html#implicit-api-methods
|
367
|
+
//
|
368
|
+
// There's a conflict between the curly braces that have to be converted from dictionaries to PHP-arrays and
|
369
|
+
// the curly braces used for url-imploded params that should not be touched.
|
370
|
+
//
|
371
|
+
// The transpiler takes all non-spaced strings in curly braces {likeThis} and converts them to ~likeThis~.
|
372
|
+
// That is done to avoid changing the curly braces into the array() in PHP.
|
373
|
+
// This way we protect the url-imploded params from being touched by the regexes that will follow.
|
374
|
+
// That conversion is done first-thing, at the very early stage of transpilation.
|
375
|
+
// The regexes are applied in the order they're listed, top-down.
|
376
|
+
//
|
377
|
+
// A dictionary in curly braces will never have those curly braces attached to the contents of the dictionary.
|
378
|
+
// There will always be a space like { 'a': b, 'c': d }.
|
379
|
+
// Hence, the remaining non-converted curly-brace dictionaries will have to be converted to arrays in PHP.
|
380
|
+
// That is done in the middle of the transpilation process.
|
381
|
+
//
|
382
|
+
// The last step is to convert those "saved embedded/imploded url-params substitutions" from ~likeThis~ back to {likeThis}.
|
383
|
+
// That is done at the very last regex steps.
|
384
|
+
// All of that is a workaround for PHP-arrays vs dictionaries vs url-imploded params in other langs.
|
385
|
+
//
|
386
|
+
[ /\{([a-zA-Z0-9_-]+?)\}/g, '~$1~' ], // resolve the "arrays vs url params" conflict (both are in {}-brackets)
|
387
|
+
[ /\[(.*)\]\{(@link .*)\}/g, '~$2 $1~' ], // docstring item with link
|
388
|
+
[ /\s+\* @method/g, '' ], // docstring @method
|
389
|
+
[ /(\s+)\* @description (.*)/g, '$1\* $2' ], // docstring description
|
390
|
+
[ /\s+\* @name .*/g, '' ], // docstring @name
|
391
|
+
[ /(\s+)\* @returns/g, '$1\* @return' ], // docstring return
|
392
|
+
[ /\!Array\.isArray\s*\(([^\)]+)\)/g, "gettype($1) === 'array' && count(array_filter(array_keys($1), 'is_string')) != 0" ],
|
393
|
+
[ /Array\.isArray\s*\(([^\)]+)\)/g, "gettype($1) === 'array' && count(array_filter(array_keys($1), 'is_string')) == 0" ],
|
394
|
+
[ /([^\(\s]+)\s+instanceof\s+String/g, 'is_string($1)' ],
|
395
|
+
|
396
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'undefined\'/g, '$1[$2] === null' ],
|
397
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'undefined\'/g, '$1[$2] !== null' ],
|
398
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'undefined\'/g, '$1 === null' ],
|
399
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'undefined\'/g, '$1 !== null' ],
|
400
|
+
[ /typeof\s+(.+?)\s+\=\=\=?\s+\'undefined\'/g, '$1 === null' ],
|
401
|
+
[ /typeof\s+(.+?)\s+\!\=\=?\s+\'undefined\'/g, '$1 !== null' ],
|
402
|
+
|
403
|
+
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+undefined/g, '$1[$2] === null' ],
|
404
|
+
[ /([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+undefined/g, '$1[$2] !== null' ],
|
405
|
+
[ /([^\s]+)\s+\=\=\=?\s+undefined/g, '$1 === null' ],
|
406
|
+
[ /([^\s]+)\s+\!\=\=?\s+undefined/g, '$1 !== null' ],
|
407
|
+
[ /(.+?)\s+\=\=\=?\s+undefined/g, '$1 === null' ],
|
408
|
+
[ /(.+?)\s+\!\=\=?\s+undefined/g, '$1 !== null' ],
|
409
|
+
|
410
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'string\'/g, "gettype($1[$2]) === 'string'" ],
|
411
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'string\'/g, "gettype($1[$2]) !== 'string'" ],
|
412
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'string\'/g, "gettype($1) === 'string'" ],
|
413
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'string\'/g, "gettype($1) !== 'string'" ],
|
414
|
+
|
415
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'object\'/g, "gettype($1[$2]) === 'array'" ],
|
416
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'object\'/g, "gettype($1[$2]) !== 'array'" ],
|
417
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'object\'/g, "gettype($1) === 'array'" ],
|
418
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'object\'/g, "gettype($1) !== 'array'" ],
|
419
|
+
|
420
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\=\=\=?\s+\'number\'/g, "(is_float($1[$2]) || is_int($1[$2]))" ], // same as above but for number
|
421
|
+
[ /typeof\s+([^\s\[]+)(?:\s|\[(.+?)\])\s+\!\=\=?\s+\'number\'/g, "!(is_float($1[$2]) || is_int($1[$2]))" ],
|
422
|
+
[ /typeof\s+([^\s]+)\s+\=\=\=?\s+\'number\'/g, "(is_float($1) || is_int($1))" ],
|
423
|
+
[ /typeof\s+([^\s]+)\s+\!\=\=?\s+\'number\'/g, "!(is_float($1) || is_int($1))" ],
|
424
|
+
|
425
|
+
[ /undefined/g, 'null' ],
|
426
|
+
[ /this\.extend\s/g, 'array_merge' ],
|
427
|
+
[ /this\.stringToBinary\s*\((.*)\)/g, '$1' ],
|
428
|
+
[ /this\.stringToBase64\s/g, 'base64_encode' ],
|
429
|
+
[ /this\.binaryToBase16\s/g, 'bin2hex' ],
|
430
|
+
[ /this\.base64ToBinary\s/g, 'base64_decode' ],
|
431
|
+
[ /this\.base64ToString\s/g, 'base64_decode' ],
|
432
|
+
[ /Promise\.all\s*\(([^\)]+)\)/g, '$1' ],
|
433
|
+
// deepExtend is commented for PHP because it does not overwrite linear arrays
|
434
|
+
// a proper \ccxt\Exchange::deep_extend() base method is implemented instead
|
435
|
+
// [ /this\.deepExtend\s/g, 'array_replace_recursive'],
|
436
|
+
[ /(\w+)\.shift\s*\(\)/g, 'array_shift($1)' ],
|
437
|
+
[ /(\w+)\.pop\s*\(\)/g, 'array_pop($1)' ],
|
438
|
+
[ /Number\.MAX_SAFE_INTEGER/g, 'PHP_INT_MAX' ],
|
439
|
+
[ /Precise\.stringAdd\s/g, 'Precise::string_add' ],
|
440
|
+
[ /Precise\.stringDiv\s/g, 'Precise::string_div' ],
|
441
|
+
[ /Precise\.stringMul\s/g, 'Precise::string_mul' ],
|
442
|
+
[ /Precise\.stringSub\s/g, 'Precise::string_sub' ],
|
443
|
+
[ /Precise\.stringAbs\s/g, 'Precise::string_abs' ],
|
444
|
+
[ /Precise\.stringNeg\s/g, 'Precise::string_neg' ],
|
445
|
+
[ /Precise\.stringMod\s/g, 'Precise::string_mod' ],
|
446
|
+
[ /Precise\.stringEquals\s/g, 'Precise::string_equals' ],
|
447
|
+
[ /Precise\.stringEq\s/g, 'Precise::string_eq' ],
|
448
|
+
[ /Precise\.stringMin\s/g, 'Precise::string_min' ],
|
449
|
+
[ /Precise\.stringMax\s/g, 'Precise::string_max' ],
|
450
|
+
[ /Precise\.stringGt\s/g, 'Precise::string_gt' ],
|
451
|
+
[ /Precise\.stringGe\s/g, 'Precise::string_ge' ],
|
452
|
+
[ /Precise\.stringLt\s/g, 'Precise::string_lt' ],
|
453
|
+
[ /Precise\.stringLe\s/g, 'Precise::string_le' ],
|
454
|
+
|
455
|
+
// insert common regexes in the middle (critical)
|
456
|
+
].concat (this.getCommonRegexes ()).concat ([
|
457
|
+
|
458
|
+
[ /this\./g, '$this->' ],
|
459
|
+
[ / this;/g, ' $this;' ],
|
460
|
+
[ /([^'])this_\./g, '$1$this_->' ],
|
461
|
+
[ /([^'])\{\}/g, '$1array()' ],
|
462
|
+
[ /([^'])\[\]/g, '$1array()' ],
|
463
|
+
|
464
|
+
// add {}-array syntax conversions up to 20 levels deep in the same line
|
465
|
+
]).concat ([ ... Array (20) ].map (x => [ /\{([^\n\}]+)\}/g, 'array($1)' ] )).concat ([
|
466
|
+
[ /\[\s*([^\]]+)\s\]\s=/g, 'list($1) =' ],
|
467
|
+
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\[\s*([^\]]+)\s\]/g, '$1list($2)' ],
|
468
|
+
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s\{\s*([^\}]+)\s\}/g, '$1array_values(list($2))' ],
|
469
|
+
[ /(^|[^a-zA-Z0-9_])(?:let|const|var)\s/g, '$1' ],
|
470
|
+
[ /Object\.keys\s*\((.*)\)\.length/g, '$1' ],
|
471
|
+
[ /Object\.keys\s*\((.*)\)/g, 'is_array($1) ? array_keys($1) : array()' ],
|
472
|
+
[ /Object\.values\s*\((.*)\)/g, 'is_array($1) ? array_values($1) : array()' ],
|
473
|
+
[ /([^\s]+\s*\(\))\.toString \(\)/g, '(string) $1' ],
|
474
|
+
[ /([^\s]+)\.toString \(\)/g, '(string) $1' ],
|
475
|
+
[ /throw new Error \((.*)\)/g, 'throw new \\Exception($1)' ],
|
476
|
+
[ /throw new ([\S]+) \((.*)\)/g, 'throw new $1($2)' ],
|
477
|
+
[ /throw ([\S]+)\;/g, 'throw $$$1;' ],
|
478
|
+
[ '([^a-z]+) (' + Object.keys (errors).join ('|') + ')([^\\s])', "$1 '\\\\ccxt\\\\$2'$3" ],
|
479
|
+
[ /\}\s+catch \(([\S]+)\) {/g, '} catch (Exception $$$1) {' ],
|
480
|
+
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(\<=|\>=|<|>)\s*(.*)\.length\s*\;([^\)]+)\)\s*{/g, 'for ($1 = $2; $1 $3 count($4);$5) {' ],
|
481
|
+
[ /for\s+\(([a-zA-Z0-9_]+)\s*=\s*([^\;\s]+\s*)\;[^\<\>\=]+(\<=|\>=|<|>)\s*(.*)\s*\;([^\)]+)\)\s*{/g, 'for ($1 = $2; $1 $3 $4;$5) {' ],
|
482
|
+
[ /([^\s]+)\.length\;/g, 'is_array($1) ? count($1) : 0;' ],
|
483
|
+
[ /\.push\s*\(([\s\S]+?)\)\;/g, '[] = $1;' ],
|
484
|
+
[ /(\b)await(\b)/g, 'yield' ],
|
485
|
+
[ /([\S])\: /g, '$1 => ' ],
|
486
|
+
|
487
|
+
// add {}-array syntax conversions up to 20 levels deep
|
488
|
+
]).concat ([ ... Array (20) ].map (x => [ /\{([^\{]+?)\}([^\s])/g, 'array($1)$2' ])).concat ([
|
489
|
+
|
490
|
+
[ /\[\s*([^\]]+?)\s*\]\.join\s*\(\s*([^\)]+?)\s*\)/g, "implode($2, array($1))" ],
|
491
|
+
|
492
|
+
// add []-array syntax conversions up to 20 levels deep
|
493
|
+
]).concat ([ ... Array (20) ].map (x => [ /\[(\s[^\]]+?\s)\]/g, 'array($1)' ])).concat ([
|
494
|
+
|
495
|
+
[ /JSON\.stringify/g, 'json_encode' ],
|
496
|
+
[ /JSON\.parse\s+\(([^\)]+)\)/g, 'json_decode($1, $$as_associative_array = true)' ],
|
497
|
+
// [ /\'([^\']+)\'\.sprintf\s*\(([^\)]+)\)/g, "sprintf ('$1', $2)" ],
|
498
|
+
[ /([^\s]+)\.toFixed\s*\(([0-9]+)\)/g, "sprintf('%.$2f', $1)" ],
|
499
|
+
[ /([^\s]+)\.toFixed\s*\(([^\)]+)\)/g, "sprintf('%.' . $2 . 'f', $1)" ],
|
500
|
+
[ /parseFloat\s/g, 'floatval'],
|
501
|
+
[ /parseInt\s/g, 'intval'],
|
502
|
+
[ / \+ (?!\d)/g, ' . ' ],
|
503
|
+
[ / \+\= (?!\d)/g, ' .= ' ],
|
504
|
+
[ /([^\s\(]+(?:\s*\(.+\))?)\.toUpperCase\s*\(\)/g, 'strtoupper($1)' ],
|
505
|
+
[ /([^\s\(]+(?:\s*\(.+\))?)\.toLowerCase\s*\(\)/g, 'strtolower($1)' ],
|
506
|
+
// [ /([^\s\(]+(?:\s*\(.+\))?)\.replaceAll\s*\(([^)]+)\)/g, 'str_replace($2, $1)' ], // still not a part of the standard in Node.js 13
|
507
|
+
[ /([^\s\(]+(?:\s*\(.+\))?)\.replace\s*\(([^)]+)\)/g, 'str_replace($2, $1)' ],
|
508
|
+
[ /this\[([^\]+]+)\]/g, '$$this->$$$1' ],
|
509
|
+
[ /([^\s\(]+).slice \(([^\)\:,]+)\)/g, 'mb_substr($1, $2)' ],
|
510
|
+
[ /([^\s\(]+).slice \(([^\,\)]+)\,\s*([^\)]+)\)/g, 'mb_substr($1, $2, $3 - $2)' ],
|
511
|
+
[ /([^\s\(]+).split \(('[^']*'|[^\,]+?)\)/g, 'explode($2, $1)' ],
|
512
|
+
[ /([^\s\(]+)\.length/g, 'strlen($1)' ],
|
513
|
+
[ /Math\.floor\s*\(([^\)]+)\)/g, '(int) floor($1)' ],
|
514
|
+
[ /Math\.abs\s*\(([^\)]+)\)/g, 'abs($1)' ],
|
515
|
+
[ /Math\.round\s*\(([^\)]+)\)/g, '(int) round($1)' ],
|
516
|
+
[ /Math\.ceil\s*\(([^\)]+)\)/g, '(int) ceil($1)' ],
|
517
|
+
[ /Math\.pow\s*\(([^\)]+)\)/g, 'pow($1)' ],
|
518
|
+
[ /Math\.log/g, 'log' ],
|
519
|
+
[ /([^\(\s]+)\s+%\s+([^\s\,\;\)]+)/g, 'fmod($1, $2)' ],
|
520
|
+
[ /\(([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\>\=\s*0\)/g, '(mb_strpos($1, $2) !== false)' ],
|
521
|
+
[ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\>\=\s*0/g, 'mb_strpos($1, $2) !== false' ],
|
522
|
+
[ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)\s*\<\s*0/g, 'mb_strpos($1, $2) === false' ],
|
523
|
+
[ /([^\s\(]+)\.indexOf\s*\(([^\)]+)\)/g, 'mb_strpos($1, $2)' ],
|
524
|
+
[ /\(([^\s\(]+)\sin\s([^\)]+)\)/g, '(is_array($2) && array_key_exists($1, $2))' ],
|
525
|
+
[ /([^\s]+)\.join\s*\(\s*([^\)]+?)\s*\)/g, 'implode($2, $1)' ],
|
526
|
+
[ 'new ccxt\\.', 'new \\ccxt\\' ], // a special case for test_exchange_datetime_functions.php (and for other files, maybe)
|
527
|
+
[ /Math\.(max|min)/g, '$1' ],
|
528
|
+
[ /console\.log/g, 'var_dump'],
|
529
|
+
[ /process\.exit/g, 'exit'],
|
530
|
+
[ /super\./g, 'parent::'],
|
531
|
+
[ /\sdelete\s([^\n]+)\;/g, ' unset($1);' ],
|
532
|
+
[ /\~([@\.\s+\:\/#\-a-zA-Z0-9_-]+?)\~/g, '{$1}' ], // resolve the "arrays vs url params" conflict (both are in {}-brackets)
|
533
|
+
])
|
534
|
+
}
|
535
|
+
|
536
|
+
getBaseClass () {
|
537
|
+
return new Exchange ()
|
538
|
+
}
|
539
|
+
|
540
|
+
getBaseMethods () {
|
541
|
+
const baseExchange = this.getBaseClass ()
|
542
|
+
let object = baseExchange
|
543
|
+
let properties = []
|
544
|
+
while (object !== Object.prototype) {
|
545
|
+
properties = properties.concat (Object.getOwnPropertyNames (object))
|
546
|
+
object = Object.getPrototypeOf (object)
|
547
|
+
}
|
548
|
+
return properties.filter (x => typeof baseExchange[x] === 'function')
|
549
|
+
}
|
550
|
+
|
551
|
+
getPythonBaseMethods () {
|
552
|
+
return this.getBaseMethods ()
|
553
|
+
}
|
554
|
+
|
555
|
+
getPHPBaseMethods () {
|
556
|
+
return this.getBaseMethods ()
|
557
|
+
}
|
558
|
+
|
559
|
+
//-------------------------------------------------------------------------
|
560
|
+
// the following common headers are used for transpiled tests
|
561
|
+
|
562
|
+
getPythonPreamble () {
|
563
|
+
return [
|
564
|
+
"import os",
|
565
|
+
"import sys",
|
566
|
+
"",
|
567
|
+
"root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))",
|
568
|
+
"sys.path.append(root)",
|
569
|
+
"",
|
570
|
+
"# ----------------------------------------------------------------------------",
|
571
|
+
"",
|
572
|
+
"# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
|
573
|
+
"# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
|
574
|
+
"",
|
575
|
+
"# ----------------------------------------------------------------------------",
|
576
|
+
"",
|
577
|
+
].join ("\n")
|
578
|
+
}
|
579
|
+
|
580
|
+
getPHPPreamble (include = true) {
|
581
|
+
return [
|
582
|
+
"<?php",
|
583
|
+
"namespace ccxt;",
|
584
|
+
include ? "include_once (__DIR__.'/../../ccxt.php');" : "",
|
585
|
+
"// ----------------------------------------------------------------------------",
|
586
|
+
"",
|
587
|
+
"// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
|
588
|
+
"// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
|
589
|
+
"",
|
590
|
+
"// -----------------------------------------------------------------------------",
|
591
|
+
"",
|
592
|
+
].join ("\n")
|
593
|
+
}
|
594
|
+
|
595
|
+
// ------------------------------------------------------------------------
|
596
|
+
// a helper to apply an array of regexes and substitutions to text
|
597
|
+
// accepts an array like [ [ regex, substitution ], ... ]
|
598
|
+
|
599
|
+
regexAll (text, array) {
|
600
|
+
for (const i in array) {
|
601
|
+
let regex = array[i][0]
|
602
|
+
const flags = (typeof regex === 'string') ? 'g' : undefined
|
603
|
+
regex = new RegExp (regex, flags)
|
604
|
+
text = text.replace (regex, array[i][1])
|
605
|
+
}
|
606
|
+
return text
|
607
|
+
}
|
608
|
+
|
609
|
+
// ========================================================================
|
610
|
+
// one-time helpers
|
611
|
+
|
612
|
+
createPythonClassDeclaration (className, baseClass) {
|
613
|
+
return 'class ' + className + '(' + baseClass + '):'
|
614
|
+
}
|
615
|
+
|
616
|
+
createPythonClassHeader (imports, bodyAsString) {
|
617
|
+
return [
|
618
|
+
"# -*- coding: utf-8 -*-",
|
619
|
+
"",
|
620
|
+
"# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
|
621
|
+
"# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
|
622
|
+
"",
|
623
|
+
... imports,
|
624
|
+
]
|
625
|
+
}
|
626
|
+
|
627
|
+
createPythonClassImports (baseClass, async = false) {
|
628
|
+
|
629
|
+
const baseClasses = {
|
630
|
+
'Exchange': 'base.exchange',
|
631
|
+
}
|
632
|
+
|
633
|
+
async = (async ? '.async_support' : '')
|
634
|
+
|
635
|
+
return [
|
636
|
+
(baseClass.indexOf ('ccxt.') === 0) ?
|
637
|
+
('import ccxt' + async + ' as ccxt') :
|
638
|
+
('from ccxt' + async + '.' + safeString (baseClasses, baseClass, baseClass) + ' import ' + baseClass)
|
639
|
+
]
|
640
|
+
}
|
641
|
+
|
642
|
+
createPythonClass (className, baseClass, body, methods, async = false) {
|
643
|
+
|
644
|
+
const pythonStandardLibraries = {
|
645
|
+
'hashlib': 'hashlib',
|
646
|
+
'math': 'math',
|
647
|
+
'json.loads': 'json',
|
648
|
+
'sys': 'sys',
|
649
|
+
}
|
650
|
+
|
651
|
+
const baseClasses = {
|
652
|
+
'Exchange': 'base.exchange',
|
653
|
+
}
|
654
|
+
|
655
|
+
async = (async ? '.async_support' : '')
|
656
|
+
|
657
|
+
const imports = this.createPythonClassImports (baseClass, async)
|
658
|
+
|
659
|
+
let bodyAsString = body.join ("\n")
|
660
|
+
|
661
|
+
let header = this.createPythonClassHeader (imports, bodyAsString)
|
662
|
+
|
663
|
+
const libraries = []
|
664
|
+
|
665
|
+
for (let library in pythonStandardLibraries) {
|
666
|
+
const regex = new RegExp ("[^\\'\\\"a-zA-Z]" + library + "[^\\'\\\"a-zA-Z]")
|
667
|
+
if (bodyAsString.match (regex))
|
668
|
+
libraries.push ('import ' + pythonStandardLibraries[library])
|
669
|
+
}
|
670
|
+
|
671
|
+
if (body.indexOf ('numbers') >= 0) {
|
672
|
+
libraries.push ('import numbers')
|
673
|
+
}
|
674
|
+
|
675
|
+
const errorImports = []
|
676
|
+
|
677
|
+
for (let error in errors) {
|
678
|
+
const regex = new RegExp ("[^\\'\"]" + error + "[^\\'\"]")
|
679
|
+
if (bodyAsString.match (regex)) {
|
680
|
+
errorImports.push ('from ccxt.base.errors import ' + error)
|
681
|
+
}
|
682
|
+
}
|
683
|
+
|
684
|
+
const precisionImports = []
|
685
|
+
|
686
|
+
for (let constant in precisionConstants) {
|
687
|
+
if (bodyAsString.indexOf (constant) >= 0) {
|
688
|
+
precisionImports.push ('from ccxt.base.decimal_to_precision import ' + constant)
|
689
|
+
}
|
690
|
+
}
|
691
|
+
if (bodyAsString.match (/[\s(]Precise/)) {
|
692
|
+
precisionImports.push ('from ccxt.base.precise import Precise')
|
693
|
+
}
|
694
|
+
const asyncioImports = []
|
695
|
+
if (bodyAsString.match (/asyncio/)) {
|
696
|
+
asyncioImports.push ('import asyncio')
|
697
|
+
}
|
698
|
+
|
699
|
+
header = header.concat (asyncioImports, libraries, errorImports, precisionImports)
|
700
|
+
|
701
|
+
methods = methods.concat (this.getPythonBaseMethods ())
|
702
|
+
|
703
|
+
for (let method of methods) {
|
704
|
+
const regex = new RegExp ('self\\.(' + method + ')([^a-zA-Z0-9_])', 'g')
|
705
|
+
bodyAsString = bodyAsString.replace (regex,
|
706
|
+
(match, p1, p2) => ('self.' + unCamelCase (p1) + p2))
|
707
|
+
}
|
708
|
+
|
709
|
+
header.push ("\n\n" + this.createPythonClassDeclaration (className, baseClass))
|
710
|
+
|
711
|
+
const footer = [
|
712
|
+
'', // footer (last empty line)
|
713
|
+
]
|
714
|
+
|
715
|
+
const result = header.join ("\n") + "\n" + bodyAsString + "\n" + footer.join ('\n')
|
716
|
+
return result
|
717
|
+
}
|
718
|
+
|
719
|
+
// ========================================================================
|
720
|
+
// exchange capabilities ordering
|
721
|
+
|
722
|
+
sortExchangeCapabilities (code) {
|
723
|
+
const lineBreak = '\n';
|
724
|
+
const capabilitiesObjectRegex = /(?<='has': {[\n])([^|})]*)(?=\n(\s+}))/;
|
725
|
+
const found = capabilitiesObjectRegex.exec (code);
|
726
|
+
if (found === null) {
|
727
|
+
return false // capabilities not found
|
728
|
+
}
|
729
|
+
let capabilities = found[0].split (lineBreak);
|
730
|
+
const exchange = new Exchange ()
|
731
|
+
const sortingOrder = {
|
732
|
+
'CORS': 'undefined,',
|
733
|
+
'spot': 'true,',
|
734
|
+
'margin': 'undefined,',
|
735
|
+
'swap': 'undefined,',
|
736
|
+
'future': 'undefined,',
|
737
|
+
'option': 'undefined,',
|
738
|
+
// then everything else
|
739
|
+
}
|
740
|
+
const features = {}
|
741
|
+
let indentation = ' ' // 16 spaces
|
742
|
+
for (let i = 0; i < capabilities.length; i++) {
|
743
|
+
const capability = capabilities[i]
|
744
|
+
const match = capability.match (/(\s+)\'(.+)\': (.+)$/)
|
745
|
+
if (match) {
|
746
|
+
indentation = match[1]
|
747
|
+
const feature = match[2]
|
748
|
+
const value = match[3]
|
749
|
+
features[feature] = value
|
750
|
+
}
|
751
|
+
}
|
752
|
+
let keys = Object.keys (features)
|
753
|
+
keys.sort ((a, b) => a.localeCompare (b))
|
754
|
+
const allKeys = Object.keys (sortingOrder).concat (keys)
|
755
|
+
for (let i = 0; i < allKeys.length; i++) {
|
756
|
+
const key = allKeys[i]
|
757
|
+
sortingOrder[key] = (key in features) ? features[key] : sortingOrder[key]
|
758
|
+
}
|
759
|
+
const result = Object.entries (sortingOrder).map (([ key, value ]) => indentation + "'" + key + "': " + value).join (lineBreak)
|
760
|
+
if (result === found[0]) {
|
761
|
+
return false
|
762
|
+
}
|
763
|
+
return code.replace (capabilitiesObjectRegex, result)
|
764
|
+
}
|
765
|
+
|
766
|
+
// ------------------------------------------------------------------------
|
767
|
+
|
768
|
+
createPHPClassDeclaration (className, baseClass) {
|
769
|
+
return 'class ' + className + ' extends ' + baseClass + ' {'
|
770
|
+
}
|
771
|
+
|
772
|
+
createPHPClassHeader (className, baseClass, bodyAsString, namespace) {
|
773
|
+
return [
|
774
|
+
"<?php",
|
775
|
+
"",
|
776
|
+
"namespace " + namespace + ";",
|
777
|
+
"",
|
778
|
+
"// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:",
|
779
|
+
"// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code",
|
780
|
+
"",
|
781
|
+
"use Exception; // a common import",
|
782
|
+
]
|
783
|
+
}
|
784
|
+
|
785
|
+
createPHPClass (className, baseClass, body, methods, async = false) {
|
786
|
+
|
787
|
+
let bodyAsString = body.join ("\n")
|
788
|
+
|
789
|
+
let header = this.createPHPClassHeader (className, baseClass, bodyAsString, async ? 'ccxt\\async' : 'ccxt')
|
790
|
+
|
791
|
+
const errorImports = []
|
792
|
+
|
793
|
+
for (let error in errors) {
|
794
|
+
const regex = new RegExp ("[^'\"]" + error + "[^'\"]")
|
795
|
+
if (bodyAsString.match (regex)) {
|
796
|
+
errorImports.push ('use \\ccxt\\' + error + ';')
|
797
|
+
}
|
798
|
+
}
|
799
|
+
|
800
|
+
const precisionImports = []
|
801
|
+
|
802
|
+
if (async && bodyAsString.match (/[\s(]Precise/)) {
|
803
|
+
precisionImports.push ('use \\ccxt\\Precise;')
|
804
|
+
}
|
805
|
+
|
806
|
+
header = header.concat (errorImports).concat (precisionImports)
|
807
|
+
|
808
|
+
methods = methods.concat (this.getPHPBaseMethods ())
|
809
|
+
|
810
|
+
for (let method of methods) {
|
811
|
+
const regex = new RegExp ('\\$this->(' + method + ')\\s?(\\(|[^a-zA-Z0-9_])', 'g')
|
812
|
+
bodyAsString = bodyAsString.replace (regex,
|
813
|
+
(match, p1, p2) => {
|
814
|
+
return ((p2 === '(') ?
|
815
|
+
('$this->' + unCamelCase (p1) + p2) : // support direct php calls
|
816
|
+
("array($this, '" + unCamelCase (p1) + "')" + p2)) // as well as passing instance methods as callables
|
817
|
+
})
|
818
|
+
}
|
819
|
+
|
820
|
+
header.push ("\n" + this.createPHPClassDeclaration (className, baseClass))
|
821
|
+
|
822
|
+
const footer =[
|
823
|
+
"}\n",
|
824
|
+
]
|
825
|
+
|
826
|
+
const result = header.join ("\n") + "\n" + bodyAsString + "\n" + footer.join ('\n')
|
827
|
+
return result
|
828
|
+
}
|
829
|
+
|
830
|
+
// ========================================================================
|
831
|
+
|
832
|
+
transpileJavaScriptToPython3 ({ js, className, removeEmptyLines }) {
|
833
|
+
|
834
|
+
// transpile JS → Python 3
|
835
|
+
let python3Body = this.regexAll (js, this.getPythonRegexes ())
|
836
|
+
|
837
|
+
if (removeEmptyLines)
|
838
|
+
python3Body = python3Body.replace (/$\s*$/gm, '')
|
839
|
+
|
840
|
+
python3Body = python3Body.replace (/\'([абвгдеёжзийклмнопрстуфхцчшщъыьэюя服务端忙碌]+)\'/gm, "u'$1'")
|
841
|
+
|
842
|
+
// special case for Python OrderedDicts
|
843
|
+
let orderedDictRegex = /\.ordered\s+\(\{([^\}]+)\}\)/g
|
844
|
+
let orderedDictMatches = undefined
|
845
|
+
while (orderedDictMatches = orderedDictRegex.exec (python3Body)) {
|
846
|
+
let replaced = orderedDictMatches[1].replace (/^(\s+)([^\:]+)\:\s*([^\,]+)\,$/gm, '$1($2, $3),')
|
847
|
+
python3Body = python3Body.replace (orderedDictRegex, '\.ordered([' + replaced + '])')
|
848
|
+
}
|
849
|
+
|
850
|
+
// snake case function names
|
851
|
+
python3Body = python3Body.replace (/def (\w+)/g, (match, group1) => 'def ' + unCamelCase (group1))
|
852
|
+
|
853
|
+
// special case for Python super
|
854
|
+
if (className) {
|
855
|
+
python3Body = python3Body.replace (/super\./g, 'super(' + className + ', self).')
|
856
|
+
}
|
857
|
+
|
858
|
+
return python3Body
|
859
|
+
}
|
860
|
+
|
861
|
+
// ------------------------------------------------------------------------
|
862
|
+
|
863
|
+
transpilePython3ToPython2 (py) {
|
864
|
+
|
865
|
+
// remove await from Python sync body (transpile Python async → Python sync)
|
866
|
+
let python2Body = this.regexAll (py, this.getPython2Regexes ())
|
867
|
+
|
868
|
+
return python2Body
|
869
|
+
}
|
870
|
+
|
871
|
+
// ------------------------------------------------------------------------
|
872
|
+
|
873
|
+
transpileAsyncPHPToSyncPHP (php) {
|
874
|
+
|
875
|
+
// remove yield from php body
|
876
|
+
return this.regexAll (php, this.getSyncPHPRegexes ())
|
877
|
+
}
|
878
|
+
|
879
|
+
// ------------------------------------------------------------------------
|
880
|
+
|
881
|
+
|
882
|
+
transpileJavaScriptToPHP ({ js, variables }) {
|
883
|
+
|
884
|
+
// match all local variables (let, const or var)
|
885
|
+
let localVariablesRegex = /(?:^|[^a-zA-Z0-9_])(?:let|const|var)\s+(?:\[([^\]]+)\]|([a-zA-Z0-9_]+))/g // local variables
|
886
|
+
|
887
|
+
let allVariables = (variables || []).map (x => x); // clone the array
|
888
|
+
// process the variables created in destructuring assignments as well
|
889
|
+
let localVariablesMatches
|
890
|
+
while (localVariablesMatches = localVariablesRegex.exec (js)) {
|
891
|
+
if (localVariablesMatches[1]) {
|
892
|
+
// this is a destructuring assignment like
|
893
|
+
// let [ a, b, c ] = 'a-b-c'.split ('-')
|
894
|
+
let matches = localVariablesMatches[1].trim ().split (', ') // split the destructuring assignment by comma
|
895
|
+
matches.forEach (x => allVariables.push (x.trim ())) // trim each variable name
|
896
|
+
} else {
|
897
|
+
// this is a single variable assignment
|
898
|
+
allVariables.push (localVariablesMatches[2].trim ()) // add it to the list of local variables
|
899
|
+
}
|
900
|
+
}
|
901
|
+
|
902
|
+
// match all variables instantiated in the catch()-block of a try-catch clause
|
903
|
+
let catchClauseRegex = /catch \(([^)]+)\)/g
|
904
|
+
let catchClauseMatches
|
905
|
+
while (catchClauseMatches = catchClauseRegex.exec (js)) {
|
906
|
+
allVariables.push (catchClauseMatches[1])
|
907
|
+
}
|
908
|
+
|
909
|
+
// match all variables instantiated as function parameters
|
910
|
+
let functionParamRegex = /function\s*(\w+)\s*\(([^)]+)\)/g
|
911
|
+
js = js.replace (functionParamRegex, (match, group1, group2) => 'function ' + unCamelCase (group1) + '(' + group2 + ')')
|
912
|
+
let functionParamVariables
|
913
|
+
while (functionParamVariables = functionParamRegex.exec (js)) {
|
914
|
+
const match = functionParamVariables[2]
|
915
|
+
const tokens = match.split (', ')
|
916
|
+
allVariables = allVariables.concat (tokens)
|
917
|
+
}
|
918
|
+
|
919
|
+
allVariables = allVariables.map (error => this.regexAll (error, this.getCommonRegexes ()))
|
920
|
+
|
921
|
+
// append $ to all variables in the method (PHP syntax demands $ at the beginning of a variable name)
|
922
|
+
let phpVariablesRegexes = allVariables.map (x => [ "(^|[^$$a-zA-Z0-9\\.\\>'\"_/])" + x + "([^a-zA-Z0-9'_/])", '$1$$' + x + '$2' ])
|
923
|
+
|
924
|
+
// support for php syntax for object-pointer dereference
|
925
|
+
// convert all $variable.property to $variable->property
|
926
|
+
let variablePropertiesRegexes = allVariables.map (x => [ "(^|[^a-zA-Z0-9\\.\\>'\"_])" + x + '\\.', '$1' + x + '->' ])
|
927
|
+
|
928
|
+
// transpile JS → PHP
|
929
|
+
const phpRegexes = this.getPHPRegexes ()
|
930
|
+
let phpBody = this.regexAll (js, phpRegexes.concat (phpVariablesRegexes).concat (variablePropertiesRegexes))
|
931
|
+
|
932
|
+
return phpBody
|
933
|
+
}
|
934
|
+
|
935
|
+
// ------------------------------------------------------------------------
|
936
|
+
|
937
|
+
transpileJavaScriptToPythonAndPHP (args) {
|
938
|
+
|
939
|
+
// transpile JS → Python 3
|
940
|
+
let python3Body = this.transpileJavaScriptToPython3 (args)
|
941
|
+
|
942
|
+
// remove await from Python sync body (transpile Python async → Python sync)
|
943
|
+
let python2Body = this.transpilePython3ToPython2 (python3Body)
|
944
|
+
|
945
|
+
// transpile JS → Async PHP
|
946
|
+
let phpAsyncBody = this.transpileJavaScriptToPHP (args)
|
947
|
+
|
948
|
+
// transpile async PHP -> sync PHP
|
949
|
+
let phpBody = this.transpileAsyncPHPToSyncPHP (phpAsyncBody)
|
950
|
+
|
951
|
+
return { python3Body, python2Body, phpBody, phpAsyncBody }
|
952
|
+
}
|
953
|
+
|
954
|
+
//-----------------------------------------------------------------------------
|
955
|
+
|
956
|
+
transpilePythonAsyncToSync () {
|
957
|
+
|
958
|
+
const async = './python/ccxt/test/test_async.py'
|
959
|
+
const sync = './python/ccxt/test/test_sync.py'
|
960
|
+
log.magenta ('Transpiling ' + async .yellow + ' → ' + sync.yellow)
|
961
|
+
const fileContents = fs.readFileSync (async, 'utf8')
|
962
|
+
let lines = fileContents.split ("\n")
|
963
|
+
|
964
|
+
lines = lines.filter (line => ![ 'import asyncio' ].includes (line))
|
965
|
+
.map (line => {
|
966
|
+
return (
|
967
|
+
line.replace ('asyncio.get_event_loop().run_until_complete(main())', 'main()')
|
968
|
+
.replace ('asyncio.run(main())', 'main()')
|
969
|
+
.replace ('import ccxt.async_support as ccxt', 'import ccxt')
|
970
|
+
.replace (/.*token\_bucket.*/g, '')
|
971
|
+
.replace ('await asyncio.sleep', 'time.sleep')
|
972
|
+
.replace ('async ', '')
|
973
|
+
.replace ('await ', ''))
|
974
|
+
})
|
975
|
+
|
976
|
+
// lines.forEach (line => log (line))
|
977
|
+
|
978
|
+
function deleteFunction (f, from) {
|
979
|
+
// the following regexes make a technical error
|
980
|
+
// since it won't cut away a single function
|
981
|
+
// it will delete everything up to the beginning of the next comment
|
982
|
+
const re1 = new RegExp ('def ' + f + '[^\#]+', 'g')
|
983
|
+
const re2 = new RegExp ('[\\s]+' + f + '\\(exchange\\)', 'g')
|
984
|
+
return from.replace (re1, '').replace (re2, '')
|
985
|
+
}
|
986
|
+
|
987
|
+
let newContents = lines.join ('\n')
|
988
|
+
|
989
|
+
newContents = deleteFunction ('test_tickers_async', newContents)
|
990
|
+
newContents = deleteFunction ('test_l2_order_books_async', newContents)
|
991
|
+
|
992
|
+
fs.truncateSync (sync)
|
993
|
+
fs.writeFileSync (sync, newContents)
|
994
|
+
}
|
995
|
+
|
996
|
+
//-----------------------------------------------------------------------------
|
997
|
+
|
998
|
+
transpilePhpAsyncToSync () {
|
999
|
+
|
1000
|
+
const async = './php/test/test_async.php'
|
1001
|
+
const sync = './php/test/test_sync.php'
|
1002
|
+
log.magenta ('Transpiling ' + async .yellow + ' → ' + sync.yellow)
|
1003
|
+
const fileContents = fs.readFileSync (async, 'utf8')
|
1004
|
+
const syncBody = this.transpileAsyncPHPToSyncPHP (fileContents)
|
1005
|
+
|
1006
|
+
const phpSyncRegexes = [
|
1007
|
+
[ /\$kernel->execute\(\$main\)/, '\$main()' ],
|
1008
|
+
[ /\$kernel->run\(\);\n/, '' ],
|
1009
|
+
[ /\$kernel = async\\Exchange::get_kernel\(\);\n/, '' ],
|
1010
|
+
[ /ccxt\\\\async/, 'ccxt' ],
|
1011
|
+
]
|
1012
|
+
|
1013
|
+
const newContents = this.regexAll (syncBody, phpSyncRegexes)
|
1014
|
+
|
1015
|
+
fs.truncateSync (sync)
|
1016
|
+
fs.writeFileSync (sync, newContents)
|
1017
|
+
}
|
1018
|
+
|
1019
|
+
// ------------------------------------------------------------------------
|
1020
|
+
|
1021
|
+
getExchangeClassDeclarationMatches (contents) {
|
1022
|
+
return contents.match (/^module\.exports\s*=\s*class\s+([\S]+)\s+extends\s+([\S]+)\s+{([\s\S]+?)^};*/m)
|
1023
|
+
}
|
1024
|
+
|
1025
|
+
// ------------------------------------------------------------------------
|
1026
|
+
|
1027
|
+
transpileDerivedExchangeClass (contents, methodNames = undefined) {
|
1028
|
+
|
1029
|
+
const [ _, className, baseClass, methodMatches ] = this.getExchangeClassDeclarationMatches (contents)
|
1030
|
+
|
1031
|
+
const methods = methodMatches.trim ().split (/\n\s*\n/)
|
1032
|
+
|
1033
|
+
let python2 = []
|
1034
|
+
let python3 = []
|
1035
|
+
let php = []
|
1036
|
+
let phpAsync = []
|
1037
|
+
|
1038
|
+
methodNames = [] // methodNames || []
|
1039
|
+
|
1040
|
+
// run through all methods
|
1041
|
+
for (let i = 0; i < methods.length; i++) {
|
1042
|
+
// parse the method signature
|
1043
|
+
let part = methods[i].trim ()
|
1044
|
+
let lines = part.split ("\n")
|
1045
|
+
let signature = lines[0].trim ()
|
1046
|
+
let methodSignatureRegex = /(async |)([\S]+)\s\(([^)]*)\)\s*{/ // signature line
|
1047
|
+
let matches = methodSignatureRegex.exec (signature)
|
1048
|
+
|
1049
|
+
if (!matches) {
|
1050
|
+
log.red (methods[i])
|
1051
|
+
log.yellow.bright ("\nMake sure your methods don't have empty lines!\n")
|
1052
|
+
}
|
1053
|
+
|
1054
|
+
// async or not
|
1055
|
+
let keyword = matches[1]
|
1056
|
+
|
1057
|
+
// method name
|
1058
|
+
let method = matches[2]
|
1059
|
+
|
1060
|
+
methodNames.push (method)
|
1061
|
+
|
1062
|
+
method = unCamelCase (method)
|
1063
|
+
|
1064
|
+
// method arguments
|
1065
|
+
let args = matches[3].trim ()
|
1066
|
+
|
1067
|
+
// extract argument names and local variables
|
1068
|
+
args = args.length ? args.split (',').map (x => x.trim ()) : []
|
1069
|
+
|
1070
|
+
// get names of all method arguments for later substitutions
|
1071
|
+
let variables = args.map (arg => arg.split ('=').map (x => x.trim ()) [0])
|
1072
|
+
|
1073
|
+
// add $ to each argument name in PHP method signature
|
1074
|
+
let phpArgs = args.join (', $').trim ().replace (/undefined/g, 'null').replace (/\{\}/g, 'array ()')
|
1075
|
+
phpArgs = phpArgs.length ? ('$' + phpArgs) : ''
|
1076
|
+
|
1077
|
+
// remove excessive spacing from argument defaults in Python method signature
|
1078
|
+
let pythonArgs = args.map (x => x.replace (' = ', '='))
|
1079
|
+
.join (', ')
|
1080
|
+
.replace (/undefined/g, 'None')
|
1081
|
+
.replace (/false/g, 'False')
|
1082
|
+
.replace (/true/g, 'True')
|
1083
|
+
|
1084
|
+
// method body without the signature (first line)
|
1085
|
+
// and without the closing bracket (last line)
|
1086
|
+
let js = lines.slice (1, -1).join ("\n")
|
1087
|
+
|
1088
|
+
// transpile everything
|
1089
|
+
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, className, variables, removeEmptyLines: true })
|
1090
|
+
|
1091
|
+
// compile the final Python code for the method signature
|
1092
|
+
let pythonString = 'def ' + method + '(self' + (pythonArgs.length ? ', ' + pythonArgs : '') + '):'
|
1093
|
+
|
1094
|
+
// compile signature + body for Python sync
|
1095
|
+
python2.push ('');
|
1096
|
+
python2.push (' ' + pythonString);
|
1097
|
+
python2.push (python2Body);
|
1098
|
+
|
1099
|
+
// compile signature + body for Python async
|
1100
|
+
python3.push ('');
|
1101
|
+
python3.push (' ' + keyword + pythonString);
|
1102
|
+
python3.push (python3Body);
|
1103
|
+
|
1104
|
+
// compile signature + body for PHP
|
1105
|
+
php.push ('');
|
1106
|
+
php.push (' public function ' + method + '(' + phpArgs + ') {');
|
1107
|
+
php.push (phpBody);
|
1108
|
+
php.push (' }')
|
1109
|
+
|
1110
|
+
phpAsync.push ('');
|
1111
|
+
phpAsync.push (' public function ' + method + '(' + phpArgs + ') {');
|
1112
|
+
phpAsync.push (phpAsyncBody);
|
1113
|
+
phpAsync.push (' }')
|
1114
|
+
}
|
1115
|
+
|
1116
|
+
return {
|
1117
|
+
|
1118
|
+
// altogether in PHP, async PHP, Python sync and async
|
1119
|
+
python2: this.createPythonClass (className, baseClass, python2, methodNames),
|
1120
|
+
python3: this.createPythonClass (className, baseClass, python3, methodNames, true),
|
1121
|
+
php: this.createPHPClass (className, baseClass, php, methodNames),
|
1122
|
+
phpAsync: this.createPHPClass (className, baseClass, phpAsync, methodNames, true),
|
1123
|
+
|
1124
|
+
className,
|
1125
|
+
baseClass,
|
1126
|
+
}
|
1127
|
+
}
|
1128
|
+
|
1129
|
+
// ========================================================================
|
1130
|
+
|
1131
|
+
transpileDerivedExchangeFile (jsFolder, filename, options, force = false) {
|
1132
|
+
|
1133
|
+
// todo normalize jsFolder and other arguments
|
1134
|
+
|
1135
|
+
try {
|
1136
|
+
|
1137
|
+
const { python2Folder, python3Folder, phpFolder, phpAsyncFolder } = options
|
1138
|
+
const pythonFilename = filename.replace ('.js', '.py')
|
1139
|
+
const phpFilename = filename.replace ('.js', '.php')
|
1140
|
+
|
1141
|
+
const jsPath = jsFolder + filename
|
1142
|
+
|
1143
|
+
let contents = fs.readFileSync (jsPath, 'utf8')
|
1144
|
+
const sortedExchangeCapabilities = this.sortExchangeCapabilities (contents)
|
1145
|
+
if (sortedExchangeCapabilities) {
|
1146
|
+
contents = sortedExchangeCapabilities
|
1147
|
+
overwriteFile (jsPath, contents)
|
1148
|
+
}
|
1149
|
+
|
1150
|
+
const jsMtime = fs.statSync (jsPath).mtime.getTime ()
|
1151
|
+
|
1152
|
+
const python2Path = python2Folder ? (python2Folder + pythonFilename) : undefined
|
1153
|
+
const python3Path = python3Folder ? (python3Folder + pythonFilename) : undefined
|
1154
|
+
const phpPath = phpFolder ? (phpFolder + phpFilename) : undefined
|
1155
|
+
const phpAsyncPath = phpAsyncFolder ? (phpAsyncFolder + phpFilename) : undefined
|
1156
|
+
|
1157
|
+
const python2Mtime = python2Folder ? (fs.existsSync (python2Path) ? fs.statSync (python2Path).mtime.getTime () : 0) : undefined
|
1158
|
+
const python3Mtime = python3Path ? (fs.existsSync (python3Path) ? fs.statSync (python3Path).mtime.getTime () : 0) : undefined
|
1159
|
+
const phpAsyncMtime = phpAsyncFolder ? (fs.existsSync (phpAsyncPath) ? fs.statSync (phpAsyncPath).mtime.getTime () : 0) : undefined
|
1160
|
+
const phpMtime = phpPath ? (fs.existsSync (phpPath) ? fs.statSync (phpPath).mtime.getTime () : 0) : undefined
|
1161
|
+
|
1162
|
+
if (force ||
|
1163
|
+
(python3Folder && (jsMtime > python3Mtime)) ||
|
1164
|
+
(phpFolder && (jsMtime > phpMtime)) ||
|
1165
|
+
(phpAsyncFolder && (jsMtime > phpAsyncMtime)) ||
|
1166
|
+
(python2Folder && (jsMtime > python2Mtime))) {
|
1167
|
+
const { python2, python3, php, phpAsync, className, baseClass } = this.transpileDerivedExchangeClass (contents)
|
1168
|
+
log.cyan ('Transpiling from', filename.yellow)
|
1169
|
+
|
1170
|
+
;[
|
1171
|
+
[ python2Folder, pythonFilename, python2 ],
|
1172
|
+
[ python3Folder, pythonFilename, python3 ],
|
1173
|
+
[ phpFolder, phpFilename, php ],
|
1174
|
+
[ phpAsyncFolder, phpFilename, phpAsync ],
|
1175
|
+
].forEach (([ folder, filename, code ]) => {
|
1176
|
+
if (folder) {
|
1177
|
+
overwriteFile (folder + filename, code)
|
1178
|
+
fs.utimesSync (folder + filename, new Date (), new Date (jsMtime))
|
1179
|
+
}
|
1180
|
+
})
|
1181
|
+
|
1182
|
+
return { className, baseClass }
|
1183
|
+
|
1184
|
+
} else {
|
1185
|
+
|
1186
|
+
const [ _, className, baseClass ] = this.getExchangeClassDeclarationMatches (contents)
|
1187
|
+
log.green ('Already transpiled', filename.yellow)
|
1188
|
+
return { className, baseClass }
|
1189
|
+
}
|
1190
|
+
|
1191
|
+
} catch (e) {
|
1192
|
+
|
1193
|
+
log.red ('\nFailed to transpile source code from', filename.yellow)
|
1194
|
+
log.red ('See https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md on how to build this library properly\n')
|
1195
|
+
throw e // rethrow it
|
1196
|
+
}
|
1197
|
+
}
|
1198
|
+
|
1199
|
+
//-------------------------------------------------------------------------
|
1200
|
+
|
1201
|
+
transpileDerivedExchangeFiles (jsFolder, options, pattern = '.js', force = false) {
|
1202
|
+
|
1203
|
+
// todo normalize jsFolder and other arguments
|
1204
|
+
|
1205
|
+
const { python2Folder, python3Folder, phpFolder, phpAsyncFolder } = options
|
1206
|
+
|
1207
|
+
// exchanges.json accounts for ids included in exchanges.cfg
|
1208
|
+
let ids = undefined
|
1209
|
+
try {
|
1210
|
+
ids = require ('../exchanges.json').ids;
|
1211
|
+
} catch (e) {
|
1212
|
+
}
|
1213
|
+
|
1214
|
+
const regex = new RegExp (pattern.replace (/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
1215
|
+
|
1216
|
+
const classNames = fs.readdirSync (jsFolder)
|
1217
|
+
.filter (file => file.match (regex) && (!ids || ids.includes (basename (file, '.js'))))
|
1218
|
+
.map (file => this.transpileDerivedExchangeFile (jsFolder, file, options, force))
|
1219
|
+
|
1220
|
+
const classes = {}
|
1221
|
+
|
1222
|
+
if (classNames.length === 0) {
|
1223
|
+
return null
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
classNames.forEach (({ className, baseClass }) => {
|
1227
|
+
classes[className] = baseClass
|
1228
|
+
})
|
1229
|
+
|
1230
|
+
if (classNames.length > 1) {
|
1231
|
+
|
1232
|
+
function deleteOldTranspiledFiles (folder, pattern) {
|
1233
|
+
fs.readdirSync (folder)
|
1234
|
+
.filter (file =>
|
1235
|
+
!fs.lstatSync (folder + file).isDirectory () &&
|
1236
|
+
!(file.replace (pattern, '') in classes) &&
|
1237
|
+
!file.match (/^[A-Z_]/))
|
1238
|
+
.map (file => folder + file)
|
1239
|
+
.forEach (file => log.red ('Deleting ' + file.yellow) && fs.unlinkSync (file))
|
1240
|
+
}
|
1241
|
+
|
1242
|
+
[
|
1243
|
+
[ python2Folder, /\.pyc?$/ ],
|
1244
|
+
[ python3Folder, /\.pyc?$/ ],
|
1245
|
+
[ phpFolder, /\.php$/ ],
|
1246
|
+
[ phpAsyncFolder, /\.php$/ ],
|
1247
|
+
].forEach (([ folder, pattern ]) => {
|
1248
|
+
if (folder) {
|
1249
|
+
deleteOldTranspiledFiles (folder, pattern)
|
1250
|
+
}
|
1251
|
+
})
|
1252
|
+
|
1253
|
+
}
|
1254
|
+
|
1255
|
+
return classes
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
// ========================================================================
|
1259
|
+
|
1260
|
+
exportTypeScriptClassNames (file, classes) {
|
1261
|
+
|
1262
|
+
log.bright.cyan ('Exporting TypeScript class names →', file.yellow)
|
1263
|
+
|
1264
|
+
const regex = /\/[\n]{2}(?: export class [^\s]+ extends [^\s]+ \{\}[\r]?[\n])+/
|
1265
|
+
const replacement = "/\n\n" + Object.keys (classes).map (className => {
|
1266
|
+
const baseClass = classes[className].replace (/ccxt\.[a-z0-9_]+/, 'Exchange')
|
1267
|
+
return ' export class ' + className + ' extends ' + baseClass + " {}"
|
1268
|
+
}).join ("\n") + "\n"
|
1269
|
+
|
1270
|
+
replaceInFile (file, regex, replacement)
|
1271
|
+
}
|
1272
|
+
|
1273
|
+
exportTypeScriptExchangeIds (file, classes) {
|
1274
|
+
|
1275
|
+
log.bright.cyan ('Exporting TypeScript exchange ids →', file.yellow)
|
1276
|
+
|
1277
|
+
const regex = /\/[\n]{2} export type ExchangeId =\n(?: \| \'[a-z0-9_]+\'[\r]?[\n])+/
|
1278
|
+
const replacement = "/\n\n export type ExchangeId =\n" + Object.keys (classes).map (className => {
|
1279
|
+
return " | '" + className + "'"
|
1280
|
+
}).join ("\n") + "\n"
|
1281
|
+
|
1282
|
+
replaceInFile (file, regex, replacement)
|
1283
|
+
}
|
1284
|
+
|
1285
|
+
exportTypeScriptDeclarations (file, classes) {
|
1286
|
+
|
1287
|
+
this.exportTypeScriptClassNames (file, classes)
|
1288
|
+
this.exportTypeScriptExchangeIds (file, classes)
|
1289
|
+
}
|
1290
|
+
|
1291
|
+
// ========================================================================
|
1292
|
+
|
1293
|
+
transpileErrorHierarchy ({ tsFilename }) {
|
1294
|
+
|
1295
|
+
const errorHierarchyFilename = './js/base/errorHierarchy.js'
|
1296
|
+
const errorHierarchyPath = __dirname + '/.' + errorHierarchyFilename
|
1297
|
+
const errorHierarchy = require (errorHierarchyPath)
|
1298
|
+
|
1299
|
+
let js = fs.readFileSync (errorHierarchyPath, 'utf8')
|
1300
|
+
|
1301
|
+
js = this.regexAll (js, [
|
1302
|
+
[ /module\.exports = [^\;]+\;\n/s, '' ],
|
1303
|
+
]).trim ()
|
1304
|
+
|
1305
|
+
const message = 'Transpiling error hierachy →'
|
1306
|
+
const root = errorHierarchy['BaseError']
|
1307
|
+
|
1308
|
+
const { python3Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js })
|
1309
|
+
|
1310
|
+
// a helper to generate a list of exception class declarations
|
1311
|
+
// properly derived from corresponding parent classes according
|
1312
|
+
// to the error hierarchy
|
1313
|
+
|
1314
|
+
function intellisense (map, parent, generate, classes) {
|
1315
|
+
function* generator(map, parent, generate, classes) {
|
1316
|
+
for (const key in map) {
|
1317
|
+
yield generate (key, parent, classes)
|
1318
|
+
yield* generator (map[key], key, generate, classes)
|
1319
|
+
}
|
1320
|
+
}
|
1321
|
+
return Array.from (generator (map, parent, generate, classes))
|
1322
|
+
}
|
1323
|
+
|
1324
|
+
// Python -------------------------------------------------------------
|
1325
|
+
|
1326
|
+
function pythonDeclareErrorClass (name, parent, classes) {
|
1327
|
+
classes.push (name)
|
1328
|
+
return [
|
1329
|
+
'class ' + name + '(' + parent + '):',
|
1330
|
+
' pass',
|
1331
|
+
'',
|
1332
|
+
'',
|
1333
|
+
].join ('\n');
|
1334
|
+
}
|
1335
|
+
|
1336
|
+
const pythonBaseError = [
|
1337
|
+
'class BaseError(Exception):',
|
1338
|
+
' pass',
|
1339
|
+
'',
|
1340
|
+
'',
|
1341
|
+
].join ('\n');
|
1342
|
+
|
1343
|
+
const quote = (s) => "'" + s + "'" // helper to add quotes around class names
|
1344
|
+
const pythonExports = [ 'error_hierarchy', 'BaseError' ]
|
1345
|
+
const pythonErrors = intellisense (root, 'BaseError', pythonDeclareErrorClass, pythonExports)
|
1346
|
+
const pythonAll = '__all__ = [\n ' + pythonExports.map (quote).join (',\n ') + '\n]'
|
1347
|
+
const python3BodyIntellisense = python3Body + '\n\n\n' + pythonBaseError + '\n' + pythonErrors.join ('\n') + '\n' + pythonAll + '\n'
|
1348
|
+
|
1349
|
+
const pythonFilename = './python/ccxt/base/errors.py'
|
1350
|
+
if (fs.existsSync (pythonFilename)) {
|
1351
|
+
log.bright.cyan (message, pythonFilename.yellow)
|
1352
|
+
fs.writeFileSync (pythonFilename, python3BodyIntellisense)
|
1353
|
+
}
|
1354
|
+
|
1355
|
+
// PHP ----------------------------------------------------------------
|
1356
|
+
|
1357
|
+
function phpMakeErrorClassFile (name, parent) {
|
1358
|
+
|
1359
|
+
const useClause = "\nuse " + parent + ";\n"
|
1360
|
+
const requireClause = "\nrequire_once PATH_TO_CCXT . '" + parent + ".php';\n"
|
1361
|
+
|
1362
|
+
const phpBody = [
|
1363
|
+
'<?php',
|
1364
|
+
'',
|
1365
|
+
'namespace ccxt;',
|
1366
|
+
(parent === 'Exception') ? useClause : requireClause,
|
1367
|
+
'class ' + name + ' extends ' + parent + ' {};',
|
1368
|
+
'',
|
1369
|
+
].join ("\n")
|
1370
|
+
const phpFilename = './php/' + name + '.php'
|
1371
|
+
log.bright.cyan (message, phpFilename.yellow)
|
1372
|
+
fs.writeFileSync (phpFilename, phpBody)
|
1373
|
+
return "require_once PATH_TO_CCXT . '" + name + ".php';"
|
1374
|
+
}
|
1375
|
+
|
1376
|
+
const phpFilename ='./ccxt.php'
|
1377
|
+
|
1378
|
+
if (fs.existsSync (phpFilename)) {
|
1379
|
+
const phpErrors = intellisense (errorHierarchy, 'Exception', phpMakeErrorClassFile)
|
1380
|
+
const phpBodyIntellisense = phpErrors.join ("\n") + "\n\n"
|
1381
|
+
log.bright.cyan (message, phpFilename.yellow)
|
1382
|
+
const phpRegex = /require_once PATH_TO_CCXT \. \'BaseError\.php\'\;\n(?:require_once PATH_TO_CCXT[^\n]+\n)+\n/m
|
1383
|
+
replaceInFile (phpFilename, phpRegex, phpBodyIntellisense)
|
1384
|
+
}
|
1385
|
+
|
1386
|
+
// TypeScript ---------------------------------------------------------
|
1387
|
+
|
1388
|
+
function tsDeclareErrorClass (name, parent) {
|
1389
|
+
return 'export class ' + name + ' extends ' + parent + ' {}'
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
const tsBaseError = [
|
1393
|
+
'export class BaseError extends Error {',
|
1394
|
+
' constructor(message: string);',
|
1395
|
+
'}',
|
1396
|
+
].join ('\n ')
|
1397
|
+
|
1398
|
+
const tsErrors = intellisense (root, 'BaseError', tsDeclareErrorClass)
|
1399
|
+
|
1400
|
+
const tsBodyIntellisense = tsBaseError + '\n\n ' + tsErrors.join ('\n ') + '\n\n'
|
1401
|
+
|
1402
|
+
log.bright.cyan (message, tsFilename.yellow)
|
1403
|
+
const regex = /export class BaseError[^}]+\}[\n][\n](?:\s+export class [a-zA-Z]+ extends [a-zA-Z]+ \{\}[\n])+[\n]/m
|
1404
|
+
replaceInFile (tsFilename, regex, tsBodyIntellisense)
|
1405
|
+
}
|
1406
|
+
|
1407
|
+
//-----------------------------------------------------------------------------
|
1408
|
+
|
1409
|
+
transpileDateTimeTests () {
|
1410
|
+
const jsFile = './js/test/base/functions/test.datetime.js'
|
1411
|
+
const pyFile = './python/ccxt/test/test_exchange_datetime_functions.py'
|
1412
|
+
const phpFile = './php/test/test_exchange_datetime_functions.php'
|
1413
|
+
|
1414
|
+
log.magenta ('Transpiling from', jsFile.yellow)
|
1415
|
+
|
1416
|
+
let js = fs.readFileSync (jsFile).toString ()
|
1417
|
+
|
1418
|
+
js = this.regexAll (js, [
|
1419
|
+
[ /[^\n]+require[^\n]+\n/g, '' ],
|
1420
|
+
[/^\/\*.*\s+/mg, ''],
|
1421
|
+
])
|
1422
|
+
|
1423
|
+
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
|
1424
|
+
|
1425
|
+
// phpBody = phpBody.replace (/exchange\./g, 'Exchange::')
|
1426
|
+
|
1427
|
+
const pythonHeader = [
|
1428
|
+
"",
|
1429
|
+
"import ccxt # noqa: F402",
|
1430
|
+
"from ccxt.base.decimal_to_precision import ROUND_UP, ROUND_DOWN # noqa F401",
|
1431
|
+
"",
|
1432
|
+
"# ----------------------------------------------------------------------------",
|
1433
|
+
"",
|
1434
|
+
"",
|
1435
|
+
].join ("\n")
|
1436
|
+
|
1437
|
+
const python = this.getPythonPreamble () + pythonHeader + python2Body
|
1438
|
+
const php = this.getPHPPreamble () + phpBody
|
1439
|
+
|
1440
|
+
log.magenta ('→', pyFile.yellow)
|
1441
|
+
log.magenta ('→', phpFile.yellow)
|
1442
|
+
|
1443
|
+
overwriteFile (pyFile, python)
|
1444
|
+
overwriteFile (phpFile, php)
|
1445
|
+
}
|
1446
|
+
|
1447
|
+
//-------------------------------------------------------------------------
|
1448
|
+
|
1449
|
+
transpilePrecisionTests () {
|
1450
|
+
|
1451
|
+
const jsFile = './js/test/base/functions/test.number.js'
|
1452
|
+
const pyFile = './python/ccxt/test/test_decimal_to_precision.py'
|
1453
|
+
const phpFile = './php/test/decimal_to_precision.php'
|
1454
|
+
|
1455
|
+
log.magenta ('Transpiling from', jsFile.yellow)
|
1456
|
+
|
1457
|
+
let js = fs.readFileSync (jsFile).toString ()
|
1458
|
+
|
1459
|
+
js = this.regexAll (js, [
|
1460
|
+
[ /\'use strict\';?\s+/g, '' ],
|
1461
|
+
[ /[^\n]+require[^\n]+\n/g, '' ],
|
1462
|
+
[ /decimalToPrecision/g, 'decimal_to_precision' ],
|
1463
|
+
[ /numberToString/g, 'number_to_string' ],
|
1464
|
+
])
|
1465
|
+
|
1466
|
+
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
|
1467
|
+
|
1468
|
+
const pythonHeader = [
|
1469
|
+
"",
|
1470
|
+
"from ccxt.base.decimal_to_precision import decimal_to_precision # noqa F401",
|
1471
|
+
"from ccxt.base.decimal_to_precision import TRUNCATE # noqa F401",
|
1472
|
+
"from ccxt.base.decimal_to_precision import ROUND # noqa F401",
|
1473
|
+
"from ccxt.base.decimal_to_precision import DECIMAL_PLACES # noqa F401",
|
1474
|
+
"from ccxt.base.decimal_to_precision import SIGNIFICANT_DIGITS # noqa F401",
|
1475
|
+
"from ccxt.base.decimal_to_precision import TICK_SIZE # noqa F401",
|
1476
|
+
"from ccxt.base.decimal_to_precision import PAD_WITH_ZERO # noqa F401",
|
1477
|
+
"from ccxt.base.decimal_to_precision import NO_PADDING # noqa F401",
|
1478
|
+
"from ccxt.base.decimal_to_precision import number_to_string # noqa F401",
|
1479
|
+
"from ccxt.base.exchange import Exchange # noqa F401",
|
1480
|
+
"from ccxt.base.precise import Precise # noqa F401",
|
1481
|
+
"",
|
1482
|
+
"",
|
1483
|
+
].join ("\n")
|
1484
|
+
|
1485
|
+
const phpHeader = [
|
1486
|
+
"",
|
1487
|
+
"include_once (__DIR__.'/fail_on_all_errors.php');",
|
1488
|
+
"",
|
1489
|
+
"// testDecimalToPrecisionErrorHandling",
|
1490
|
+
"//",
|
1491
|
+
"// $this->expectException ('ccxt\\\\BaseError');",
|
1492
|
+
"// $this->expectExceptionMessageRegExp ('/Negative precision is not yet supported/');",
|
1493
|
+
"// Exchange::decimalToPrecision ('123456.789', TRUNCATE, -2, DECIMAL_PLACES);",
|
1494
|
+
"//",
|
1495
|
+
"// $this->expectException ('ccxt\\\\BaseError');",
|
1496
|
+
"// $this->expectExceptionMessageRegExp ('/Invalid number/');",
|
1497
|
+
"// Exchange::decimalToPrecision ('foo');",
|
1498
|
+
"",
|
1499
|
+
"// ----------------------------------------------------------------------------",
|
1500
|
+
"",
|
1501
|
+
"function decimal_to_precision ($x, $roundingMode = ROUND, $numPrecisionDigits = null, $countingMode = DECIMAL_PLACES, $paddingMode = NO_PADDING) {",
|
1502
|
+
" return Exchange::decimal_to_precision ($x, $roundingMode, $numPrecisionDigits, $countingMode, $paddingMode);",
|
1503
|
+
"}",
|
1504
|
+
"function number_to_string ($x) {",
|
1505
|
+
" return Exchange::number_to_string ($x);",
|
1506
|
+
"}",
|
1507
|
+
"",
|
1508
|
+
].join ("\n")
|
1509
|
+
|
1510
|
+
const python = this.getPythonPreamble () + pythonHeader + python2Body
|
1511
|
+
const php = this.getPHPPreamble () + phpHeader + phpBody
|
1512
|
+
|
1513
|
+
log.magenta ('→', pyFile.yellow)
|
1514
|
+
log.magenta ('→', phpFile.yellow)
|
1515
|
+
|
1516
|
+
overwriteFile (pyFile, python)
|
1517
|
+
overwriteFile (phpFile, php)
|
1518
|
+
}
|
1519
|
+
|
1520
|
+
//-------------------------------------------------------------------------
|
1521
|
+
|
1522
|
+
transpileCryptoTests () {
|
1523
|
+
const jsFile = './js/test/base/functions/test.crypto.js'
|
1524
|
+
const pyFile = './python/ccxt/test/test_crypto.py'
|
1525
|
+
const phpFile = './php/test/test_crypto.php'
|
1526
|
+
|
1527
|
+
log.magenta ('Transpiling from', jsFile.yellow)
|
1528
|
+
let js = fs.readFileSync (jsFile).toString ()
|
1529
|
+
|
1530
|
+
js = this.regexAll (js, [
|
1531
|
+
[ /\'use strict\';?\s+/g, '' ],
|
1532
|
+
[ /[^\n]+require[^\n]+\n/g, '' ],
|
1533
|
+
[ /function equals \([\S\s]+?return true\n}\n/g, '' ],
|
1534
|
+
])
|
1535
|
+
|
1536
|
+
let { python3Body, python2Body, phpBody, phpAsyncBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
|
1537
|
+
|
1538
|
+
const pythonHeader = [
|
1539
|
+
"",
|
1540
|
+
"import ccxt # noqa: F402",
|
1541
|
+
"",
|
1542
|
+
"Exchange = ccxt.Exchange",
|
1543
|
+
"hash = Exchange.hash",
|
1544
|
+
"ecdsa = Exchange.ecdsa",
|
1545
|
+
"jwt = Exchange.jwt",
|
1546
|
+
"encode = Exchange.encode",
|
1547
|
+
"",
|
1548
|
+
"",
|
1549
|
+
"def equals(a, b):",
|
1550
|
+
" return a == b",
|
1551
|
+
"",
|
1552
|
+
].join ("\n")
|
1553
|
+
|
1554
|
+
const phpHeader = [
|
1555
|
+
"",
|
1556
|
+
"function hash(...$args) {",
|
1557
|
+
" return Exchange::hash(...$args);",
|
1558
|
+
"}",
|
1559
|
+
"",
|
1560
|
+
"function encode(...$args) {",
|
1561
|
+
" return Exchange::encode(...$args);",
|
1562
|
+
"}",
|
1563
|
+
"",
|
1564
|
+
"function ecdsa(...$args) {",
|
1565
|
+
" return Exchange::ecdsa(...$args);",
|
1566
|
+
"}",
|
1567
|
+
"",
|
1568
|
+
"function jwt(...$args) {",
|
1569
|
+
" return Exchange::jwt(...$args);",
|
1570
|
+
"}",
|
1571
|
+
"",
|
1572
|
+
"function equals($a, $b) {",
|
1573
|
+
" return $a === $b;",
|
1574
|
+
"}",
|
1575
|
+
].join ("\n")
|
1576
|
+
|
1577
|
+
const python = this.getPythonPreamble () + pythonHeader + python2Body
|
1578
|
+
const php = this.getPHPPreamble () + phpHeader + phpBody
|
1579
|
+
|
1580
|
+
log.magenta ('→', pyFile.yellow)
|
1581
|
+
log.magenta ('→', phpFile.yellow)
|
1582
|
+
|
1583
|
+
overwriteFile (pyFile, python)
|
1584
|
+
overwriteFile (phpFile, php)
|
1585
|
+
}
|
1586
|
+
|
1587
|
+
// ============================================================================
|
1588
|
+
|
1589
|
+
transpileExchangeTests () {
|
1590
|
+
const tests = [
|
1591
|
+
{
|
1592
|
+
'jsFile': './js/test/Exchange/test.market.js',
|
1593
|
+
'pyFile': './python/ccxt/test/test_market.py',
|
1594
|
+
'phpFile': './php/test/test_market.php',
|
1595
|
+
},
|
1596
|
+
{
|
1597
|
+
'jsFile': './js/test/Exchange/test.trade.js',
|
1598
|
+
'pyFile': './python/ccxt/test/test_trade.py',
|
1599
|
+
'phpFile': './php/test/test_trade.php',
|
1600
|
+
},
|
1601
|
+
{
|
1602
|
+
'jsFile': './js/test/Exchange/test.order.js',
|
1603
|
+
'pyFile': './python/ccxt/test/test_order.py',
|
1604
|
+
'phpFile': './php/test/test_order.php',
|
1605
|
+
},
|
1606
|
+
{
|
1607
|
+
'jsFile': './js/test/Exchange/test.position.js',
|
1608
|
+
'pyFile': './python/ccxt/test/test_position.py',
|
1609
|
+
'phpFile': './php/test/test_position.php',
|
1610
|
+
},
|
1611
|
+
{
|
1612
|
+
'jsFile': './js/test/Exchange/test.transaction.js',
|
1613
|
+
'pyFile': './python/ccxt/test/test_transaction.py',
|
1614
|
+
'phpFile': './php/test/test_transaction.php',
|
1615
|
+
},
|
1616
|
+
{
|
1617
|
+
'jsFile': './js/test/Exchange/test.ohlcv.js',
|
1618
|
+
'pyFile': './python/ccxt/test/test_ohlcv.py',
|
1619
|
+
'phpFile': './php/test/test_ohlcv.php',
|
1620
|
+
},
|
1621
|
+
{
|
1622
|
+
'jsFile': './js/test/Exchange/test.leverageTier.js',
|
1623
|
+
'pyFile': './python/ccxt/test/test_leverage_tier.py',
|
1624
|
+
'phpFile': './php/test/test_leverage_tier.php',
|
1625
|
+
},
|
1626
|
+
]
|
1627
|
+
for (const test of tests) {
|
1628
|
+
this.transpileTest (test)
|
1629
|
+
}
|
1630
|
+
}
|
1631
|
+
|
1632
|
+
// ============================================================================
|
1633
|
+
|
1634
|
+
transpileTest (test) {
|
1635
|
+
log.magenta ('Transpiling from', test.jsFile.yellow)
|
1636
|
+
let js = fs.readFileSync (test.jsFile).toString ()
|
1637
|
+
|
1638
|
+
js = this.regexAll (js, [
|
1639
|
+
[ /\'use strict\';?\s+/g, '' ],
|
1640
|
+
[ /[^\n]+require[^\n]+\n/g, '' ],
|
1641
|
+
[ /module.exports\s+=\s+[^;]+;/g, '' ],
|
1642
|
+
])
|
1643
|
+
|
1644
|
+
const pythonHeader = [
|
1645
|
+
'import numbers # noqa: E402',
|
1646
|
+
'',
|
1647
|
+
'',
|
1648
|
+
].join('\n')
|
1649
|
+
|
1650
|
+
let { python3Body, python2Body, phpBody } = this.transpileJavaScriptToPythonAndPHP ({ js, removeEmptyLines: false })
|
1651
|
+
const python = pythonHeader + python3Body;
|
1652
|
+
const php = this.getPHPPreamble (false) + phpBody;
|
1653
|
+
|
1654
|
+
log.magenta ('→', test.pyFile.yellow)
|
1655
|
+
log.magenta ('→', test.phpFile.yellow)
|
1656
|
+
|
1657
|
+
overwriteFile (test.pyFile, python)
|
1658
|
+
overwriteFile (test.phpFile, php)
|
1659
|
+
}
|
1660
|
+
|
1661
|
+
// ============================================================================
|
1662
|
+
|
1663
|
+
transpileTests () {
|
1664
|
+
|
1665
|
+
this.transpilePrecisionTests ()
|
1666
|
+
this.transpileDateTimeTests ()
|
1667
|
+
this.transpileCryptoTests ()
|
1668
|
+
|
1669
|
+
this.transpileExchangeTests ()
|
1670
|
+
}
|
1671
|
+
|
1672
|
+
// ============================================================================
|
1673
|
+
|
1674
|
+
transpilePhpBaseClassMethods () {
|
1675
|
+
const baseMethods = this.getPHPBaseMethods ()
|
1676
|
+
const indent = 4
|
1677
|
+
const space = ' '.repeat (indent)
|
1678
|
+
const result = [
|
1679
|
+
'public static $camelcase_methods = array(',
|
1680
|
+
]
|
1681
|
+
for (const method of baseMethods) {
|
1682
|
+
const underscoreCase = unCamelCase (method)
|
1683
|
+
if (underscoreCase !== method) {
|
1684
|
+
result.push (space.repeat (2) + '\'' + method + '\' => ' + '\'' + underscoreCase + '\',')
|
1685
|
+
}
|
1686
|
+
}
|
1687
|
+
result.push (space + ');')
|
1688
|
+
const string = result.join ('\n')
|
1689
|
+
|
1690
|
+
const phpBaseClass = './php/Exchange.php';
|
1691
|
+
const phpBody = fs.readFileSync (phpBaseClass, 'utf8')
|
1692
|
+
const regex = /public static \$camelcase_methods = array\([\s\S]+?\);/g
|
1693
|
+
const bodyArray = phpBody.split (regex)
|
1694
|
+
|
1695
|
+
const newBody = bodyArray[0] + string + bodyArray[1]
|
1696
|
+
|
1697
|
+
log.magenta ('Transpiling from ', phpBaseClass.yellow, '→', phpBaseClass.yellow)
|
1698
|
+
overwriteFile (phpBaseClass, newBody)
|
1699
|
+
}
|
1700
|
+
|
1701
|
+
// ============================================================================
|
1702
|
+
|
1703
|
+
transpileEverything (force = false) {
|
1704
|
+
|
1705
|
+
// default pattern is '.js'
|
1706
|
+
const [ /* node */, /* script */, pattern ] = process.argv.filter (x => !x.startsWith ('--'))
|
1707
|
+
, python2Folder = './python/ccxt/'
|
1708
|
+
, python3Folder = './python/ccxt/async_support/'
|
1709
|
+
, phpFolder = './php/'
|
1710
|
+
, phpAsyncFolder = './php/async/'
|
1711
|
+
, options = { python2Folder, python3Folder, phpFolder, phpAsyncFolder }
|
1712
|
+
|
1713
|
+
createFolderRecursively (python2Folder)
|
1714
|
+
createFolderRecursively (python3Folder)
|
1715
|
+
createFolderRecursively (phpFolder)
|
1716
|
+
createFolderRecursively (phpAsyncFolder)
|
1717
|
+
|
1718
|
+
//*
|
1719
|
+
|
1720
|
+
const classes = this.transpileDerivedExchangeFiles ('./js/', options, pattern, force)
|
1721
|
+
|
1722
|
+
if (classes === null) {
|
1723
|
+
log.bright.yellow ('0 files transpiled.')
|
1724
|
+
return;
|
1725
|
+
}
|
1726
|
+
|
1727
|
+
// HINT: if we're going to support specific class definitions
|
1728
|
+
// this process won't work anymore as it will override the definitions
|
1729
|
+
this.exportTypeScriptDeclarations (tsFilename, classes)
|
1730
|
+
|
1731
|
+
//*/
|
1732
|
+
|
1733
|
+
this.transpileErrorHierarchy ({ tsFilename })
|
1734
|
+
|
1735
|
+
this.transpileTests ()
|
1736
|
+
|
1737
|
+
this.transpilePythonAsyncToSync ()
|
1738
|
+
|
1739
|
+
this.transpilePhpAsyncToSync ()
|
1740
|
+
|
1741
|
+
this.transpilePhpBaseClassMethods ()
|
1742
|
+
|
1743
|
+
log.bright.green ('Transpiled successfully.')
|
1744
|
+
}
|
1745
|
+
}
|
1746
|
+
|
1747
|
+
// ============================================================================
|
1748
|
+
// main entry point
|
1749
|
+
|
1750
|
+
if (require.main === module) { // called directly like `node module`
|
1751
|
+
|
1752
|
+
const transpiler = new Transpiler ()
|
1753
|
+
const test = process.argv.includes ('--test') || process.argv.includes ('--tests')
|
1754
|
+
const errors = process.argv.includes ('--error') || process.argv.includes ('--errors')
|
1755
|
+
const force = process.argv.includes ('--force')
|
1756
|
+
log.bright.green ({ force })
|
1757
|
+
if (test) {
|
1758
|
+
transpiler.transpileTests ()
|
1759
|
+
} else if (errors) {
|
1760
|
+
transpiler.transpileErrorHierarchy ({ tsFilename })
|
1761
|
+
} else {
|
1762
|
+
transpiler.transpileEverything (force)
|
1763
|
+
}
|
1764
|
+
|
1765
|
+
} else { // if required as a module
|
1766
|
+
|
1767
|
+
// do nothing
|
1768
|
+
}
|
1769
|
+
|
1770
|
+
// ============================================================================
|
1771
|
+
|
1772
|
+
module.exports = Transpiler
|