This commit is contained in:
discountry
2025-10-03 15:28:16 +08:00
parent d5c4b04746
commit fcb83bd16b
925 changed files with 165721 additions and 4 deletions
+111
View File
@@ -0,0 +1,111 @@
{
"env": {
"es2021": true,
"node": true
},
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "airbnb-base", "plugin:import/typescript"],
"ignorePatterns": ["*/*", "cli.ts"],
"rules": {
"no-console": "off",
"import/no-unresolved":"off", // tmp until typescript decides to properly resolve names
"import/named": "off",
"strict": "off",
"semi": "error",
"indent": ["error", 4],
"init-declarations": "error",
"no-undef-init": "off",
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}],
"import/extensions": "off",
"brace-style": ["error", "1tbs"],
"multiline-comment-style": ["error", "separate-lines"],
"dot-notation": "off",
"quote-props": ["error", "always"],
"no-multi-spaces": ["error", { "ignoreEOLComments": true }],
"no-whitespace-before-property": "error",
"space-before-blocks": ["error", "always"],
"space-before-function-paren": ["error", "always"],
"no-spaced-func": "off",
"func-call-spacing": ["error", "always"],
"block-spacing": ["error", "always"],
"keyword-spacing": ["error", { "before": true, "after": true }],
"object-curly-spacing": ["error", "always", { "objectsInObjects": false }],
"object-curly-newline": ["error", { "consistent": true }],
"space-infix-ops": "error",
"space-unary-ops": "error",
"space-in-parens": "error",
"no-nested-ternary": "error",
"eqeqeq": "error",
"quotes": ["error", "single", { "avoidEscape": true }],
"no-unused-vars": ["warn", { "argsIgnorePattern": "^(headers|body|account|info|symbol|price|tag|side|since|limit|params|market|timeframe|api|path|code|currency|statusCode|statusText|url|method|response|requestHeaders|requestBody|bidsKey|asksKey|context|config|type|priceKey|amountKey|networkCode|marginMode|subscription|message|client)" }],
"new-parens": "error",
"new-cap": "off",
"no-var": "error",
"prefer-const": ["error", {
"destructuring": "any",
"ignoreReadBeforeAssign": false
}],
"no-warning-comments": ["warn", { "terms": ["fixme"] }],
"padded-blocks": ["error", "never"],
"lines-between-class-members": "error",
"no-multiple-empty-lines": ["error", { "max": 2 }],
"padding-line-between-statements": ["warn",
// { "blankLine": "never", "prev":"*", "next": "*" }, // comment this to allow blank-lines
{ "blankLine": "always", "prev":"directive", "next": "*" },
{ "blankLine": "always", "prev":"*", "next": "cjs-export" },
{ "blankLine": "always", "prev":"*", "next": "export" },
{ "blankLine": "always", "prev":"*", "next": "function" }
],
"prefer-template": "off",
"curly": "error",
"no-plusplus": "off",
"no-restricted-properties": "off",
"prefer-destructuring": "off",
"class-methods-use-this": "off",
"no-param-reassign": "off",
"max-len": "off",
"no-return-await": "off",
"array-bracket-spacing": ["error", "always"],
"radix": "off",
"camelcase": "off",
"no-lonely-if": "off",
"no-mixed-operators": "off",
"no-shadow": "off",
"no-useless-concat": "off",
"no-continue": "off",
"no-else-return": "off",
"no-unneeded-ternary": "off",
"operator-assignment": "off",
"no-underscore-dangle": "off",
"consistent-return": "off",
"no-await-in-loop": "off",
"prefer-exponentiation-operator": "off",
"no-use-before-define": ["error", {
"functions": false,
"classes": true,
"variables": true,
"allowNamedExports": false
}]
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".ts"]
}
},
"import/parsers": {
"@typescript-eslint/parser": [".ts", ".js"]
}
}
}
+134
View File
@@ -0,0 +1,134 @@
import { spawn } from 'child_process';
import asTable from 'as-table';
import ccxt, { version } from '../../js/ccxt.js';
interface Stats {
min: number;
average: number;
max: number;
median: number;
iterations: number;
}
interface Test {
language: string;
method: string;
command: string;
}
interface Benchmark extends Omit<Test, 'command'>, Stats { }
const stats = (times: number[]): Stats => {
// calculate statistics
const sum = times.reduce ((a, b) => a + b, 0);
const avg = Math.round (sum / times.length);
const min = Math.min (...times);
const max = Math.max (...times);
times.sort ((a, b) => a - b);
const median = times.length % 2 === 0 ? (times[times.length / 2 - 1] + times[times.length / 2]) / 2 : times[Math.floor (times.length / 2)];
return { min, 'average': avg, max, median, 'iterations': times.length };
};
async function benchmark (exchangeId, method, args, verbose = false, minIterations = 10, argsv = '') {
const exchange = new ccxt.pro[exchangeId] ({});
const languages = [ 'js', 'py', 'php', 'cs' ];
const commands: Test[] = languages.map ((language) => ({
'language': language,
'method': method,
'command': `npm run cli.${language} ${exchangeId} ${method} ${args.join (' ')} -- ${argsv} --poll`,
}));
const wsMethod = method + 'Ws';
if (exchange.has[wsMethod]) {
const wsCommands: Test[] = languages.map ((language) => ({
'language': language,
'method': wsMethod,
'command': `npm run cli.${language} ${exchangeId} ${wsMethod} ${args.join (' ')} -- ${argsv} --poll`,
}));
commands.push (...wsCommands);
}
const regex = /iteration (\d+) passed in (\d+) ms/g;
async function runCommand (command: string) {
if (verbose) {
console.log (exchange.iso8601 (new Date ().getTime ()), ' running command:', command);
}
return new Promise<{ times: number[] }> ((resolve, reject) => {
const [ cmd, ...args ] = command.split (' ');
const child = spawn (cmd, args);
const matches = [];
const language = command.slice (8, 15);
child.stdout.on ('data', (data: Buffer) => {
const message = data.toString ();
matches.push (...Array.from (message.matchAll (regex)));
const match = matches[matches.length - 1];
if (match && match[1] && match[2]) {
const iteration = parseInt (match[1]);
const time = parseInt (match[2]);
if (iteration <= minIterations) {
if (verbose) {
console.log (exchange.iso8601 (new Date ().getTime ()), `${language} iteration ${iteration} passed in ${time} ms`);
}
} else {
const times = matches.map ((m) => parseInt (m[2]));
child.kill ();
if (verbose) {
console.log (exchange.iso8601 (new Date ().getTime ()), `killed process - ${language} iteration ${iteration} passed in ${time} ms`);
}
resolve ({ times });
}
}
});
child.stderr.on ('data', (data: Buffer) => {
const message = data.toString ();
console.error (exchange.iso8601 (new Date ().getTime ()), `command ${command} failed. stderr: ${message}`);
const times = matches.map ((m) => parseInt (m[2]));
resolve ({ times });
});
child.on ('close', (code: number) => {
const times = matches.map ((m) => parseInt (m[2]));
resolve ({ times });
console.log (exchange.iso8601 (new Date ().getTime ()), `${language} child process exited with code ${code}`);
});
child.on ('error', (err) => {
console.error (exchange.iso8601 (new Date ().getTime ()), `command ${command} failed. error: ${err}`);
reject (err);
});
});
}
const benchmarks: Benchmark[] = [];
const results = await Promise.all (commands.map ((c) => runCommand (c.command)));
for (let i = 0; i < results.length; i++) {
const result = results[i];
benchmarks.push ({ 'language': commands[i].language, 'method': commands[i].method, ...stats (result.times) });
}
if (verbose) {
const rawResults = results.map ((r, i) => ({ 'language': commands[i].language, 'method': commands[i].method, ...stats (r.times), 'times': r.times }));
console.log (rawResults);
}
console.log (asTable (benchmarks));
}
const [ _, , exchangeId, methodName, ...params ] = process.argv.filter ((x) => !x.startsWith ('--'));
const verbose = process.argv.includes ('--verbose');
const minIterationsString = process.argv.find ((x) => x.startsWith ('--min-iterations='))?.slice (18);
const minIterations = minIterationsString ? parseInt (minIterationsString) : 10;
const argsv = process.argv.filter ((x) => x.startsWith ('--') && !x.startsWith ('--min-iterations')).join (' ');
console.log ((new Date ()).toISOString ());
console.log ('Node.js:', process.version);
console.log ('CCXT v' + version);
const start = new Date ().getTime ();
await benchmark (exchangeId, methodName, params, verbose, minIterations, argsv);
const end = new Date ().getTime ();
console.log ((new Date ().toISOString ()), 'Total time:', end - start, 'ms');
process.exit (0);
+53
View File
@@ -0,0 +1,53 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
// Bulding OHLCV array from trades (executions) data is a bit tricky. For example, if you want to build 100 ohlcv bars of 1-minute timeframe, then you have to fetch the 100 minutes of trading data. So, higher timeframe bars require more trading data (i.e. building 100 bars of 1-day timeframe OHLCV would require massive amount of trading data, which might not be desirable for user, because of data-usage rate limits)
async function example_with_fetch_trades () {
const exch = new ccxt.binance ({});
const timeframe = '1m';
const symbol = 'OGN/USDT';
const since = exch.milliseconds () - 1000 * 60 * 30; // last 30 mins
const limit = 1000;
const trades = await exch.fetchTrades (symbol, since, limit);
const generatedBars = exch.buildOHLCVC (trades, timeframe, since, limit);
// you can ignore 6th index ("count" field) from ohlcv entries, which is not part of OHLCV standard structure and is just added internally by `buildOHLCVC` method
console.log ('[REST] Constructed', generatedBars.length, 'bars from trades: ', generatedBars);
}
async function example_with_watch_trades () {
const exch = new ccxt.pro.binance ({});
const timeframe = '1m';
const symbol = 'DOGE/USDT';
const limit = 1000;
const since = exch.milliseconds () - 10 * 60 * 1000 * 1000; // last 10 hrs
let collectedTrades = [];
const collectedBars = [];
while (true) {
const wsTrades = await exch.watchTrades (symbol, since, limit, {});
collectedTrades = collectedTrades.concat (wsTrades);
const generatedBars = exch.buildOHLCVC (collectedTrades, timeframe, since, limit);
// Note: first bar would be partially constructed bar and its 'open' & 'high' & 'low' prices (except 'close' price) would probably have different values compared to real bar on chart, because the first obtained trade timestamp might be somewhere in the middle of timeframe period, so the pre-period would be missing because we would not have trades data. To fix that, you can get older data with `fetchTrades` to fill up bars till start bar.
for (let i = 0; i < generatedBars.length; i++) {
const bar = generatedBars[i];
const barTimestamp = bar[0];
const collectedBarsLength = collectedBars.length;
const lastCollectedBarTimestamp = collectedBarsLength > 0 ? collectedBars[collectedBarsLength - 1][0] : 0;
if (barTimestamp === lastCollectedBarTimestamp) {
// if timestamps are same, just updarte the last bar
collectedBars[collectedBarsLength - 1] = bar;
} else if (barTimestamp > lastCollectedBarTimestamp) {
collectedBars.push (bar);
// remove the trades from saved array, which were till last collected bar's open timestamp
collectedTrades = exch.filterBySinceLimit (collectedTrades, barTimestamp);
}
}
// Note: first bar would carry incomplete values, please read comment in "buildOHLCVCFromWatchTrades" method definition for further explanation
console.log ('[WS] Constructed', collectedBars.length, 'bars from', symbol, 'trades: ', collectedBars);
}
}
await example_with_fetch_trades ();
await example_with_watch_trades ();
@@ -0,0 +1,36 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const prefix = '-';
const exchange_1 = new ccxt.okx ();
const exchange_2 = new ccxt.htx ();
const keys_1 = Object.keys (exchange_1.has);
const keys_2 = Object.keys (exchange_2.has);
// check missing from exchange-1
console.log ('### checking missing functionalities from exchange-1:', exchange_1.id);
for (let i = 0; i < keys_2.length; i++) {
const key = keys_2[i];
if (exchange_2.has[key]) {
if (!keys_1.includes (key)) {
console.log (prefix, key, 'does not exist in', exchange_1.id, 'as opposed to', exchange_2.id);
} else if (exchange_2.has[key] !== exchange_1.has[key]) {
console.log (prefix, key, '> ', exchange_1.id, ':', exchange_1.has[key], ',', exchange_2.id, ':', exchange_2.has[key]);
}
}
}
// check missing from exchange-2
console.log ('### checking missing functionalities from exchange-2:', exchange_2.id);
for (let i = 0; i < keys_1.length; i++) {
const key = keys_1[i];
if (exchange_1.has[key]) {
if (!keys_2.includes (key)) {
console.log (prefix, key, 'does not exist in', exchange_2.id, 'as opposed to', exchange_1.id);
} else if (exchange_1.has[key] !== exchange_2.has[key]) {
console.log (prefix, key, '> ', exchange_2.id, ':', exchange_2.has[key], ',', exchange_1.id, ':', exchange_1.has[key]);
}
}
}
}
await example ();
@@ -0,0 +1,89 @@
// @ts-nocheck
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
console.log ('CCXT Version:', ccxt.version);
// ------------------------------------------------------------------------------
async function example () {
// at this moment, only OKX support embedded stop-loss & take-profit orders in unified manner. other exchanges are being added actively and will be available soon.
const exchange = new ccxt.okx ({
"apiKey": "YOUR_API_KEY",
"secret": "YOUR_API_SECRET",
"password": "YOUR_API_PASSWORD", // if exchange does not require password, comment out this line
});
const symbol = 'DOGE/USDT:USDT';
const side = 'buy'; // 'buy' | 'sell'
const order_type = 'limit'; // 'market' | 'limit'
const amount = 1; // how many contracts (see `market(symbol).contractSize` to find out coin portion per one contract)
await exchange.loadMarkets ();
const market = exchange.market (symbol);
const ticker = await exchange.fetchTicker (symbol);
const last_price = ticker['last'];
const ask_price = ticker['ask'];
const bid_price = ticker['bid'];
// if order_type is 'market', then price is not needed
let price = undefined;
// if order_type is 'limit', then set a price at your desired level
if (order_type === 'limit') {
price = (side === 'buy') ? bid_price * 0.95 : ask_price * 1.05; // i.e. 5% from current price
}
// set trigger price for stop-loss/take-profit to 2% from current price
// (note, across different exchanges "trigger" price can be also mentioned with different synonyms, like "activation price", "stop price", "conditional price", etc. )
const stop_loss_trigger_price = (order_type === 'market' ? last_price : price) * (side === 'buy' ? 0.98 : 1.02);
const take_profit_trigger_price = (order_type === 'market' ? last_price : price) * (side === 'buy' ? 1.02 : 0.98);
// when symbol's price reaches your predefined "trigger price", stop-loss order would be activated as a "market order". but if you want it to be activated as a "limit order", then set a 'price' parameter for it
const params = {
'stopLoss': {
'triggerPrice': stop_loss_trigger_price,
// set a 'price' to act as limit order, otherwise remove it for a market order
'price': stop_loss_trigger_price * 0.98,
},
'takeProfit': {
'triggerPrice': take_profit_trigger_price,
// set a 'price' to act as limit order, otherwise remove it for a market order
'price': take_profit_trigger_price * 0.98,
},
// note that some exchanges might require some exchange specific parameter when opening a position, i.e.:
// 'posSide': 'long', // for phemex hedge-mode api
};
const position_amount = market['contractSize'] * amount;
const position_value = position_amount * last_price;
// log
console.log ('Going to open a position', 'for', amount, 'contracts worth', position_amount, market['base'], '~', position_value, market['settle'], 'using', side, order_type, 'order (', (order_type === 'limit' ? exchange.priceToPrecision (symbol, price) : ''), '), using the following params:');
console.log (params);
console.log ('-----------------------------------------------------------------------');
// exchange.verbose = True // uncomment for debugging purposes if necessary
try {
const created_order = await exchange.createOrder (symbol, order_type, side, amount, price, params);
console.log ("Created an order", created_order);
// Fetch all your open orders for this symbol
// - use 'fetchOpenOrders' or 'fetchOrders' and filter with 'open' status
// - note, that some exchanges might return one order object with embedded stoploss/takeprofit fields, while other exchanges might have separate stoploss/takeprofit order objects
const all_open_orders = await exchange.fetchOpenOrders (symbol);
console.log ("Fetched all your orders for this symbol", all_open_orders);
// To cancel a limit order, use "exchange.cancel_order(created_order['id'], symbol)""
} catch (e) {
console.log (e.toString ());
}
}
await example ();
// NOTES:
// - Sometimes you might experience, when their stop-loss/take-profit order might not become activated, even though on chart the price had crossed that "trigger-price" order was not executed . That happens because some exchange might be using mark-price (instead of last-price) as a reference-price, so that mark-price might reach your trigger-price and it would activate your SL/TP order (even though on your symbol's chart you are viewing the "last-price" by default, which could have different movements than the mark-price).
@@ -0,0 +1,33 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const exchange = new ccxt.pro.binance ({
'apiKey': 'MY_API_KEY',
'secret': 'MY_SECRET',
});
exchange.setSandboxMode (true);
exchange.verbose = true; // uncomment for debugging purposes if necessary
// load markets
await exchange.loadMarkets ();
const symbol = 'ETH/USDT';
const type = 'limit';
const side = 'buy';
const amount = 0.01;
let price = 1000;
let orders = [];
for (let i=1; i<5; i++) {
const response = await exchange.createOrderWs (
symbol,
type,
side,
amount,
price
);
price += i;
orders.push (response);
}
console.log (orders);
}
await example ();
@@ -0,0 +1,21 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const exchange = new ccxt.binance ({
'apiKey': 'MY_API_KEY',
'secret': 'MY_SECRET',
});
exchange.setSandboxMode (true);
await exchange.loadMarkets ();
exchange.verbose = true; // uncomment for debugging purposes if necessary
const orders = await exchange.createOrders (
[
{ 'symbol': 'LTC/USDT:USDT', 'type': 'limit', 'side': 'buy', 'amount': 10, 'price': 55 },
{ 'symbol': 'ETH/USDT:USDT', 'type': 'market', 'side': 'buy', 'amount': 0.5 },
]
);
console.log (orders);
}
await example ();
@@ -0,0 +1,37 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const exchange = new ccxt.bingx ({
'apiKey': 'MY_API_KEY',
'secret': 'MY_SECRET',
});
// exchange.setSandboxMode (true);
// exchange.verbose = true; // uncomment for debugging purposes if necessary
await exchange.loadMarkets ();
const symbol = 'BTC/USDT:USDT';
const order_type = 'market';
const side = 'sell';
const amount = 0.0001;
const price = undefined;
const reduceOnly = true;
const trailingAmount = 100;
// const trailingTriggerPrice = undefined; // not supported on all exchanges
const params = {
'reduceOnly': reduceOnly,
'trailingAmount': trailingAmount,
// 'trailingTriggerPrice': trailingTriggerPrice,
};
try {
const create_order = await exchange.createOrder (symbol, order_type, side, amount, price, params);
// Alternatively use the createTrailingAmountOrder method:
// const create_order = await exchange.createTrailingAmountOrder (symbol, order_type, side, amount, price, trailingAmount, trailingTriggerPrice, {
// 'reduceOnly': reduceOnly,
// });
console.log (create_order);
} catch (e) {
console.log (e.toString ());
}
}
await example ();
@@ -0,0 +1,37 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const exchange = new ccxt.bingx ({
'apiKey': 'MY_API_KEY',
'secret': 'MY_SECRET',
});
// exchange.setSandboxMode (true);
// exchange.verbose = true; // uncomment for debugging purposes if necessary
await exchange.loadMarkets ();
const symbol = 'BTC/USDT:USDT';
const order_type = 'market';
const side = 'sell';
const amount = 0.0001;
const price = undefined;
const reduceOnly = true;
const trailingPercent = 10;
// const trailingTriggerPrice = undefined; // not supported on all exchanges
const params = {
'reduceOnly': reduceOnly,
'trailingPercent': trailingPercent,
// 'trailingTriggerPrice': trailingTriggerPrice,
};
try {
const create_order = await exchange.createOrder (symbol, order_type, side, amount, price, params);
// Alternatively use the createTrailingAmountOrder method:
// const create_order = await exchange.createTrailingPercentOrder (symbol, order_type, side, amount, price, trailingPercent, trailingTriggerPrice, {
// 'reduceOnly': reduceOnly,
// });
console.log (create_order);
} catch (e) {
console.log (e.toString ());
}
}
await example ();
@@ -0,0 +1,12 @@
// @ts-nocheck
// to set custom "proxy-agent" for ccxt
import ccxt from 'ccxt';
import HttpProxyAgent from 'http-proxy-agent';
import HttpsProxyAgent from 'https-proxy-agent';
const proxy = 'http://1.2.3.4:5678';
const httpAgent = new HttpProxyAgent (proxy);
const httpsAgent = new HttpsProxyAgent (proxy);
// then pass it through constructor
const kraken = new ccxt.kraken ({ agent: httpAgent /* or httpsAgent */ });
// or set it later
kraken.agent = agent;
@@ -0,0 +1,88 @@
// eslint-disable-next-line no-unused-vars
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
// ###### Description ######
//
// This function tries to fetch the "listing time" of a symbol by fetching the earliest available bar in daily resolution.
// Top-tier exchanges also support fetching smaller timeframes (eg. 1 minute) even several years back, so for those exchanges you can also use `useMinuteTimeframe = true` argument to get the timestamp rounded to the earliest minute bar (instead of daily bar timestamp).
// See usage in the end of this file
async function fetchFirstBarTimestamp (exchange:any, symbol: string, useMinuteTimeframe = false) {
// set some constants
const millisecondsPerDay = 86400000;
const minutesPerDay = 1440;
const minimumTimestamp = 1230768000000; // 2009-01-01 (bitcoin created year)
// get market features
const market = exchange.market (symbol);
const marketType = exchange.safeString (market, 'type');
let features = exchange.safeDict (exchange.features, marketType, {});
if (market['subType'] !== undefined) {
features = exchange.safeDict (features, market['subType'], {});
}
const ohlcv = exchange.safeDict (features, 'fetchOHLCV');
if (ohlcv === undefined) {
return undefined;
}
const limit = exchange.safeInteger (ohlcv, 'limit');
const fetchParams = { 'maxRetriesOnFailure': 3 };
// start loop
let currentSince = exchange.milliseconds () - millisecondsPerDay * (limit - 1);
let foundStartTime = 0;
// eslint-disable-next-line
while (true) {
currentSince = Math.max (currentSince, minimumTimestamp);
const dailyBars = await exchange.fetchOHLCV (symbol, '1d', currentSince, limit, fetchParams);
if (dailyBars.length <= 0) {
break; // if no days returned, then probably start date was passed
}
const firstTs = dailyBars[0][0];
if (firstTs === foundStartTime) {
// if the first timestamp is equal to the last-fetched timestamp, then break here, because some exchanges still return initial bar even if since is much ahead to listing time
break;
}
foundStartTime = firstTs;
currentSince = foundStartTime - millisecondsPerDay * (limit - 1); // shift 'since' one step back
if (dailyBars.length === 1) {
// in some cases, some exchanges might still return first bar of chart when endtime overlaps previous day
break;
}
}
// if minute resolution needed
if (useMinuteTimeframe) {
const maxIteration = Math.ceil (minutesPerDay / limit) * 2;
const allPromises: any[] = [];
for (let i = 0; i < maxIteration; i++) {
currentSince = foundStartTime - millisecondsPerDay + i * limit * 60 * 1000; // shift one-duration back for more accuracy for different kind of exchanges, like OKX, where first daily bar is offset by one day, but minute bars present
allPromises.push (exchange.fetchOHLCV (symbol, '1m', currentSince, limit, fetchParams));
}
const allResponses = await Promise.all (allPromises);
// find earliest bar
for (let i = 0; i < allResponses.length; i++) {
const response = allResponses[i];
if (response.length > 0) {
foundStartTime = response[0][0];
break;
}
}
}
return foundStartTime;
}
// ###### Usage ######
const runExample = false; // set to true to run example
if (runExample) {
const myEx = new ccxt.binance ();
await myEx.loadMarkets ();
const symbol = 'TRUMP/USDT';
const earliest_timestamp = await fetchFirstBarTimestamp (myEx, symbol, true);
console.log ('- Earliest bar timestamp:', earliest_timestamp, ', readable: ', myEx.iso8601 (earliest_timestamp));
console.log ('- market.created value:', myEx.market (symbol)['created']);
}
export default fetchFirstBarTimestamp;
@@ -0,0 +1 @@
build/
+116
View File
@@ -0,0 +1,116 @@
{
"name": "fetch-futures",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/node": {
"version": "10.14.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.18.tgz",
"integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ==",
"dev": true
},
"ansicolor": {
"version": "1.1.92",
"resolved": "https://registry.npmjs.org/ansicolor/-/ansicolor-1.1.92.tgz",
"integrity": "sha512-2jRpULfLpLvFeYYxIu1z6i0LQ4Khj/UxecsrdJ+837P0AVOW3smdKHpHmeKVYHZPG67o3jQFdMjNvPwktwPmZg=="
},
"as-table": {
"version": "1.0.55",
"resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz",
"integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==",
"requires": {
"printable-characters": "^1.0.42"
}
},
"ccxt": {
"version": "1.18.1145",
"resolved": "https://registry.npmjs.org/ccxt/-/ccxt-1.18.1145.tgz",
"integrity": "sha512-orU4kGB59RCMo3Ed04HBpRWqZdnpPcZ3VENPkF4ImacCm/ilte0boXAS+mYJVZSXLljyQzfm4D9fY7IukWsmIA=="
},
"data-uri-to-buffer": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.1.tgz",
"integrity": "sha512-OkVVLrerfAKZlW2ZZ3Ve2y65jgiWqBKsTfUIAFbn8nVbPcCZg6l6gikKlEYv0kXcmzqGm6mFq/Jf2vriuEkv8A==",
"requires": {
"@types/node": "^8.0.7"
},
"dependencies": {
"@types/node": {
"version": "8.10.54",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.54.tgz",
"integrity": "sha512-kaYyLYf6ICn6/isAyD4K1MyWWd5Q3JgH6bnMN089LUx88+s4W8GvK9Q6JMBVu5vsFFp7pMdSxdKmlBXwH/VFRg=="
}
}
},
"get-source": {
"version": "1.0.40",
"resolved": "https://registry.npmjs.org/get-source/-/get-source-1.0.40.tgz",
"integrity": "sha512-grbzSLO9gCVpb+ngrUf2ch4nzcfAs9pYqlnu0v2+Ho0rOpwnJqhZsHWLXaqdNHMtmnY+OwGgS4SPdzMRXEwVtQ==",
"requires": {
"data-uri-to-buffer": "^2.0.0",
"source-map": "^0.6.1"
}
},
"ololog": {
"version": "1.1.146",
"resolved": "https://registry.npmjs.org/ololog/-/ololog-1.1.146.tgz",
"integrity": "sha512-C68zLVMFgIZC1Rdz7LTmtIEuAsth/2yllA/kb3OUfdWI9+U5x4F7EXO3xgfp9Rhn/tiETffo6DnWGPQaVHjKiw==",
"requires": {
"ansicolor": "^1.1.84",
"pipez": "^1.1.12",
"printable-characters": "^1.0.42",
"stacktracey": "^1.2.117",
"string.bullet": "^1.0.12",
"string.ify": "^1.0.57"
}
},
"pipez": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/pipez/-/pipez-1.1.12.tgz",
"integrity": "sha1-s1nOVAHpNfEp+koJT83omvuENtg="
},
"printable-characters": {
"version": "1.0.42",
"resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz",
"integrity": "sha1-Pxjpd6m9jrN/zE/1ZZ176Qhos9g="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"stacktracey": {
"version": "1.2.117",
"resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-1.2.117.tgz",
"integrity": "sha512-sj9olGCco8jxMw3fcmhL5er1yuVL2qSMhNfycwx+iIWz6uVpG+bQ2YNpVSlWscGmSskt61gSmUBjZ0Mb5jYBLw==",
"requires": {
"as-table": "^1.0.36",
"get-source": "^1.0.34"
}
},
"string.bullet": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/string.bullet/-/string.bullet-1.0.12.tgz",
"integrity": "sha1-tB/EF4UKV+QEK/PPNHtu7iV+4lM=",
"requires": {
"printable-characters": "^1.0.26"
}
},
"string.ify": {
"version": "1.0.61",
"resolved": "https://registry.npmjs.org/string.ify/-/string.ify-1.0.61.tgz",
"integrity": "sha512-ROVOYWN8B62bP1LUJIDK6/NooBrN8xIvpV8Ugssbw7Oa4YKJmiOeoEomhDLLRVpYHo0vOBCpEM8RFpxUk9KDIQ==",
"requires": {
"printable-characters": "^1.0.42",
"string.bullet": "^1.0.12"
}
},
"typescript": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz",
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
"dev": true
}
}
}
@@ -0,0 +1,34 @@
{
"name": "fetch-futures",
"version": "0.1.0",
"description": "ccxt example code in typescript.",
"main": "build/src/index.js",
"types": "build/src/index.d.ts",
"files": [
"build/src"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/ccxt/ccxt.git"
},
"keywords": [],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc -p .",
"start": "node ./build/index"
},
"dependencies": {
"ansicolor": "^1.1.92",
"as-table": "^1.0.55",
"ccxt": "^2.0.0",
"ololog": "^1.1.146"
},
"devDependencies": {
"typescript": "~3.5.0",
"@types/node": "^10.0.3"
},
"engines": {
"node": ">=7.6.0"
}
}
@@ -0,0 +1,4 @@
module.exports = {
singleQuote: true,
trailingComma: 'es5',
};
@@ -0,0 +1,28 @@
// Example code in typescript
// Based on /examples/js/fetch-okex-futures.js
import * as ccxt from 'ccxt';
const log = require('ololog');
const fetchFutures = async () => {
const exchange = new ccxt.bitmex();
exchange.markets = await exchange.loadMarkets(true);
for (let symbol in exchange.markets) {
log('----------------------------------------------------');
log(`symbol = ${symbol}`);
try {
const market = exchange.markets[symbol];
if (market['future']) {
const ticker = await exchange.fetchTicker(symbol);
log('----------------------------------------------------');
log(symbol, ticker);
await (ccxt as any).sleep(exchange.rateLimit); // Missing type information.
}
} catch (error) {
log('error =', error);
}
}
};
fetchFutures();
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"rootDir": "src",
"outDir": "build",
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"declaration": true,
"forceConsistentCasingInFileNames": true,
"lib": [
"es2016"
],
"module": "commonjs",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"pretty": true,
"sourceMap": true,
"strict": true,
"target": "es2017"
},
"include": [
"src/**/*.ts",
"test/**/*.ts"
],
"exclude": [
"node_modules"
]
}
@@ -0,0 +1,44 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
// fetch and handle constinuosly
async function fetchOHLCVContinuously (exchange, symbol) {
while (true) {
try {
const ohlcv = await exchange.fetchOHLCV (symbol);
const ohlcvLength = ohlcv.length;
console.log ('Fetched ', exchange.id, ' - ', symbol, ' candles. last candle: ', ohlcv[ohlcvLength - 1]);
} catch (e) {
console.log (e);
break;
}
}
}
// start exchanges and fetch OHLCV loop
async function startExchange (exchangeName, symbols) {
const ex = new ccxt[exchangeName] ({});
const promises = [];
for (let i = 0; i < symbols.length; i++) {
const symbol = symbols[i];
promises.push (fetchOHLCVContinuously (ex, symbol));
}
await Promise.all (promises);
await ex.close ();
}
// main function
async function example () {
const exchanges = [ 'binance', 'okx', 'kraken' ];
const symbols = [ 'BTC/USDT', 'ETH/USDT' ];
const promises = [];
for (let i = 0; i < exchanges.length; i++) {
const exchangeName = exchanges[i];
promises.push (startExchange (exchangeName, symbols));
}
await Promise.all (promises);
}
await example ();
+17
View File
@@ -0,0 +1,17 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const myex = new ccxt.okx ({});
const fromTimestamp = myex.milliseconds () - 86400 * 1000;// last 24 hrs
const ohlcv = await myex.fetchOHLCV ('BTC/USDT', '1m', fromTimestamp, 3, { 'whatever': 123 });
const length = ohlcv.length;
if (length > 0) {
const lastPrice = ohlcv[length - 1][4];
console.log ('Fetched ', length, ' candles for ', myex.id, ': last close ', lastPrice);
} else {
console.log ('No candles have been fetched');
}
}
await example ();
@@ -0,0 +1 @@
build/
+116
View File
@@ -0,0 +1,116 @@
{
"name": "fetch-tickers",
"version": "0.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/node": {
"version": "10.14.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.18.tgz",
"integrity": "sha512-ryO3Q3++yZC/+b8j8BdKd/dn9JlzlHBPdm80656xwYUdmPkpTGTjkAdt6BByiNupGPE8w0FhBgvYy/fX9hRNGQ==",
"dev": true
},
"ansicolor": {
"version": "1.1.92",
"resolved": "https://registry.npmjs.org/ansicolor/-/ansicolor-1.1.92.tgz",
"integrity": "sha512-2jRpULfLpLvFeYYxIu1z6i0LQ4Khj/UxecsrdJ+837P0AVOW3smdKHpHmeKVYHZPG67o3jQFdMjNvPwktwPmZg=="
},
"as-table": {
"version": "1.0.55",
"resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz",
"integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==",
"requires": {
"printable-characters": "^1.0.42"
}
},
"ccxt": {
"version": "1.18.1147",
"resolved": "https://registry.npmjs.org/ccxt/-/ccxt-1.18.1147.tgz",
"integrity": "sha512-3iflgpmBKgbskca3hwUDW/ewepKQ9rFEvxMV9s8NkWJFloO0ZAQzf3SfyxdRDGcJHcNNxSmbRFX/zRp3ucH0jw=="
},
"data-uri-to-buffer": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.1.tgz",
"integrity": "sha512-OkVVLrerfAKZlW2ZZ3Ve2y65jgiWqBKsTfUIAFbn8nVbPcCZg6l6gikKlEYv0kXcmzqGm6mFq/Jf2vriuEkv8A==",
"requires": {
"@types/node": "^8.0.7"
},
"dependencies": {
"@types/node": {
"version": "8.10.54",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.54.tgz",
"integrity": "sha512-kaYyLYf6ICn6/isAyD4K1MyWWd5Q3JgH6bnMN089LUx88+s4W8GvK9Q6JMBVu5vsFFp7pMdSxdKmlBXwH/VFRg=="
}
}
},
"get-source": {
"version": "1.0.40",
"resolved": "https://registry.npmjs.org/get-source/-/get-source-1.0.40.tgz",
"integrity": "sha512-grbzSLO9gCVpb+ngrUf2ch4nzcfAs9pYqlnu0v2+Ho0rOpwnJqhZsHWLXaqdNHMtmnY+OwGgS4SPdzMRXEwVtQ==",
"requires": {
"data-uri-to-buffer": "^2.0.0",
"source-map": "^0.6.1"
}
},
"ololog": {
"version": "1.1.146",
"resolved": "https://registry.npmjs.org/ololog/-/ololog-1.1.146.tgz",
"integrity": "sha512-C68zLVMFgIZC1Rdz7LTmtIEuAsth/2yllA/kb3OUfdWI9+U5x4F7EXO3xgfp9Rhn/tiETffo6DnWGPQaVHjKiw==",
"requires": {
"ansicolor": "^1.1.84",
"pipez": "^1.1.12",
"printable-characters": "^1.0.42",
"stacktracey": "^1.2.117",
"string.bullet": "^1.0.12",
"string.ify": "^1.0.57"
}
},
"pipez": {
"version": "1.1.12",
"resolved": "https://registry.npmjs.org/pipez/-/pipez-1.1.12.tgz",
"integrity": "sha1-s1nOVAHpNfEp+koJT83omvuENtg="
},
"printable-characters": {
"version": "1.0.42",
"resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz",
"integrity": "sha1-Pxjpd6m9jrN/zE/1ZZ176Qhos9g="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
},
"stacktracey": {
"version": "1.2.117",
"resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-1.2.117.tgz",
"integrity": "sha512-sj9olGCco8jxMw3fcmhL5er1yuVL2qSMhNfycwx+iIWz6uVpG+bQ2YNpVSlWscGmSskt61gSmUBjZ0Mb5jYBLw==",
"requires": {
"as-table": "^1.0.36",
"get-source": "^1.0.34"
}
},
"string.bullet": {
"version": "1.0.12",
"resolved": "https://registry.npmjs.org/string.bullet/-/string.bullet-1.0.12.tgz",
"integrity": "sha1-tB/EF4UKV+QEK/PPNHtu7iV+4lM=",
"requires": {
"printable-characters": "^1.0.26"
}
},
"string.ify": {
"version": "1.0.61",
"resolved": "https://registry.npmjs.org/string.ify/-/string.ify-1.0.61.tgz",
"integrity": "sha512-ROVOYWN8B62bP1LUJIDK6/NooBrN8xIvpV8Ugssbw7Oa4YKJmiOeoEomhDLLRVpYHo0vOBCpEM8RFpxUk9KDIQ==",
"requires": {
"printable-characters": "^1.0.42",
"string.bullet": "^1.0.12"
}
},
"typescript": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz",
"integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==",
"dev": true
}
}
}
@@ -0,0 +1,34 @@
{
"name": "fetch-tickers",
"version": "0.1.0",
"description": "ccxt example code in typescript.",
"main": "build/src/index.js",
"types": "build/src/index.d.ts",
"files": [
"build/src"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/ccxt/ccxt.git"
},
"keywords": [],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc -p .",
"start": "node ./build/index"
},
"dependencies": {
"ansicolor": "^1.1.92",
"as-table": "^1.0.55",
"ccxt": "^2.0.0",
"ololog": "^1.1.146"
},
"devDependencies": {
"typescript": "~3.5.0",
"@types/node": "^10.0.3"
},
"engines": {
"node": ">=7.6.0"
}
}
@@ -0,0 +1,4 @@
module.exports = {
singleQuote: true,
trailingComma: 'es5',
};
@@ -0,0 +1,21 @@
// Example code in typescript
// Based on /examples/js/fetch-from-many-exchanges-simultaneously.js
import * as ccxt from 'ccxt';
const log = require('ololog');
const symbol = 'BTC/USD';
const exchanges = ['coinbasepro', 'gemini', 'kraken'];
const fetchTickers = async (symbol: string) => {
const result = await Promise.all(exchanges.map(async (id: string): Promise<ccxt.Exchange> => {
const CCXT = ccxt as any; // Hack!
const exchange = new CCXT[id]({ 'enableRateLimit': true }) as ccxt.Exchange;
const ticker = await exchange.fetchTicker(symbol);
const exchangeExtended = exchange.extend({ 'exchange': id }, ticker) as ccxt.Exchange;
return exchangeExtended;
}));
log(result);
};
fetchTickers(symbol);
@@ -0,0 +1,28 @@
{
"compilerOptions": {
"rootDir": "src",
"outDir": "build",
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"declaration": true,
"forceConsistentCasingInFileNames": true,
"lib": [
"es2016"
],
"module": "commonjs",
"noEmitOnError": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"pretty": true,
"sourceMap": true,
"strict": true,
"target": "es2017"
},
"include": [
"src/**/*.ts",
"test/**/*.ts"
],
"exclude": [
"node_modules"
]
}
+137
View File
@@ -0,0 +1,137 @@
import { hibachi } from '../../js/ccxt.js';
import fs from 'fs';
/*
In order to run the examples, you need to setup keys.local.json file like this:
```
{
"hibachi": {
"accountId": 111,
"apiKey": "1111111111111111111111111111111111111111111=",
"privateKey": "0x1111111111111111111111111111111111111111111111111111111111111111",
"publicKey": "0x11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
"withdrawAddress": "0x1111111111111111111111111111111111111111"
}
}
```
You can get the accountId, apiKey and privateKey from Hibachi App by creating an API key
After that you can view the API key, it will show the publicKey (only for trustless account, you can ignore it for exchange managed account)
Note: if you are using exchange managed account, the privateKey's length will be 44 instead
The withdrawAddress can be any ethereum wallet address, that is used to receive funds for withdraw tests
*/
async function example () {
const keys = JSON.parse(fs.readFileSync('keys.local.json', 'utf-8'));
const exchange = new hibachi (keys.hibachi);
exchange.verbose = true;
const markets = await exchange.fetchMarkets();
console.log ('fetchMarkets', markets.length, markets[0]);
const currencies = await exchange.fetchCurrencies();
console.dir (currencies, { depth: null, colors: true });
const trades = await exchange.fetchTrades("BTC/USDT:USDT");
console.log('fetchTrades', trades.length, trades[0]);
const tenMinutes = 10 * 60 * 1000;
const until = Date.now();
const since = until - tenMinutes;
const ohlcv = await exchange.fetchOHLCV('BTC/USDT:USDT', '5min', since, 100, {until});
console.log ('fetchOHLCV', ohlcv.length, ohlcv[0]);
const balance = await exchange.fetchBalance();
console.dir (balance, { depth: null, colors: true });
const ticker = await exchange.fetchTicker('BTC/USDT:USDT');
console.log ('fetchTicker', ticker);
// createOrder, editOrder and cancelOrder
const order1 = await exchange.createOrder('BTC/USDT:USDT', 'market', 'buy', 0.00002);
const order2 = await exchange.createOrder('BTC/USDT:USDT', 'market', 'sell', 0.00002);
console.log('create market order', order1.id, order2.id);
const order3 = await exchange.createOrder('ETH/USDT:USDT', 'limit', 'buy', 1.234, 1.234);
const order4 = await exchange.editOrder(order3.id, 'ETH/USDT:USDT', 'limit', 'buy', 0.987, 1.123);
const order5 = await exchange.cancelOrder(order3.id);
console.log('create, edit and cancel limit order', order3.id, order4.id, order5.id);
// advanced order parameters
const postOnlyOrder = await exchange.createOrder('BTC/USDT:USDT', 'limit', 'buy', 2.0, 2.0, {'timeInForce': 'PO'});
await exchange.cancelOrder(postOnlyOrder.id);
const iocOrder = await exchange.createOrder('BTC/USDT:USDT', 'limit', 'buy', 2.0, 2.0, {'timeInForce': 'IOC'});
await exchange.createOrder('BTC/USDT:USDT', 'market', 'buy', 0.00002);
const reduceOnlyOrder = await exchange.createOrder('BTC/USDT:USDT', 'market', 'sell', 0.00002, undefined, {'reduceOnly': true});
const triggerOrder = await exchange.createOrder('BTC/USDT:USDT', 'limit', 'sell', 2.0, 2.0, {'triggerPrice': '2.0'});
await exchange.cancelOrder(triggerOrder.id);
console.log('postOnly, IOC, reduceOnly, trigger order', postOnlyOrder.id, iocOrder.id, reduceOnlyOrder.id, triggerOrder.id);
const order1_info = await exchange.fetchOrder (order1.id, 'BTC/USDT:USDT');
console.log ('fetchOrder', order1_info);
const orderbook = await exchange.fetchOrderBook ('BTC/USDT:USDT');
console.log ('fetchOrderBook', orderbook);
const withdrawResponse = await exchange.withdraw('USDT', 0.02, keys.hibachi.withdrawAddress);
console.log(withdrawResponse);
const myTrades = await exchange.fetchMyTrades('BTC/USDT:USDT', undefined, 1);
console.log('fetchMyTrades', myTrades);
const tradingFees = await exchange.fetchTradingFees ();
console.log ('fetchTradingFees', tradingFees);
const openOrders = await exchange.fetchOpenOrders ();
console.log ('fetchOpenOrders', openOrders);
const openOrdersWithLimit = await exchange.fetchOpenOrders (undefined, undefined, 1);
console.log ('fetchOpenOrdersWithLimit', openOrdersWithLimit);
const openOrdersBTC = await exchange.fetchOpenOrders ('BTC/USDT:USDT');
console.log ('fetchOpenOrdersBTC', openOrdersBTC);
const openOrdersSince = await exchange.fetchOpenOrders (undefined, 1752552000000); // 7/15/2025 00:00 UTC
console.log ('fetchOpenOrdersSince', openOrdersSince);
if (keys.hibachi.publicKey !== undefined) {
const depositAddress = await exchange.fetchDepositAddress ('USDT', {'publicKey': keys.hibachi.publicKey});
console.log ('fetchDepositAddress', depositAddress);
}
const ledger = await exchange.fetchLedger('USDT', undefined, 2);
console.log('fetchLedger', ledger);
const deposits = await exchange.fetchDeposits ();
console.log ('fetchDeposits', deposits);
const withdrawals = await exchange.fetchWithdrawals ();
console.log ('fetchWithdrawals', withdrawals);
const timestamp = await exchange.fetchTime();
console.log('fetchTime', timestamp)
const openInterest = await exchange.fetchOpenInterest('BTC/USDT:USDT');
console.log('fetchOpenInterest', openInterest);
const fundingRate = await exchange.fetchFundingRate('BTC/USDT:USDT');
console.log('fetchFundingRate', fundingRate);
const fundingRateHistory = await exchange.fetchFundingRateHistory('BTC/USDT:USDT', undefined, 2);
console.log('fetchFundingRateHistory', fundingRateHistory);
// Batch orders
const createOrders = await exchange.createOrders([
{'symbol': 'ETH/USDT:USDT', 'type': 'limit', 'side': 'buy', 'amount': 1.234, 'price': 1.234},
{'symbol': 'BTC/USDT:USDT', 'type': 'limit', 'side': 'buy', 'amount': 1.001, 'price': 1.001},
{'symbol': 'ETH/USDT:USDT', 'type': 'limit', 'side': 'buy', 'amount': 1.002, 'price': 1.002},
{'symbol': 'ETH/USDT:USDT', 'type': 'limit', 'side': 'buy', 'amount': 1.003, 'price': 1.003},
]);
console.log('createOrders', createOrders);
const editOrders = await exchange.editOrders([
// @ts-expect-error OrderRequest lacks `id` but we expects it for editing
{'id': createOrders[0].id, 'symbol': 'ETH/USDT:USDT', 'type': 'limit', 'side': 'buy', 'amount': 1.111, 'price': 0.999},
// @ts-expect-error OrderRequest lacks `id` but we expects it for editing
{'id': createOrders[1].id, 'symbol': 'BTC/USDT:USDT', 'type': 'limit', 'side': 'buy', 'amount': 1.112, 'price': 0.998},
]);
console.log('editOrders', editOrders);
const cancelOrders = await exchange.cancelOrders([createOrders[0].id, createOrders[1].id]);
console.log('cancelOrders', cancelOrders);
const cancelAll = await exchange.cancelAllOrders('ETH/USDT:USDT');
console.log(cancelAll);
}
example ();
@@ -0,0 +1,11 @@
import { binance } from '../../js/ccxt.js';
async function example () {
const exchange = new binance ({});
const ob = await exchange.fetchOrderBook ('BTC/USDT', 3);
const asks = ob['asks'];
const bids = ob['bids'];
console.log (asks);
console.log (bids);
}
example ();
@@ -0,0 +1,69 @@
// @ts-nocheck
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
console.log ('CCXT Version:', ccxt.version);
// ------------------------------------------------------------------------------
async function example () {
const exchange = new ccxt.kraken ({
"apiKey": "YOUR_API_KEY",
"secret": "YOUR_API_SECRET",
});
const symbol = 'UNI/USD';
let side = 'buy'; // set it to 'buy' for a long position, 'sell' for a short position
const order_type = 'market'; // set it to 'market' or 'limit'
const amount = 1;
const leverage = 2;
await exchange.loadMarkets ();
const market = exchange.market (symbol);
// if order_type is 'market', then price is not needed
let price = undefined;
// if order_type is 'limit', then set a price at your desired level
// you can fetch the ticker and update price
// const ticker = await exchange.fetchTicker (symbol);
// const last_price = ticker['last'];
// const ask_price = ticker['ask'];
// const bid_price = ticker['bid'];
// if (order_type === 'limit') {
// price = (side === 'buy') ? bid_price * 0.95 : ask_price * 1.05; // i.e. 5% from current price
// }
const params = {
'leverage': leverage,
};
// log
console.log ('Going to open a position', 'for', amount, 'worth', amount, market['base'], '~', market['settle'], 'using', side, order_type, 'order (', (order_type === 'limit' ? exchange.priceToPrecision (symbol, price) : ''), '), using the following params:');
console.log (params);
console.log ('-----------------------------------------------------------------------');
// exchange.verbose = True // uncomment for debugging purposes if necessary
try {
const created_order = await exchange.createOrder (symbol, order_type, side, amount, price, params);
console.log ("Created an order", created_order);
// Fetch all your closed orders for this symbol (because we used market order)
// - use 'fetchClosedOrders' or 'fetchOrders' and filter with 'closed' status
const all_closed_orders = await exchange.fetchClosedOrders (symbol);
console.log ("Fetched all your closed orders for this symbol", all_closed_orders);
const all_open_positions = await exchange.fetchPositions (symbol);
console.log ("Fetched all your positions for this symbol", all_open_positions);
// To close a position:
// - long position (buy), you can create a sell order: exchange.createOrder (symbol, order_type, 'sell', amount, price, params);
// - short position (sell), you can create a buy order: exchange.createOrder (symbol, order_type, 'buy', amount, price, params);
} catch (e) {
console.log (e.toString ());
}
}
await example ();
@@ -0,0 +1,72 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
// Note, this is just an example and might not yet work on other exchanges, which are being still unified.
async function example () {
// ########## user inputs ##########
const exchange = new ccxt['binance'] ({ 'apiKey': 'xxx', 'secret': 'xxx' });
const symbol = 'BUSD/USDT'; // set target symbol
const marginMode = 'isolated'; // margin mode (cross or isolated)
const collateral_coin = 'USDT'; // which asset you want to use for margin-borrow collateral
const borrow_coin = 'BUSD'; // which coin to borrow
const order_side: any = 'sell'; // which side to trade
const amount_to_trade = 14; // how many coins to sell
const order_type = 'limit'; // order type (can be market, limit or etc)
const limit_price: any = 0.99; // price to sell at (set undefined/null/None if market-order)
const margin_magnitude = 5; // target margin (aka 'leverage'). This might also be obtainable using other unified methods, but for example purposes, we set here manually
// ########## end of user-inputs ##########
//
// for example purposes, let's also check available balance at first
const balance_margin = await exchange.fetchBalance ({ 'defaultType': 'margin', 'marginMode': marginMode }); // use `defaultType` because of temporary bug, otherwise, after several days, you can use `type` too.
// if we don't have enought coins, then we have to borrow at first
let needed_amount_to_borrow: any = undefined; // will be auto-set below
if (amount_to_trade > balance_margin[symbol][borrow_coin]['free']) {
needed_amount_to_borrow = amount_to_trade - balance_margin[symbol][borrow_coin]['free'];
console.log ('hmm, I have only ', balance_margin[symbol][borrow_coin]['free'], ' ', borrow_coin, ' in margin balance, and still need additional ', needed_amount_to_borrow, ' to make an order. Lets borrow it.');
// To initate a borrow, at first, check if we have enough collateral (for this example, as we make a sell-short, we need '-1' to keep for collateral currency)
const needed_collateral_amount = needed_amount_to_borrow / (margin_magnitude - 1);
// Check if we have any collateral to get permission for borrow
if (balance_margin[symbol][collateral_coin]['free'] < needed_collateral_amount) {
// If we don't have enough collateral, then let's try to transfer collateral-asset from spot-balance to margin-balance
console.log ('hmm, I have only ', balance_margin[symbol][collateral_coin]['free'], ' in balance, but ', needed_collateral_amount, ' collateral is needed. I should transfer ', needed_collateral_amount, ' from spot');
// let's check if we have spot balance at all
const balance_spot = await exchange.fetchBalance ({ 'type': 'spot' });
if (exchange.parseNumber (balance_spot[collateral_coin]['free']) < needed_collateral_amount) {
console.log ('hmm, I neither do have enough balance on spot - only ', balance_spot[collateral_coin]['free'], '. Script can not continue...');
return;
} else {
console.log ('Transferring ', needed_collateral_amount, ' to margin account');
await exchange.transfer (collateral_coin, needed_collateral_amount, 'spot', marginMode, { 'symbol': symbol });
}
}
// now, as we have enough margin collateral, initiate borrow
console.log ('Initiating margin borrow of ', needed_amount_to_borrow, ' ', borrow_coin);
const borrowResult = await exchange.borrowMargin (borrow_coin, needed_amount_to_borrow, symbol, { 'marginMode': marginMode });
}
console.log ('Submitting order.');
const order = await exchange.createOrder (symbol, order_type, order_side, amount_to_trade, limit_price, { 'marginMode': marginMode });
console.log ('Order was submitted !', order['id']);
//
//
// ...
// ...
// some time later, if you want to repay the loan back (like 'close the position')...
// ...
// ...
//
//
// set the "repay-back" amount (for this example snippet, this will be same amount that we borrowed above)
if (needed_amount_to_borrow !== undefined) {
const amount_to_repay_back = needed_amount_to_borrow;
// At first, you need to get back the borrowed coin, by making an opposide trade
console.log ('Making purchase back of ', amount_to_repay_back, ' ', borrow_coin, ' to repay it back.');
const purchase_back_price: any = 1.01;
const order_back = await exchange.createOrder (symbol, order_type, (order_side === 'buy' ? 'sell' : 'buy'), amount_to_repay_back, purchase_back_price, { 'marginMode': marginMode });
console.log ('Now, repaying the loan.');
const repayResult = await exchange.repayMargin (borrow_coin, amount_to_repay_back, symbol, { 'marginMode': marginMode });
console.log ('finished.');
}
}
await example ();
@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}
@@ -0,0 +1,36 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env*.local
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
@@ -0,0 +1,43 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
## Description
This app demonstrates the use of ccxt with a nextjs project. It showcases the use of both ccxt as a server side package and also to be running in the client:
- **/tickers**: uses the **clients Window websockets** to connect to the exchange and stream ticker values
- **/balance**: uses a **server side call** to protect exposing any api keys and fetches and shows the user balance.
## Getting Started
1. install the dependencies:
```bash
npm run install
```
2. Set the keys in pages/balance.tsx
3. Run the app:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## Screenshots
##### Index
![menu](https://github.com/ccxt/ccxt/assets/12142844/645b5fe8-fd13-44f8-bded-20733843ccea)
##### Client side websocket example
![websocket](https://github.com/ccxt/ccxt/assets/12142844/7c28f8c9-aefd-4db3-8aff-6ff06b93a8bc)
##### Server side private endpoint fetch balance
![balance](https://github.com/ccxt/ccxt/assets/12142844/bd253903-43ea-4225-a7ca-193aaa29f42a)
@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
}
module.exports = nextConfig
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,28 @@
{
"name": "nextjs-page-router",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"ccxt": "^4.1.56",
"next": "15.4.7",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.0.3",
"postcss": "^8",
"tailwindcss": "^3.3.0",
"typescript": "^5"
}
}
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,6 @@
import '@/styles/globals.css'
import type { AppProps } from 'next/app'
export default function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
@@ -0,0 +1,13 @@
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
@@ -0,0 +1,46 @@
import ccxt, { Balances } from 'ccxt'
// This is for showcase purposes in prod move this to environment variable like process.env.BINANCEUSDM_API_KEY
const apiKey = ""
const secret = ""
export async function getServerSideProps () {
const exchange = new ccxt.kraken({ apiKey, secret })
const balances = await exchange.fetchBalance()
// remove undefined values to prevent serializing error
Object.keys(balances).forEach(key => balances[key] === undefined && delete balances[key])
return {
props: {
balances,
},
}
}
export default function Balance({balances}: {balances: Balances}) {
return (
<main className={`flex min-h-screen flex-col items-center justify-between p-24`}>
<table>
<thead>
<tr>
<th>Currency</th>
<th>Free</th>
<th>Used</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{Object.keys(balances).map((currency: string) => (
balances[currency].free !== undefined && (
<tr key={currency}>
<td>{currency}</td>
<td>{balances[currency].free}</td>
<td>{balances[currency].used}</td>
<td>{balances[currency].total}</td>
</tr>
)
))}
</tbody>
</table>
</main>
)
}
@@ -0,0 +1,8 @@
export default function Home() {
return (
<main className={`flex min-h-screen flex-col items-center space-y-10`}>
<a className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-700 w-120 m-4" href="/balance">Balance - Using Server Side calls</a>
<a className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-700 w-120 m-4" href="/tickers">Tickers - Using Client Side websockets</a>
</main>
)
}
@@ -0,0 +1,61 @@
import ccxt, { Exchange, Ticker } from 'ccxt'
import { useEffect, useState } from 'react';
const exchangeIds = ['binance', 'bitget', 'bybit', 'cryptocom']
export default function Home() {
const [exchanges, setExchanges] = useState<Record<string, Exchange>>({});
const [tickers, setTickers] = useState<Record<string, Ticker>>({});
const [error, setError] = useState<string>();
useEffect(() => {
console.log('starting exchanges...');
const newExchanges: Record<string, Exchange> = exchangeIds.reduce((acc: any, exchangeId) => {
acc[exchangeId] = new (ccxt.pro as any)[exchangeId];
return acc;
}, {});
setExchanges(newExchanges);
}, []);
useEffect(() => {
const fetchTickers = async () => {
for (const exchangeId in exchanges) {
try {
const newTicker = await exchanges[exchangeId].watchTicker('BTC/USDT');
setTickers(tickers => ({ ...tickers, [exchangeId]: newTicker }));
} catch (e) {
setError(exchangeId + ': ' + JSON.stringify(e) + '\n')
}
};
};
fetchTickers();
}, [tickers, exchanges, error]);
return (
<main
className={`flex min-h-screen flex-col items-center justify-between p-24`}
>
<div className="z-10 max-w-5xl w-full font-mono text-sm lg:flex justify-between">
{exchangeIds.map((exchangeId) => (
<div key={exchangeId} className="flex-1">
<h3>{exchangeId}</h3>
<ul>
<li>{`last: ${tickers[exchangeId]?.last}`}</li>
<li>{`high: ${tickers[exchangeId]?.high}`}</li>
<li>{`low: ${tickers[exchangeId]?.low}`}</li>
<li>{`bid: ${tickers[exchangeId]?.bid}`}</li>
<li>{`ask: ${tickers[exchangeId]?.ask}`}</li>
<li>{`ask volume: ${tickers[exchangeId]?.askVolume}`}</li>
<li>{`bid volume: ${tickers[exchangeId]?.bidVolume}`}</li>
<li>{`close: ${tickers[exchangeId]?.close}`}</li>
</ul>
</div>
))}
</div>
<div>
<h3>Last error:</h3>
<p>{error ? error : "None"}</p>
</div>
</main>
)
}
@@ -0,0 +1,27 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
--background-end-rgb: 255, 255, 255;
}
@media (prefers-color-scheme: dark) {
:root {
--foreground-rgb: 255, 255, 255;
--background-start-rgb: 0, 0, 0;
--background-end-rgb: 0, 0, 0;
}
}
body {
color: rgb(var(--foreground-rgb));
background: linear-gradient(
to bottom,
transparent,
rgb(var(--background-end-rgb))
)
rgb(var(--background-start-rgb));
}
@@ -0,0 +1,20 @@
import type { Config } from 'tailwindcss'
const config: Config = {
content: [
'./src/pages/**/*.{js,ts,jsx,tsx,mdx}',
'./src/components/**/*.{js,ts,jsx,tsx,mdx}',
'./src/app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {
backgroundImage: {
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
'gradient-conic':
'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
},
},
},
plugins: [],
}
export default config
@@ -0,0 +1,22 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}
@@ -0,0 +1,62 @@
// @ts-nocheck
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
// ------------------------------------------------------------------------------
async function example () {
const exchange = new ccxt.phemex ({
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_API_SECRET',
});
const symbol = 'XRP/USDT:USDT';
const side = 'buy'; // set it to 'buy' for a long position, 'sell' for a short position
const order_type = 'limit'; // set it to 'market' or 'limit'
const amount = 1; // how many contracts
const price = 0.5; // set a price at your desired level
// take profit and stop loss prices and types
const take_profit_trigger_price = 0.6;
const stop_loss_trigger_price = 0.4;
const take_profit_limit_price = 0.7;
const stop_loss_limit_price = 0.3;
await exchange.loadMarkets ();
// when symbol's price reaches your predefined "trigger price", stop-loss order would be activated as a "market order". but if you want it to be activated as a "limit order", then set a 'price' parameter for it
const params = {
'posSide': 'Long', // "Long" / "Short" for hedge mode
'stopLoss': {
'triggerPrice': stop_loss_trigger_price,
'type': 'limit',
'price': stop_loss_limit_price,
},
'takeProfit': {
'triggerPrice': take_profit_trigger_price,
'type': 'limit',
'price': take_profit_limit_price,
},
};
console.log ('-----------------------------------------------------------------------');
// exchange.verbose = True // uncomment for debugging purposes if necessary
try {
const created_order = await exchange.createOrder (symbol, order_type, side, amount, price, params);
console.log ('Created an order', created_order);
// Fetch all your open orders for this symbol
const all_open_orders = await exchange.fetchOpenOrders (symbol);
console.log ('Fetched all your orders for this symbol', all_open_orders);
// To cancel a limit order, use "exchange.cancel_order(created_order['id'], symbol)""
} catch (e) {
console.log (e.toString ());
}
}
await example ();
+49
View File
@@ -0,0 +1,49 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
// 1) ABOUT CCXT PROXIES, READ MORE AT: https://docs.ccxt.com/#/README?id=proxy
// 2) in python, uncomment the below:
// if sys.platform == 'win32':
// asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
async function example_proxyUrl () {
const myEx = new ccxt.kucoin ();
myEx.proxyUrl = 'http://188.245.226.105:8090/proxy_url.php?caller=https://ccxt.com&url=';
console.log (await myEx.fetch ('https://api.ipify.org/'));
}
async function example_httpProxy () {
const myEx = new ccxt.kucoin ();
myEx.httpProxy = 'http://188.245.226.105:8911'; // "httpProxy" or "httpsProxy" (depending on your proxy protocol)
console.log (await myEx.fetch ('https://api.ipify.org/'));
}
async function example_socksProxy () {
const myEx = new ccxt.kucoin ();
myEx.socksProxy = 'socks5://127.0.0.1:1080'; // from protocols: socks, socks5, socks5h
console.log (await myEx.fetch ('https://api.ipify.org/'));
}
async function example_webSockets () {
const myEx = new ccxt.pro.kucoin ();
myEx.httpProxy = 'http://188.245.226.105:8911'; // even though you are using WebSockets, you might also need to set up proxy for the exchange's REST requests
myEx.wsProxy = 'http://188.245.226.105:8911'; // "wsProxy" or "wssProxy" or "wsSocksProxy" (depending on your proxy protocol)
await myEx.loadMarkets ();
//
// To ensure your WS proxy works, uncomment below code and watch the log
//
// myEx.verbose = true;
// await myEx.loadHttpProxyAgent ();
// await myEx.watch ('ws://188.245.226.105:9876/', 'myip'); // in the incoming logs, confirm that you see the proxy IP in "hello" message
//
console.log (await myEx.watchTicker ('BTC/USDT'));
await myEx.close ();
}
// await example_proxyUrl ();
await example_httpProxy ();
// await example_socksProxy ();
// await example_webSockets ();
@@ -0,0 +1,15 @@
// @ts-nocheck
// JavaScript sample Proxy with CORS support
// Save this in a file like cors.js and run with:
// node cors [port]
// It will listen for your requests on the port you pass in command line (or port 8080 by default)
import cors from 'cors-anywhere'; // npm install cors-anywhere
const port = (process.argv.length > 2) ? parseInt (process.argv[2]) : 8080; // if not provided from cli, default to 8080
cors.createServer ({
// you can set origin, if needed by exchange
// setHeaders: { 'origin': 'https://www.bitmex.com' }
}).listen (port, 'localhost');
console.log ('Running CORS Anywhere on localhost:' + port);
@@ -0,0 +1,17 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const binance = new ccxt.pro.binance ({});
const subscriptions = [
[ 'BTC/USDT', '5m' ],
[ 'ETH/USDT', '5m' ],
[ 'BTC/USDT', '1h' ],
];
while (true) {
const ohlcv = await binance.watchOHLCVForSymbols (subscriptions);
console.log (ohlcv);
}
}
await example ();
+14
View File
@@ -0,0 +1,14 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const binance = new ccxt.pro.binance ({});
const symbol = 'BTC/USDT';
const timeframe = '1m';
while (true) {
const ohlcv = await binance.watchOHLCV (symbol, timeframe);
console.log (ohlcv);
}
}
await example ();
@@ -0,0 +1,13 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const binance = new ccxt.pro.binance ({});
const symbols = [ 'BTC/USDT', 'ETH/USDT', 'DOGE/USDT' ];
while (true) {
const orderbook = await binance.watchOrderBookForSymbols (symbols);
console.log (orderbook['symbol'], orderbook['asks'][0], orderbook['bids'][0]);
}
}
await example ();
@@ -0,0 +1,13 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const binance = new ccxt.pro.binance ({});
const symbols = [ 'BTC/USDT', 'ETH/USDT', 'DOGE/USDT' ];
while (true) {
const trades = await binance.watchTradesForSymbols (symbols);
console.log (trades);
}
}
await example ();
+13
View File
@@ -0,0 +1,13 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const binance = new ccxt.pro.binance ({});
const symbols = [ 'BTC/USDT', 'ETH/USDT', 'DOGE/USDT' ];
while (true) {
const tickers = await binance.watchTickers (symbols);
console.log (tickers['BTC/USDT'], tickers['ETH/USDT'], tickers['DOGE/USDT']);
}
}
await example ();
@@ -0,0 +1,53 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
// watch and handle constinuosly
async function watchPositionsContinuously (exchange) {
while (true) {
try {
const positions = await exchange.watchPositions ();
console.log ('Fetched ', exchange.id, ' - Positions: ', positions);
} catch (e) {
console.log (e);
break;
}
}
}
// start exchanges and fetch OHLCV loop
async function startExchange (exchangeName, config) {
const ex = new ccxt[exchangeName] (config);
const promises = [];
promises.push (watchPositionsContinuously (ex));
await Promise.all (promises);
await ex.close ();
}
// main function
async function example () {
const exchanges = {
'binanceusdm': {
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_API_SECRET',
},
'okx': {
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_API_SECRET',
},
'huobi':{
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_API_SECRET',
},
};
const promises = [];
const exchangeIds = Object.keys (exchanges);
for (let i = 0; i < exchangeIds.length; i++) {
const exchangeName = exchangeIds[i];
const config = exchanges[exchangeName];
promises.push (startExchange (exchangeName, config));
}
await Promise.all (promises);
}
await example ();
+15
View File
@@ -0,0 +1,15 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const exchange = new ccxt.pro.binanceusdm ({
'apiKey': 'YOUR_API_KEY',
'secret': 'YOUR_API_SECRET'
});
while (true) {
const trades = await exchange.watchPositions ();
console.log (trades);
}
}
await example ();
@@ -0,0 +1,16 @@
import ccxt from '../../js/ccxt.js';
// AUTO-TRANSPILE //
async function example () {
const exchange = new ccxt.pro.binanceusdm ({
'apiKey': 'YOUR_API_KEY',
'secret': 'Your_API_SECRET'
});
const symbols = [ 'BTC/USDT:USDT', 'ETH/USDT:USDT', 'DOGE/USDT:USDT' ];
while (true) {
const trades = await exchange.watchPositions (symbols);
console.log (trades);
}
}
await example ();