Skip to content

Commit

Permalink
feat: added multiple tradingview indicators (#539)
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisleekr committed May 4, 2024
1 parent 0c04b66 commit 7988078
Show file tree
Hide file tree
Showing 88 changed files with 9,134 additions and 7,058 deletions.
4 changes: 3 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.tabSize": 2,
"eslint.alwaysShowStatus": true,
"files.trimTrailingWhitespace": true,
"files.exclude": {},
"files.insertFinalNewline": true,
Expand All @@ -18,10 +17,13 @@
"cSpell.words": [
"bbands",
"Bollinger",
"buildx",
"chrisleekr",
"cummulative",
"hgetall",
"MACD",
"mrkdwn",
"redlock",
"tradingview",
"tulind",
"uuidv"
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

All notable changes to this project will be documented in this file.

## Unreleased

- Added multiple TradingView indicators - [#539](https://github.com/chrisleekr/binance-trading-bot/pull/539)

## [0.0.98] - 2023-04-13

- Added the prefix to environment parameter for `TRADINGVIEW` related - [#616](https://github.com/chrisleekr/binance-trading-bot/pull/616)
Expand Down
12 changes: 12 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,18 @@ module.exports = grunt => {
'./public/dist/js/SymbolEditLastBuyPriceIcon.min.js',
'./public/dist/js/SymbolTriggerSellIcon.min.js',
'./public/dist/js/SymbolTriggerBuyIcon.min.js',
'./public/dist/js/SymbolSettingActionResetGridTrade.min.js',
'./public/dist/js/SymbolSettingActionResetToGlobalSetting.min.js',
'./public/dist/js/SymbolSettingActions.min.js',
'./public/dist/js/SymbolSettingIconBotOptionsAutoTriggerBuy.min.js',
'./public/dist/js/SymbolSettingIconBotOptions.min.js',
'./public/dist/js/SymbolSettingIconTradingView.min.js',
'./public/dist/js/SymbolSettingIconGridBuy.min.js',
'./public/dist/js/SymbolSettingIconGridSell.min.js',
'./public/dist/js/SymbolSettingIcon.min.js',
'./public/dist/js/SymbolLogsIcon.min.js',
'./public/dist/js/CoinWrapperSymbol.min.js',
'./public/dist/js/CoinWrapperTradingViews.min.js',
'./public/dist/js/CoinWrapperTradingView.min.js',
'./public/dist/js/CoinWrapper.min.js',
'./public/dist/js/DustTransferIcon.min.js',
Expand All @@ -68,6 +74,12 @@ module.exports = grunt => {
'./public/dist/js/SettingIconActions.min.js',
'./public/dist/js/SettingIconGridSell.min.js',
'./public/dist/js/SettingIconGridBuy.min.js',
'./public/dist/js/SettingIconTradingView.min.js',
'./public/dist/js/SettingIconBotOptionsLogs.min.js',
'./public/dist/js/SettingIconBotOptionsOrderLimit.min.js',
'./public/dist/js/SettingIconBotOptionsAutoTriggerBuy.min.js',
'./public/dist/js/SettingIconBotOptionsAuthentication.min.js',
'./public/dist/js/SettingIconBotOptionsTradingView.min.js',
'./public/dist/js/SettingIconBotOptions.min.js',
'./public/dist/js/SettingIconLastBuyPriceRemoveThreshold.min.js',
'./public/dist/js/SettingIcon.min.js',
Expand Down
18 changes: 18 additions & 0 deletions app/__tests__/error-handler.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,23 @@ describe('error-handler', () => {
}).toThrow(`something-unhandled`);
});
});

describe(`redlock error`, () => {
it('does not throws an error', async () => {
expect(() => {
process.on = jest.fn().mockImplementation((event, error) => {
if (event === 'uncaughtException') {
error({
message: `redlock:lock-XRPBUSD`,
code: 500
});
}
});

const { runErrorHandler } = require('../error-handler');
runErrorHandler(mockLogger);
}).not.toThrow();
});
});
});
});
9 changes: 7 additions & 2 deletions app/__tests__/server-cronjob.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('server-cronjob', () => {

let mockExecuteAlive;
let mockExecuteTrailingTradeIndicator;
let mockExecuteTradingView;

describe('cronjob running fine', () => {
beforeEach(async () => {
Expand All @@ -19,10 +20,12 @@ describe('server-cronjob', () => {

mockExecuteAlive = jest.fn().mockResolvedValue(true);
mockExecuteTrailingTradeIndicator = jest.fn().mockResolvedValue(true);
mockExecuteTradingView = jest.fn().mockResolvedValue(true);

jest.mock('../cronjob', () => ({
executeAlive: mockExecuteAlive,
executeTrailingTradeIndicator: mockExecuteTrailingTradeIndicator
executeTrailingTradeIndicator: mockExecuteTrailingTradeIndicator,
executeTradingView: mockExecuteTradingView
}));

mockCronJob = jest
Expand Down Expand Up @@ -201,10 +204,12 @@ describe('server-cronjob', () => {
mockExecuteTrailingTradeIndicator = jest.fn().mockImplementation(() => {
setTimeout(() => Promise.resolve(true), 30000);
});
mockExecuteTradingView = jest.fn().mockResolvedValue(true);

jest.mock('../cronjob', () => ({
executeAlive: mockExecuteAlive,
executeTrailingTradeIndicator: mockExecuteTrailingTradeIndicator
executeTrailingTradeIndicator: mockExecuteTrailingTradeIndicator,
executeTradingView: mockExecuteTradingView
}));

mockCronJob = jest
Expand Down
50 changes: 50 additions & 0 deletions app/binance/__tests__/orders.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ describe('orders.js', () => {
let spyOnClearInterval;

let mockUpdateGridTradeLastOrder;
let mockDeleteGridTradeOrder;
let mockGetOpenOrdersFromAPI;
let mockErrorHandlerWrapper;

Expand Down Expand Up @@ -216,6 +217,7 @@ describe('orders.js', () => {
);
});
});

describe('when database orders not found', () => {
beforeEach(async () => {
mongoMock.findAll = jest.fn().mockResolvedValue([]);
Expand Down Expand Up @@ -245,5 +247,53 @@ describe('orders.js', () => {
expect(mockUpdateGridTradeLastOrder).not.toHaveBeenCalled();
});
});

describe('when an error is occurred', () => {
beforeEach(async () => {
mongoMock.findAll = jest.fn().mockResolvedValue([
{
key: 'BTCUSDT',
order: {
symbol: 'BTCUSDT',
cummulativeQuoteQty: '0.00000000',
executedQty: '0.00000000',
isWorking: false,
orderId: 7479643460,
origQty: '0.00920000',
price: '3248.37000000',
side: 'BUY',
status: 'NEW',
stopPrice: '3245.19000000',
type: 'STOP_LOSS_LIMIT',
updateTime: 1642713283562
}
}
]);

binanceMock.client.getOrder = jest
.fn()
.mockRejectedValue(new Error('something happened'));

mockUpdateGridTradeLastOrder = jest.fn();

mockDeleteGridTradeOrder = jest.fn().mockResolvedValue(true);

jest.mock('../../cronjob/trailingTradeHelper/order', () => ({
updateGridTradeLastOrder: mockUpdateGridTradeLastOrder,
deleteGridTradeOrder: mockDeleteGridTradeOrder
}));

const { syncDatabaseOrders } = require('../orders');

await syncDatabaseOrders(loggerMock);
});

it('triggers deleteGridTradeOrder', () => {
expect(mockDeleteGridTradeOrder).toHaveBeenCalledWith(
loggerMock,
'BTCUSDT'
);
});
});
});
});
35 changes: 23 additions & 12 deletions app/binance/orders.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ const {
getOpenOrdersFromAPI
} = require('../cronjob/trailingTradeHelper/common');
const {
updateGridTradeLastOrder
updateGridTradeLastOrder,
deleteGridTradeOrder
} = require('../cronjob/trailingTradeHelper/order');
const { errorHandlerWrapper } = require('../error-handler');

Expand Down Expand Up @@ -70,22 +71,32 @@ const syncDatabaseOrders = async logger => {
{}
);

await Promise.all(
return Promise.all(
databaseOrders.map(async databaseOrder => {
const { order } = databaseOrder;
const { key, order } = databaseOrder;
const { symbol, orderId } = order;

const orderResult = await binance.client.getOrder({
symbol,
orderId
});
try {
const orderResult = await binance.client.getOrder({
symbol,
orderId
});

const { side } = orderResult;
const { side } = orderResult;

return updateGridTradeLastOrder(logger, symbol, side.toLowerCase(), {
...order,
...orderResult
});
return updateGridTradeLastOrder(logger, symbol, side.toLowerCase(), {
...order,
...orderResult
});
} catch (e) {
logger.info(
{ databaseOrder, saveLog: true, e },
`There was an error that occurred while retrieving the last grid order. ` +
`Delete the order - ${symbol} - ${orderId}`
);

return deleteGridTradeOrder(logger, key);
}
})
);
};
Expand Down
3 changes: 2 additions & 1 deletion app/cronjob/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ describe('index', () => {
expect(index).toStrictEqual({
executeAlive: expect.any(Function),
executeTrailingTrade: expect.any(Function),
executeTrailingTradeIndicator: expect.any(Function)
executeTrailingTradeIndicator: expect.any(Function),
executeTradingView: expect.any(Function)
});
});
});
86 changes: 86 additions & 0 deletions app/cronjob/__tests__/tradingView.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/* eslint-disable max-classes-per-file */
/* eslint-disable global-require */
const { logger } = require('../../helpers');

describe('tradingView', () => {
let mockLoggerInfo;

let mockGetGlobalConfiguration;
let mockGetTradingView;

let mockErrorHandlerWrapper;

beforeEach(() => {
jest.clearAllMocks().resetModules();

mockLoggerInfo = jest.fn();

logger.info = mockLoggerInfo;
jest.mock('../../helpers', () => ({
logger: {
info: mockLoggerInfo,
error: jest.fn(),
warn: jest.fn(),
debug: jest.fn(),
child: jest.fn()
}
}));

mockErrorHandlerWrapper = jest
.fn()
.mockImplementation((_logger, _job, callback) =>
Promise.resolve(callback())
);

jest.mock('../../error-handler', () => ({
errorHandlerWrapper: mockErrorHandlerWrapper
}));
});

const mockSteps = () => {
mockGetGlobalConfiguration = jest
.fn()
.mockImplementation((_logger, rawData) => ({
...rawData,
...{
globalConfiguration: {
global: 'configuration data'
}
}
}));

mockGetTradingView = jest.fn().mockImplementation((_logger, rawData) => ({
...rawData,
...{
tradingView: { tradingview: 'data' }
}
}));

jest.mock('../trailingTradeIndicator/steps', () => ({
getGlobalConfiguration: mockGetGlobalConfiguration,
getTradingView: mockGetTradingView
}));
};

describe('without any error', () => {
beforeEach(async () => {
mockSteps();

const { execute: tradingViewExecute } = require('../tradingView');

await tradingViewExecute(logger);
});

it('returns expected result', () => {
expect(mockLoggerInfo).toHaveBeenCalledWith(
{
data: {
globalConfiguration: { global: 'configuration data' },
tradingView: { tradingview: 'data' }
}
},
'TradingView: Finish process...'
);
});
});
});
10 changes: 0 additions & 10 deletions app/cronjob/__tests__/trailingTradeIndicator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ describe('trailingTradeIndicator', () => {
let mockExecuteDustTransfer;
let mockGetClosedTrades;
let mockGetOrderStats;
let mockGetTradingView;
let mockSaveDataToCache;

let mockExecute;
Expand Down Expand Up @@ -132,13 +131,6 @@ describe('trailingTradeIndicator', () => {
}
}));

mockGetTradingView = jest.fn().mockImplementation((_logger, rawData) => ({
...rawData,
...{
tradingView: 'retrieved'
}
}));

mockSaveDataToCache = jest.fn().mockImplementation((_logger, rawData) => ({
...rawData,
...{
Expand All @@ -155,7 +147,6 @@ describe('trailingTradeIndicator', () => {
executeDustTransfer: mockExecuteDustTransfer,
getClosedTrades: mockGetClosedTrades,
getOrderStats: mockGetOrderStats,
getTradingView: mockGetTradingView,
saveDataToCache: mockSaveDataToCache
}));
};
Expand Down Expand Up @@ -207,7 +198,6 @@ describe('trailingTradeIndicator', () => {
dustTransfer: 'dust-transfer',
getClosedTrades: 'executed',
getOrderStats: 'retrieved',
tradingView: 'retrieved',
saved: 'data-to-cache'
}
},
Expand Down
4 changes: 3 additions & 1 deletion app/cronjob/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ const { execute: executeTrailingTrade } = require('./trailingTrade');
const {
execute: executeTrailingTradeIndicator
} = require('./trailingTradeIndicator');
const { execute: executeTradingView } = require('./tradingView');

module.exports = {
executeAlive,
executeTrailingTrade,
executeTrailingTradeIndicator
executeTrailingTradeIndicator,
executeTradingView
};
Loading

0 comments on commit 7988078

Please sign in to comment.