Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Grid trade feature #248

Merged
merged 20 commits into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 241 additions & 0 deletions app/cronjob/trailingTrade/step/__tests__/determine-action.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ describe('determine-action.js', () => {
describe('execute', () => {
describe('when symbol is locked', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(_key => null);

rawData = {
action: 'not-determined',
symbol: 'BTCUSDT',
Expand Down Expand Up @@ -72,6 +74,8 @@ describe('determine-action.js', () => {

describe('when action is buy-order-wait', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(_key => null);

rawData = {
action: 'buy-order-wait',
symbol: 'BTCUSDT',
Expand Down Expand Up @@ -135,6 +139,8 @@ describe('determine-action.js', () => {

describe('when action is buy', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(_key => null);

rawData = {
action: 'buy',
symbol: 'BTCUSDT',
Expand Down Expand Up @@ -200,6 +206,8 @@ describe('determine-action.js', () => {
describe('when last buy price is not configured and current price is less or equal than trigger price', () => {
describe('when base asset balance has enough to sell', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(_key => null);

cache.getWithTTL = jest.fn().mockResolvedValue([
[null, -2],
[null, null]
Expand Down Expand Up @@ -327,8 +335,146 @@ describe('determine-action.js', () => {
});
});

describe('when grid trade buy order is found', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(key => {
if (key === `BTCUSDT-grid-trade-last-buy-order`) {
return JSON.stringify({
orderId: 27123456
});
}

return null;
});

cache.getWithTTL = jest.fn().mockResolvedValue([
[null, -2],
[null, null]
]);

rawData = {
action: 'not-determined',
symbol: 'BTCUSDT',
isLocked: false,
symbolInfo: {
baseAsset: 'BTC',
filterMinNotional: {
minNotional: '10.00000000'
}
},
baseAssetBalance: {
total: 0.0003
},
symbolConfiguration: {
buy: {
athRestriction: {
enabled: true
},
currentGridTradeIndex: 0,
currentGridTrade: {
triggerPercentage: 1,
stopPercentage: 1.025,
limitPercentage: 1.026,
maxPurchaseAmount: 10,
executed: false,
executedOrder: null
}
},
sell: {
stopLoss: {
enabled: false
},
currentGridTradeIndex: 0,
currentGridTrade: {
triggerPercentage: 1.03,
stopPercentage: 0.985,
limitPercentage: 0.984,
quantityPercentage: 0.8,
executed: false,
executedOrder: null
}
}
},
buy: {
currentPrice: 28000,
triggerPrice: 28000,
athRestrictionPrice: 29572.2
},
sell: {
currentPrice: 28000,
triggerPrice: null,
lastBuyPrice: null,
stopLossTriggerPrice: null
}
};

result = await step.execute(logger, rawData);
});

it('returns expected result', () => {
expect(result).toStrictEqual({
action: 'buy-order-wait',
symbol: 'BTCUSDT',
isLocked: false,
symbolInfo: {
baseAsset: 'BTC',
filterMinNotional: {
minNotional: '10.00000000'
}
},
baseAssetBalance: {
total: 0.0003
},
symbolConfiguration: {
buy: {
athRestriction: {
enabled: true
},
currentGridTradeIndex: 0,
currentGridTrade: {
triggerPercentage: 1,
stopPercentage: 1.025,
limitPercentage: 1.026,
maxPurchaseAmount: 10,
executed: false,
executedOrder: null
}
},
sell: {
stopLoss: {
enabled: false
},
currentGridTradeIndex: 0,
currentGridTrade: {
triggerPercentage: 1.03,
stopPercentage: 0.985,
limitPercentage: 0.984,
quantityPercentage: 0.8,
executed: false,
executedOrder: null
}
}
},
buy: {
currentPrice: 28000,
triggerPrice: 28000,
athRestrictionPrice: 29572.2,
processMessage: `There is a last gird trade buy order. Wait.`,
updatedAt: expect.any(Object)
},
sell: {
currentPrice: 28000,
triggerPrice: null,
lastBuyPrice: null,
stopLossTriggerPrice: null
}
});
});
});

describe('when the symbol is disabled', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(_key => null);
cache.getWithTTL = jest.fn().mockResolvedValue([
[null, 300],
[
Expand Down Expand Up @@ -466,6 +612,10 @@ describe('determine-action.js', () => {
});

describe('when the trigger price is higher than the ATH restriction price', () => {
beforeEach(() => {
cache.get = jest.fn().mockImplementation(_key => null);
});

describe('when the ATH restriction is enabled', () => {
describe('currentGridTradeIndex is 0', () => {
beforeEach(async () => {
Expand Down Expand Up @@ -985,6 +1135,8 @@ describe('determine-action.js', () => {

describe('when base asset balance does not have enough to sell', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(_key => null);

cache.getWithTTL = jest.fn().mockResolvedValue([
[null, -2],
[null, null]
Expand Down Expand Up @@ -1115,6 +1267,8 @@ describe('determine-action.js', () => {
describe('when last buy price is set and has enough to sell', () => {
describe('when current price is higher than trigger price', () => {
beforeEach(() => {
cache.get = jest.fn().mockImplementation(_key => null);

rawData = {
action: 'not-determined',
symbol: 'BTCUSDT',
Expand Down Expand Up @@ -1172,6 +1326,87 @@ describe('determine-action.js', () => {
};
});

describe('when grid trade sell order is found', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(key => {
if (key === `BTCUSDT-grid-trade-last-sell-order`) {
return JSON.stringify({
orderId: 27123456
});
}

return null;
});

cache.getWithTTL = jest.fn().mockResolvedValue([
[null, -2],
[null, null]
]);

result = await step.execute(logger, rawData);
});

it('returns expected result', () => {
expect(result).toStrictEqual({
action: 'sell-order-wait',
symbol: 'BTCUSDT',
isLocked: false,
symbolInfo: {
baseAsset: 'BTC',
filterMinNotional: {
minNotional: '10.00000000'
}
},
baseAssetBalance: {
total: 0.0006
},
symbolConfiguration: {
buy: {
athRestriction: {
enabled: true
},
currentGridTradeIndex: 0,
currentGridTrade: {
triggerPercentage: 1,
stopPercentage: 1.025,
limitPercentage: 1.026,
maxPurchaseAmount: 10,
executed: false,
executedOrder: null
}
},
sell: {
stopLoss: {
enabled: false
},
currentGridTradeIndex: 0,
currentGridTrade: {
triggerPercentage: 1.03,
stopPercentage: 0.985,
limitPercentage: 0.984,
quantityPercentage: 0.8,
executed: false,
executedOrder: null
}
}
},
buy: {
currentPrice: 31000,
triggerPrice: 28000,
athRestrictionPrice: 27000
},
sell: {
currentPrice: 31000,
triggerPrice: 30900,
lastBuyPrice: 30000,
stopLossTriggerPrice: 24000,
processMessage: `There is a last gird trade sell order. Wait.`,
updatedAt: expect.any(Object)
}
});
});
});

describe('when symbol is disabled', () => {
beforeEach(async () => {
cache.getWithTTL = jest.fn().mockResolvedValue([
Expand Down Expand Up @@ -1328,6 +1563,10 @@ describe('determine-action.js', () => {
});

describe('when current price is less than stop loss trigger price', () => {
beforeEach(() => {
cache.get = jest.fn().mockImplementation(_key => null);
});

describe('when stop loss is disabled', () => {
beforeEach(async () => {
rawData = {
Expand Down Expand Up @@ -1668,6 +1907,8 @@ describe('determine-action.js', () => {

describe('when current price is less than trigger price', () => {
beforeEach(async () => {
cache.get = jest.fn().mockImplementation(_key => null);

rawData = {
action: 'not-determined',
symbol: 'BTCUSDT',
Expand Down
Loading