From 69b3f33dce93cadfe7cba22cdd36d29ba5cf60a1 Mon Sep 17 00:00:00 2001 From: egisz Date: Wed, 22 Apr 2020 12:30:55 +0300 Subject: [PATCH 1/3] Enable short/long trades without exit of open trade --- src/dict/exchange_position.js | 4 ++-- src/modules/pairs/pair_state_execution.js | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/dict/exchange_position.js b/src/dict/exchange_position.js index 57e883cac..444dae159 100644 --- a/src/dict/exchange_position.js +++ b/src/dict/exchange_position.js @@ -3,7 +3,7 @@ const Position = require('./position'); module.exports = class ExchangePosition { constructor(exchange, position) { if (!(position instanceof Position)) { - throw 'TypeError: invalid position'; + throw new Error(`TypeError: invalid position`); } this._exchange = exchange; @@ -11,7 +11,7 @@ module.exports = class ExchangePosition { } getKey() { - return this._exchange + this._position.symbol; + return this._exchange + this._position.symbol + this._position.side; } getExchange() { diff --git a/src/modules/pairs/pair_state_execution.js b/src/modules/pairs/pair_state_execution.js index 73e7d9503..477874cfa 100644 --- a/src/modules/pairs/pair_state_execution.js +++ b/src/modules/pairs/pair_state_execution.js @@ -37,7 +37,7 @@ module.exports = class PairStateExecution { async onSellBuyPair(pair, side) { const position = await this.exchangeManager.getPosition(pair.exchange, pair.symbol); - if (position) { + if (position && position.side === side) { this.pairStateManager.clear(pair.exchange, pair.symbol); this.logger.debug(`block ${side} order; open position:${JSON.stringify([pair.exchange, pair.symbol])}`); return; @@ -64,6 +64,10 @@ module.exports = class PairStateExecution { `Pair State: Create position open order: ${JSON.stringify([pair.exchange, pair.symbol, side, pair.options])}` ); + if (position) { + // If we have open position and we want to switch short/long, we need to add position size to order size + pair.options.positionAmount = position.amount; + } const exchangeOrder = await this.executeOrder(pair.exchange, pair.symbol, side, pair.options); if (exchangeOrder) { @@ -304,6 +308,10 @@ module.exports = class PairStateExecution { return undefined; } + if (options && options.positionAmount) { + orderSize = parseFloat(orderSize) + Math.abs(options.positionAmount); + } + // inverse price for short if (side === 'short') { orderSize *= -1; From 5f0ec1e34c58d8264fdf6a774d1b034bcb944eaa Mon Sep 17 00:00:00 2001 From: egisz Date: Thu, 23 Apr 2020 13:18:48 +0300 Subject: [PATCH 2/3] fix eslint errors --- src/modules/pairs/pair_state_execution.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/modules/pairs/pair_state_execution.js b/src/modules/pairs/pair_state_execution.js index 477874cfa..7eb3656fb 100644 --- a/src/modules/pairs/pair_state_execution.js +++ b/src/modules/pairs/pair_state_execution.js @@ -64,12 +64,13 @@ module.exports = class PairStateExecution { `Pair State: Create position open order: ${JSON.stringify([pair.exchange, pair.symbol, side, pair.options])}` ); + const pairOptions = pair.options; if (position) { // If we have open position and we want to switch short/long, we need to add position size to order size - pair.options.positionAmount = position.amount; - } - const exchangeOrder = await this.executeOrder(pair.exchange, pair.symbol, side, pair.options); + pairOptions.positionAmount = position.amount; + } + const exchangeOrder = await this.executeOrder(pair.exchange, pair.symbol, side, pairOptions); if (exchangeOrder) { if (exchangeOrder.shouldCancelOrderProcess()) { // check if we need to to cancel the process From 658dfa5e11bdbdd8112214df05bf5c7b2235828b Mon Sep 17 00:00:00 2001 From: egisz Date: Fri, 27 Nov 2020 14:57:51 +0200 Subject: [PATCH 3/3] fix conflicts with upstream, add tests --- src/dict/exchange_position.js | 4 +- src/dict/position.js | 10 ++- src/modules/pairs/pair_state_execution.js | 10 ++- .../pairs/pair_state_execution.test.js | 65 +++++++++++++++++++ 4 files changed, 78 insertions(+), 11 deletions(-) diff --git a/src/dict/exchange_position.js b/src/dict/exchange_position.js index 444dae159..43e797c82 100644 --- a/src/dict/exchange_position.js +++ b/src/dict/exchange_position.js @@ -11,7 +11,7 @@ module.exports = class ExchangePosition { } getKey() { - return this._exchange + this._position.symbol + this._position.side; + return this._exchange + this._position.getSymbol() + this._position.getSide(); } getExchange() { @@ -23,6 +23,6 @@ module.exports = class ExchangePosition { } getSymbol() { - return this._position.symbol; + return this._position.getSymbol(); } }; diff --git a/src/dict/position.js b/src/dict/position.js index ecca09eab..8eb202385 100644 --- a/src/dict/position.js +++ b/src/dict/position.js @@ -18,7 +18,7 @@ module.exports = class Position { * @param raw */ constructor(symbol, side, amount, profit, updatedAt, entry, createdAt, raw = undefined) { - if (!['long', 'short'].includes(side)) { + if (![Position.SIDE_LONG, Position.SIDE_SHORT].includes(side)) { throw new Error(`Invalid position direction given:${side}`); } @@ -41,11 +41,11 @@ module.exports = class Position { } isShort() { - return this.getSide() === 'short'; + return this.getSide() === Position.SIDE_SHORT; } isLong() { - return this.getSide() === 'long'; + return this.getSide() === Position.SIDE_LONG; } getAmount() { @@ -64,6 +64,10 @@ module.exports = class Position { return this.entry; } + getSide() { + return this.side; + } + getCreatedAt() { return this.createdAt; } diff --git a/src/modules/pairs/pair_state_execution.js b/src/modules/pairs/pair_state_execution.js index edfd2cd74..267d930e2 100644 --- a/src/modules/pairs/pair_state_execution.js +++ b/src/modules/pairs/pair_state_execution.js @@ -60,14 +60,12 @@ module.exports = class PairStateExecution { ])}` ); - const pairOptions = pair.options; if (position) { // If we have open position and we want to switch short/long, we need to add position size to order size - pairState.positionAmount = position.getAmount(); + pairState.options = pairState.options || {}; + pairState.options.positionAmount = position.getAmount(); } const exchangeOrder = await this.pairStateExecuteOrder(pairState); - - const exchangeOrder = await this.executeOrder(pair.exchange, pair.symbol, side, pairOptions); if (exchangeOrder) { if (exchangeOrder.shouldCancelOrderProcess()) { // check if we need to to cancel the process @@ -278,8 +276,8 @@ module.exports = class PairStateExecution { return; } - if (pair.positionAmount) { - orderSize = parseFloat(orderSize) + Math.abs(pair.positionAmount); + if (options.positionAmount) { + orderSize = parseFloat(orderSize) + Math.abs(options.positionAmount); } // inverse price for short diff --git a/test/modules/pairs/pair_state_execution.test.js b/test/modules/pairs/pair_state_execution.test.js index 972eb5d8e..40f0df3c0 100644 --- a/test/modules/pairs/pair_state_execution.test.js +++ b/test/modules/pairs/pair_state_execution.test.js @@ -69,6 +69,71 @@ describe('#pair state execution', function() { assert.equal(myOrder.hasAdjustedPrice(), false); }); + it('test limit open order trigger for long when we have short position', async () => { + let myOrder; + + const executor = new PairStateExecution( + undefined, + { + calculateOrderSizeCapital: () => { + return 1337; + } + }, + { + executeOrder: (exchange, order) => { + myOrder = order; + return undefined; + } + }, + undefined + ); + + await executor.pairStateExecuteOrder( + PairState.createLong('exchange', 'BTCUSD', OrderCapital.createAsset(1369), {positionAmount: -1337}, true, () => {}) + ); + + assert.equal(myOrder.symbol, 'BTCUSD'); + assert.equal(myOrder.side, 'long'); + assert.equal(myOrder.price, undefined); + assert.equal(myOrder.amount, 1337*2); + assert.equal(myOrder.type, 'limit'); + assert.equal(myOrder.options.post_only, true); + assert.equal(myOrder.hasAdjustedPrice(), true); + }); + + + it('test limit open order trigger for short when we have long position', async () => { + let myOrder; + + const executor = new PairStateExecution( + undefined, + { + calculateOrderSizeCapital: () => { + return 1337; + } + }, + { + executeOrder: (exchange, order) => { + myOrder = order; + return undefined; + } + }, + undefined + ); + + await executor.pairStateExecuteOrder( + PairState.createShort('exchange', 'BTCUSD', OrderCapital.createAsset(1369), {positionAmount: 1337}, true, () => {}) + ); + + assert.equal(myOrder.symbol, 'BTCUSD'); + assert.equal(myOrder.side, 'short'); + assert.equal(myOrder.price, undefined); + assert.equal(myOrder.amount, -1337*2); + assert.equal(myOrder.type, 'limit'); + assert.equal(myOrder.options.post_only, true); + assert.equal(myOrder.hasAdjustedPrice(), true); + }); + it('test limit open order trigger for short', async () => { let myOrder;