Skip to content

Commit

Permalink
Merge pull request #53 from Adamant-im/dev
Browse files Browse the repository at this point in the history
v6.0.0
  • Loading branch information
dev-adamant-im authored Mar 20, 2024
2 parents 0221e2e + 8829df0 commit 815c35f
Show file tree
Hide file tree
Showing 43 changed files with 6,626 additions and 2,068 deletions.
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ logs/
.vscode/
tests.js
config.test
config.json
config.test.json
config.test.jsonc
config.json
config.jsonc
.DS_Store
.idea/
Expand All @@ -16,3 +17,6 @@ balances.out
trade/tests/debug_trader.js
client.py
test_encode.py
coverage/
trade/settings/
!trade/settings/tradeParams_Default.js
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Profit trading is a mode in which a bot runs orders according to some strategy.
* [Coinstore](https://h5.coinstore.com/h5/signup?invitCode=o951vZ)
* [FameEX](https://www.fameex.com/en-US/commissiondispense?code=MKKAWV)
* [NonKYC](https://nonkyc.io?ref=655b4df9eb13acde84677358)
* [XeggeX](https://xeggex.com?ref=656846d209bbed85b91aba4d)

To add other exchange support, see [marketmaking.app/services](https://marketmaking.app/services/).

Expand Down
42 changes: 29 additions & 13 deletions app.js
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
const notify = require('./helpers/notify');
const config = require('./modules/configReader');
const db = require('./modules/DB');
const doClearDB = process.argv.includes('clear_db');
const config = require('./modules/configReader');
const { initApi } = require('./routes/init');

// Socket connection
if (config.passPhrase) {
const api = require('./modules/api');
const txParser = require('./modules/incomingTxsParser');
// It may take up to a second to create trading params file 'tradeParams_{exchange}.js' from the default one
setTimeout(initServices, 1000);
// It may take up to a 5 seconds to get exchange markets and Infoservice rates
setTimeout(startModules, 5000);

api.socket.initSocket({ socket: config.socket, wsType: config.ws_type, onNewMessage: txParser, admAddress: config.address });
}
function initServices() {
try {
// Socket connection
if (config.passPhrase) {
const api = require('./modules/api');
const txParser = require('./modules/incomingTxsParser');

setTimeout(init, 5000);
api.socket.initSocket({ socket: config.socket, wsType: config.ws_type, onNewMessage: txParser, admAddress: config.address });
}

function init() {
try {
// Debug and health API init
const { initApi } = require('./routes/init');
if (config.api?.port) {
initApi();
}
} catch (e) {
console.error(`${config.notifyName} is not started. Error: ${e}`);
process.exit(1);
}
}

function startModules() {
try {
const notify = require('./helpers/notify');

if (doClearDB) {
console.log('Clearing database…');

db.systemDb.db.drop();
db.incomingTxsDb.db.drop();
db.ordersDb.db.drop();
db.fillsDb.db.drop();

notify(`*${config.notifyName}: database cleared*. Manually stop the Bot now.`, 'info');
} else {
if (config.passPhrase) {
Expand All @@ -41,7 +57,7 @@ function init() {
notify(`${config.notifyName} *started*${addressInfo} (${config.projectBranch}, v${config.version}).`, 'info');
}
} catch (e) {
notify(`${config.notifyName} is not started. Error: ${e}`, 'error');
console.error(`${config.notifyName} is not started. Error: ${e}`);
process.exit(1);
}
}
41 changes: 29 additions & 12 deletions config.default.jsonc
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
/**
The bot's secret passphrase. Create separate ADM account for the bot.
Bot's ADM address will correspond this passphrase.
The bot's secret passphrase. Create a separate ADM account for the bot.
Bot's ADM address will correspond to this passPhrase.
**/
"passPhrase": "distance expect praise frequent..",

/**
List of nodes to fetch transactions.
If one become unavailable, bot will choose live one.
If one becomes unavailable, the bot will choose a live one.
**/
"node_ADM": [
"https://bid.adamant.im",
Expand Down Expand Up @@ -38,7 +38,7 @@
"U123.."
],

/** Notify non-admins that they are not admins. If false, bot will be silent. **/
/** Notify non-admins that they are not admins. If false, the bot will be silent. **/
"notify_non_admins": false,

/** List of supported exchanges **/
Expand All @@ -48,7 +48,8 @@
"StakeCube",
"Coinstore",
"FameEX",
"NonKYC"
"NonKYC",
"XeggeX"
],

/** Exchange to work with. Case insensitive. **/
Expand All @@ -58,15 +59,31 @@
"pair": "ADM/USDT",

/**
Interval in minutes to clear all of Unknown (which are not placed by the bot) opened orders.
Some exchanges has API issues with closing or matching orders.
If an exchange doesn't publicly expose API for the pair, the bot can use private API.
Specific to exchange API implementation.
**/
"pair_hidden": false,

/**
Interval in minutes to clear all of the Unknown (which are not placed by the bot) opened orders.
Some exchanges have API issues with closing or matching orders.
Because of this, your account can accumulate open orders and lead to frozen balances.
In such a case you can run "/clear unk" command manually, or set this parameter for automatic clearing.
Note: this command cancels all of Unknown orders for the pair, including ones you placed on exchange's website.
In such a case, you can run the "/clear unk" command manually or set this parameter for automatic clearing.
Note: this command cancels all Unknown orders for the pair, including ones you placed on an exchange's website.
0 means disabled.
**/
"clearAllOrdersInterval": 0,

/** Exchange's custom restrictions to override `traderapi.features()`, if you have a special account **/
"exchange_restrictions": {
/** Max number of open orders. Set 'false' to skip **/
"orderNumberLimit": false,
/** If the exchange doesn't provide min order amount value, the bot uses the default one. Set 'false' to use DEFAULT_MIN_ORDER_AMOUNT_USD **/
"minOrderAmountUSD": false,
/** Same for the upper bound of the min order amount **/
"minOrderAmountUpperBoundUSD": false
},

/**
A short name which helps you to understand which exchange account you use.
Letters and digits only.
Expand All @@ -82,7 +99,7 @@
/** Exchange's account trade password or memo (if required by exchange) **/
"apipassword": "YOUR-TRADE-PASS",

/** Override project name for notifications. Letters, digits, - and ~ only. By default, it's derived from a repository name, TradeBot. **/
/** Override project name for notifications. Letters, digits, - and ~ only. By default, it's derived from a repository name, TradeBot-me. **/
"project_name": "",

/** Bot's name for notifications. Keep it empty if you want the default format ADM/USDT@Coinstore-acc1 TradeBot **/
Expand Down Expand Up @@ -124,14 +141,14 @@

/**
Enables health API
Allows to check if bot is running with http://ip:port/ping
Allows to check if a bot is running with http://ip:port/ping
**/
"health": false,

/**
Enables debug API
Do not set for live bots, use only for debugging.
Allows to get DBs records like http://ip:port/db?tb=incomingTxsDb
Allows to get DB records like http://ip:port/db?tb=incomingTxsDb
**/
"debug": false
},
Expand Down
4 changes: 4 additions & 0 deletions helpers/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ module.exports = {
LADDER_PREVIOUS_FILLED_ORDER_STATES: [undefined, 'Not placed', 'Filled', 'Cancelled', 'To be removed', 'Removed'],
REGEXP_WHOLE_NUMBER: /^[0-9]+$/,
REGEXP_UUID: /^[a-f\d]{4}(?:[a-f\d]{4}-){4}[a-f\d]{12}$/,
DEFAULT_API_PROCESSING_DELAY_MS: 100,
DEFAULT_MIN_ORDER_AMOUNT_USD: 0.1,
DEFAULT_MIN_ORDER_AMOUNT_UPPER_BOUND_USD: 2,
OVER_LIQUIDITY_SPREAD_PERCENT: 0.7,
};
117 changes: 117 additions & 0 deletions helpers/cryptos/exchanger.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const config = require('../../modules/configReader');
const tradeParams = require('../../trade/settings/tradeParams_' + config.exchange);
const orderUtils = require('../../trade/orderUtils');
const log = require('../log');
const constants = require('../const');
const utils = require('../utils');
Expand Down Expand Up @@ -115,6 +117,121 @@ module.exports = {
return pairs.includes(',' + coin + '/') || pairs.includes('/' + coin);
},

/**
* Parses a pair, exchange, account and project name from full pair string
* @param {string} pair A pair or a pair with an exchange, account and project name.
* Examples:
* - ADM/USDT
* - ADM/USDT@Bittrex
* - ADM/USDT@Bittrex-acc1
* - ADM/USDT@Bittrex-acc1 TradeBot
* - ADM/USDT@Bittrex-acc1+TradeBot
* @return {Object}
*/
parsePair(pair) {
let baseCoin; let quoteCoin; let exchange; let account; let project;

if (pair.includes(' ')) {
[pair, project] = pair.split(' ');
} else if (pair.includes('+')) {
[pair, project] = pair.split('+');
}

if (pair.includes('-')) {
[pair, account] = pair.split('-');
}

if (pair.includes('@')) {
[pair, exchange] = pair.split('@');
}

if (pair.includes('_')) {
[baseCoin, quoteCoin] = pair.split('_');
} else if (pair.includes('/')) {
[baseCoin, quoteCoin] = pair.split('/');
}

return {
pair,
baseCoin,
quoteCoin,
exchange,
account,
project,
};
},

/**
* Estimates daily mm trading volume according to tradeParams
* @param maxAmount If to override tradeParams.mm_maxAmount
* @return {Object} Estimate mm trade volume in coin1, coin2, USD, USDT and BTC
*/
estimateCurrentDailyTradeVolume(maxAmount) {
try {
maxAmount = maxAmount || tradeParams.mm_maxAmount;
const midAmount = (tradeParams.mm_minAmount + maxAmount) / 2;
const midInterval = (tradeParams.mm_minInterval + tradeParams.mm_maxInterval) / 2;
const dailyTrades = constants.DAY / midInterval;
const dailyVolumeCoin1 = midAmount * dailyTrades;
return this.calcCoin1AmountInOtherCoins(dailyVolumeCoin1);
} catch (e) {
log.error(`Error in estimateCurrentDailyTradeVolume() of ${utils.getModuleName(module.id)} module: ` + e);
}
},

/**
* Calculates coin1 amount in coin1, coin2, USD, USDT and BTC
* @param coin1Amount Amount in coin1
* @return {Object}
*/
calcCoin1AmountInOtherCoins(coin1Amount) {
try {
return {
coin1: coin1Amount,
coin2: this.convertCryptos(config.coin1, config.coin2, coin1Amount).outAmount,
USD: this.convertCryptos(config.coin1, 'USD', coin1Amount).outAmount,
USDT: this.convertCryptos(config.coin1, 'USDT', coin1Amount).outAmount,
BTC: this.convertCryptos(config.coin1, 'BTC', coin1Amount).outAmount,
};
} catch (e) {
log.error(`Error in calcCoin1AmountInOtherCoins() of ${utils.getModuleName(module.id)} module: ` + e);
}
},

/**
* Calculates mm_maxAmount from mm trade volume.
* mm_minInterval, mm_maxInterval and mm_minAmount will stay the same
* @return {Number} New tradeParams.mm_maxAmount
*/
calcMaxAmountFromDailyTradeVolume(dailyVolumeCoin1) {
try {
const midInterval = (tradeParams.mm_minInterval + tradeParams.mm_maxInterval) / 2;
const dailyTrades = constants.DAY / midInterval;
const new_mm_maxAmount = (2 * dailyVolumeCoin1 / dailyTrades) - tradeParams.mm_minAmount;
return utils.isPositiveNumber(new_mm_maxAmount) ? new_mm_maxAmount : undefined;
} catch (e) {
log.error(`Error in calcMaxAmountFromDailyTradeVolume() of ${utils.getModuleName(module.id)} module: ` + e);
}
},

/**
* Creates volume change infoString
* @return {String}
*/
getVolumeChangeInfoString(oldVolume, newVolume) {
try {
const coin1Decimals = orderUtils.parseMarket(config.pair).coin1Decimals;
const coin2Decimals = orderUtils.parseMarket(config.pair).coin2Decimals;

let infoString = `from ${utils.formatNumber(oldVolume.coin1.toFixed(coin1Decimals), true)} ${config.coin1} (${utils.formatNumber(oldVolume.coin2.toFixed(coin2Decimals), true)} ${config.coin2})`;
infoString += ` to ${utils.formatNumber(newVolume.coin1.toFixed(coin1Decimals), true)} ${config.coin1} (${utils.formatNumber(newVolume.coin2.toFixed(coin2Decimals), true)} ${config.coin2})`;

return infoString;
} catch (e) {
log.error(`Error in getVolumeChangeInfoString() of ${utils.getModuleName(module.id)} module: ` + e);
}
},

ADM: new adm_utils(),
};

Expand Down
10 changes: 9 additions & 1 deletion helpers/dbModel.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
module.exports = (db) => {
class Model {
constructor(data = {}, shouldSave) {
/**
* Creates a Mongod record/document
* Note: The constructor is not async; if you want to store the data in the database, consider that it will take time.
* As a workaround, create a document with shouldSave=false and then do 'await record.save()'
* @param {*} data Data to store
* @param {boolean} [shouldSave=false] If store date in the database
*/
constructor(data = {}, shouldSave = false) {
this.db = db;

Object.assign(this, data);
Expand All @@ -9,6 +16,7 @@ module.exports = (db) => {
this.save();
}
}

static get db() {
return db;
}
Expand Down
18 changes: 18 additions & 0 deletions helpers/networks.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,16 @@ const networks = {
},
'AVAX-C-CHAIN': {
code: 'AVAX-C-CHAIN',
altcode: 'AVAX-CCHAIN',
name: 'Avalanche C-Chain',
sampleAddress: '0xf41ca2e343a827403527c6b3c1fa91a9b134d45b',
},
'AVAX-X-CHAIN': {
code: 'AVAX-X-CHAIN',
altcode: 'AVAX-XCHAIN',
name: 'Avalanche X-Chain',
sampleAddress: 'X-avax1tzdcgj4ehsvhhgpl7zylwpw0gl2rxcg4r5afk5',
},
MATIC: {
code: 'MATIC',
name: 'Polygon',
Expand Down Expand Up @@ -66,6 +73,7 @@ const networks = {
},
SOL: {
code: 'SOL',
altcode: 'SPL',
name: 'Solana',
sampleAddress: '31Sof5r1xi7dfcaz4x9Kuwm8J9ueAdDduMcme59sP8gc',
},
Expand Down Expand Up @@ -124,6 +132,16 @@ const networks = {
name: 'Stellar',
sampleAddress: 'GB5A3OA657UWF3BN7WU4XFFWT333HFP2KFK2OFAXPEL3BBGQ7QLRNASG',
},
CAP20: {
code: 'CAP20',
name: 'Chiliz Chain',
sampleAddress: '0x579391C9865545000d8922ACF71a660521cc6404',
},
BRC20: {
code: 'BRC20',
name: 'Ordinals',
sampleAddress: 'bc1pxaneaf3w4d27hl2y93fuft2xk6m4u3wc4rafevc6slgd7f5tq2dqyfgy06',
},
};

module.exports = networks;
Loading

0 comments on commit 815c35f

Please sign in to comment.