diff --git a/src/js/apis/binance/BinanceApiClient.js b/src/js/apis/binance/BinanceApiClient.js index be3751a..8362b95 100644 --- a/src/js/apis/binance/BinanceApiClient.js +++ b/src/js/apis/binance/BinanceApiClient.js @@ -29,6 +29,11 @@ export default class BinanceApiClient extends APIClient { */ #restClient = null; + /** + * @type {Map} + */ + #assetPermissions = new Map(); + /** * @param {BinanceApiConfig} config * @param {Logger} logger @@ -37,6 +42,7 @@ export default class BinanceApiClient extends APIClient { super(config, logger); this.#wsClient = new WebsocketClient(config.getWSClientConfig()); this.#restClient = new MainClient(config.getRestClientConfig()); + this.#loadAssetInfo(); this.#wsClient.on('formattedMessage', this.#wsMessageHandler.bind(this)); this.#wsClient.on('open', this.#connectionOpened.bind(this)); this.#wsClient.on('reconnecting', this.#reconnected.bind(this)); @@ -98,8 +104,8 @@ export default class BinanceApiClient extends APIClient { } const event = new TradeEvent(tradingPair); - event.price = data.price; - event.quantity = data.quantity; + event.price = parseFloat(data.price); + event.quantity = parseFloat(data.quantity); this.dispatchAssetEvent(event); } @@ -115,12 +121,12 @@ export default class BinanceApiClient extends APIClient { const event = new OrderBookUpdateEvent(tradingPair); event.updateId = data.lastUpdateId; if(data.bids && data.asks){ - data.bids.forEach(b => event.bids.push(new OrderBookEntry(b[0], b[1]))); - data.asks.forEach(a => event.asks.push(new OrderBookEntry(a[0], a[1]))); + data.bids.forEach(b => event.bids.push(new OrderBookEntry(parseFloat(b[0]), parseFloat(b[1])))); + data.asks.forEach(a => event.asks.push(new OrderBookEntry(parseFloat(a[0]), parseFloat(a[1])))); event.isReset = true; } else { - data.bidDepthDelta.forEach(b => event.bids.push(new OrderBookEntry(b.price, b.quantity))); - data.askDepthDelta .forEach(a => event.asks.push(new OrderBookEntry(a.price, a.quantity))); + data.bidDepthDelta.forEach(b => event.bids.push(new OrderBookEntry(parseFloat(b.price), parseFloat(b.quantity)))); + data.askDepthDelta .forEach(a => event.asks.push(new OrderBookEntry(parseFloat(a.price), parseFloat(a.quantity)))); } @@ -136,16 +142,16 @@ export default class BinanceApiClient extends APIClient { } const event = new Ticker24hEvent(tradingPair); - event.priceChange = data.priceChange; - event.priceChangePercent = data.priceChangePercent; - event.weightedAveragePrice = data.weightedAveragePrice; - event.bestBidPrice = data.bestBid; - event.bestBidQuantity = data.bestBidQuantity; - event.bestAskPrice = data.bestAskPrice; - event.bestAskQuantity = data.bestAskQuantity; - event.high = data.high; - event.low = data.low; - event.trades = data.trades; + event.priceChange = parseFloat(data.priceChange); + event.priceChangePercent = parseFloat(data.priceChangePercent); + event.weightedAveragePrice = parseFloat(data.weightedAveragePrice); + event.bestBidPrice = parseFloat(data.bestBid); + event.bestBidQuantity = parseFloat(data.bestBidQuantity); + event.bestAskPrice = parseFloat(data.bestAskPrice); + event.bestAskQuantity = parseFloat(data.bestAskQuantity); + event.high = parseFloat(data.high); + event.low = parseFloat(data.low); + event.trades = parseFloat(data.trades); this.dispatchAssetEvent(event); } @@ -158,10 +164,10 @@ export default class BinanceApiClient extends APIClient { return; } - const event = new KLineEvent(tradingPair, data.kline.high, data.kline.low, data.kline.open, data.kline.close); + const event = new KLineEvent(tradingPair, parseFloat(data.kline.high), parseFloat(data.kline.low), parseFloat(data.kline.open), parseFloat(data.kline.close)); event.isClosed = data.kline.final; - event.startTime = data.kline.startTime; - event.endTime = data.kline.endTime; + event.startTime = parseFloat(data.kline.startTime); + event.endTime = parseFloat(data.kline.endTime); this.dispatchAssetEvent(event); } @@ -172,7 +178,7 @@ export default class BinanceApiClient extends APIClient { async #fireAccountBalanceChangeEvent(data) { const event = new BalanceChangeEvent(); data.balances.forEach(balanceUpdate => { - event.setChangedBalance(balanceUpdate.asset, balanceUpdate.availableBalance); + event.setChangedBalance(balanceUpdate.asset, parseFloat(balanceUpdate.availableBalance)); }); this.dispatchAccountEvent(event); @@ -234,7 +240,7 @@ export default class BinanceApiClient extends APIClient { const event = new BalanceChangeEvent(); resp.forEach(coinInfo => { if (coinInfo.free > 0) { - event.setChangedBalance(coinInfo.coin, coinInfo.free); + event.setChangedBalance(coinInfo.coin, parseFloat(coinInfo.free)); } }); @@ -260,17 +266,18 @@ export default class BinanceApiClient extends APIClient { "symbol": transaction.symbol.getKey(), "side": "BUY", "type": "LIMIT", - "price": transaction.buySettings.orderPrice, - "quantity" : transaction.buySettings.orderQuantity, + "price": this.#correctPriceForAsset(transaction.buySettings.orderPrice, transaction.symbol.getKey()), + "quantity": this.#correctQuantityForAsset(transaction.buySettings.orderQuantity, transaction.symbol.getKey()), "newClientOrderId": transaction.id, "timeInForce": "GTC", "timestamp": Date.now() }).then(resp => { transaction.buySettings.apiID = resp.orderId; transaction.phase = TransactionPhase.BUY_IN_PROGRESS; + transaction.lockedForUpdate = false; }).catch(e => { this.getLogger().error("Unable to create buy order for " + transaction.id, {code: e.code, msg: e.message, url: e.requestUrl}); - const event = new TransactionUpdateEvent(transaction.id, transaction.buySettings.apiID, TransactionStatus.CANCELED) + const event = new TransactionUpdateEvent(transaction.id, transaction.buySettings.apiID, TransactionStatus.CANCELED); this.dispatchAccountEvent(event); }); } @@ -283,17 +290,18 @@ export default class BinanceApiClient extends APIClient { "symbol": transaction.symbol.getKey(), "side": "SELL", "type": "LIMIT", - "price": transaction.sellSettings.orderPrice, - "quantity" : transaction.sellSettings.orderQuantity, + "price": this.#correctPriceForAsset(transaction.sellSettings.orderPrice, transaction.symbol.getKey()), + "quantity" : this.#correctQuantityForAsset(transaction.sellSettings.orderQuantity, transaction.symbol.getKey()), "newClientOrderId": transaction.id, "timeInForce": "GTC", "timestamp": Date.now() }).then(resp => { transaction.sellSettings.apiID = resp.orderId; transaction.phase = TransactionPhase.SELL_IN_PROGRESS; + transaction.lockedForUpdate = false; }).catch(e => { this.getLogger().error("Unable to create sell order for " + transaction.id, {code: e.code, msg: e.message, url: e.requestUrl}); - const event = new TransactionUpdateEvent(transaction.id, transaction.sellSettings.apiID, TransactionStatus.CANCELED) + const event = new TransactionUpdateEvent(transaction.id, transaction.sellSettings.apiID, TransactionStatus.CANCELED); this.dispatchAccountEvent(event); }); } @@ -302,29 +310,41 @@ export default class BinanceApiClient extends APIClient { * @param {Transaction} transaction */ requestBuyCancel(transaction){ - this.#restClient.cancelOrder({ - "symbol": transaction.symbol.getKey(), - "orderId": transaction.buySettings.apiID, - "origClientOrderId": transaction.id - }).then(resp =>{ - this.requestTransactionUpdate(transaction, "buy"); - }).catch(e => { - this.getLogger().error("Unable to cancle buy order for " + transaction.id, {code: e.code, msg: e.message, url: e.requestUrl}); - }); + this.#requestCancel(transaction.buySettings.apiID, transaction.id, transaction.symbol.getKey()); } /** * @param {Transaction} transaction */ requestSellCancel(transaction){ - this.#restClient.cancelOrder({ - "symbol": transaction.symbol.getKey(), - "orderId": transaction.sellSettings.apiID, - "origClientOrderId": transaction.id - }).then(resp =>{ - this.requestTransactionUpdate(transaction, "sell"); + this.#requestCancel(transaction.sellSettings.apiID, transaction.id, transaction.symbol.getKey()); + } + + /** + * @param {string} orderId + * @param {string} clientId + * @param {string} symbol + */ + #requestCancel(orderId, clientId, symbol){ + /** + * @type {import("binance").CancelOrderParams} + */ + const orderParams = { + "symbol": symbol, + }; + + if(clientId != null && clientId != ""){ + orderParams.origClientOrderId = clientId; + } + + if(orderId != null && orderId != ""){ + orderParams.orderId = orderId; + } + + this.#restClient.cancelOrder(orderParams).then(resp =>{ + this.#requestOrderUpdate(orderId, clientId, symbol); }).catch(e => { - this.getLogger().error("Unable to cancle sell order for " + transaction.id, {code: e.code, msg: e.message, url: e.requestUrl}); + this.getLogger().error("Unable to cancle order for " + clientId, {code: e.code, msg: e.message, url: e.requestUrl}); }); } @@ -343,13 +363,28 @@ export default class BinanceApiClient extends APIClient { * @param {string} symbol */ #requestOrderUpdate(orderId, clientId, symbol){ - this.#restClient.getOrder({ + /** + * @type {import("binance").GetOrderParams} + */ + const orderParams = { "symbol": symbol, - "orderId": orderId, - "origClientOrderId": clientId, - }).then(resp => { + }; + + if(clientId != null && clientId != ""){ + orderParams.origClientOrderId = clientId; + } + + if(orderId != null && orderId != ""){ + orderParams.orderId = orderId; + } + + this.#restClient.getOrder(orderParams).then(resp => { const event = new TransactionUpdateEvent(resp.clientOrderId, resp.orderId, resp.status, resp.updateTime); - this.#requestTransactionFills(orderId, clientId, symbol, event); + if(resp.status == "CANCELED" && parseFloat(resp.executedQty) <= 0){ + this.dispatchAccountEvent(event); + } else { + this.#requestTransactionFills(orderId, clientId, symbol, event); + } }).catch(e => { this.getLogger().error("Unable to get order status for " + clientId + "(" + orderId + ")", {code: e.code, msg: e.message, url: e.requestUrl}); }); @@ -366,10 +401,69 @@ export default class BinanceApiClient extends APIClient { "symbol": symbol, "orderId": orderId, }).then(resp => { - resp.forEach(trade => event.fills.push(new TransactionFill(trade.price, trade.qty))); + resp.forEach(trade => event.fills.push(new TransactionFill(parseFloat(trade.price), parseFloat(trade.qty)))); this.dispatchAccountEvent(event); }).catch(e => { this.getLogger().error("Unable to get order fills for " + clientId + "(" + orderId + ")", {code: e.code, msg: e.message, url: e.requestUrl}); }); } + + #loadAssetInfo(){ + this.#restClient.getExchangeInfo().then(exchangeInfo => { + exchangeInfo.symbols.forEach(symbolInfo => { + this.#assetPermissions.set(symbolInfo.symbol, symbolInfo); + }); + }).catch(e => { + this.getLogger().error("Unable to get exchange info!"); + }); + } + + /** + * @param {number} price + * @param {string} asset + * @returns {number} + */ + #correctPriceForAsset(price, asset){ + if(!this.#assetPermissions.has(asset)){ + return price; + } + + const symbolInfo = this.#assetPermissions.get(asset); + const priceFilter = symbolInfo.filters.find(filter => filter.filterType == 'PRICE_FILTER'); + let newPrice = parseFloat(price); + + if(priceFilter != null){ + newPrice = this.#toPrecision(newPrice, priceFilter.tickSize); + } + + return newPrice; + } + + /** + * @param {number} quantity + * @param {string} asset + * @returns {number} + */ + #correctQuantityForAsset(quantity, asset){ + if(!this.#assetPermissions.has(asset)){ + return quantity; + } + + const symbolInfo = this.#assetPermissions.get(asset); + const lotFilter = symbolInfo.filters.find(filter => filter.filterType == 'LOT_SIZE'); + let newQuantity = parseFloat(quantity); + + if(lotFilter != null){ + newQuantity = this.#toPrecision(newQuantity, lotFilter.stepSize); + } + + return newQuantity; + } + + #toPrecision(value, precision){ + let precisionDigits = Math.log(Math.round(1/parseFloat(precision))) * Math.LOG10E + 1 | 0; + precisionDigits = precisionDigits > 0 && precision >= 1 ? precisionDigits - 1 : precisionDigits; + + return parseFloat(value).toFixed(precisionDigits); + } } \ No newline at end of file diff --git a/src/js/apiwrapper/event/TransactionUpdateEvent.js b/src/js/apiwrapper/event/TransactionUpdateEvent.js index c0604f3..a4b527a 100644 --- a/src/js/apiwrapper/event/TransactionUpdateEvent.js +++ b/src/js/apiwrapper/event/TransactionUpdateEvent.js @@ -39,7 +39,7 @@ export default class TransactionUpdateEvent extends APIEvent{ super(APIEventType.TRANSACTION_UPDATE); this.apiId = apiId; this.transactionId = transactionId; - this.statis = status; + this.status = status; this.timestamp = timestamp; } } \ No newline at end of file diff --git a/src/js/apiwrapper/transaction/Transaction.js b/src/js/apiwrapper/transaction/Transaction.js index 1d957ca..8ddee60 100644 --- a/src/js/apiwrapper/transaction/Transaction.js +++ b/src/js/apiwrapper/transaction/Transaction.js @@ -45,6 +45,11 @@ export default class Transaction extends Serializable { */ timestamp = 0; + /** + * @type {boolean} indicates, that the transaction was locked for sell/buy/update and will be unlocked, when the action is over + */ + lockedForUpdate = false; + /** * @param {string} initiator * @param {TradingPair} tradingPair @@ -70,6 +75,7 @@ export default class Transaction extends Serializable { obj.id = this.id; obj.result = this.result; obj.phase = this.phase; + obj.timestamp = this.timestamp; obj.symbol = SerializableHelper.serialize(this.symbol); obj.buySettings = SerializableHelper.serialize(this.buySettings); obj.sellSettings = SerializableHelper.serialize(this.sellSettings); @@ -89,6 +95,7 @@ export default class Transaction extends Serializable { obj.id = tmpObj.id; obj.result = tmpObj.result; obj.phase = tmpObj.phase; + obj.timestamp = tmpObj.timestamp; obj.symbol = SerializableHelper.deserialize(tmpObj.symbol); obj.buySettings = SerializableHelper.deserialize(tmpObj.buySettings); obj.sellSettings = SerializableHelper.deserialize(tmpObj.sellSettings); diff --git a/src/js/apiwrapper/transaction/TransactionPhase.js b/src/js/apiwrapper/transaction/TransactionPhase.js index 85ef0aa..5be785a 100644 --- a/src/js/apiwrapper/transaction/TransactionPhase.js +++ b/src/js/apiwrapper/transaction/TransactionPhase.js @@ -129,7 +129,7 @@ export default class TransactionPhase { return phase; } - if(this.isBuyPhase(currentPhase)){ + if(TransactionPhase.INITIAL == currentPhase || TransactionPhase.BUY_IN_PROGRESS == currentPhase){ if(TransactionStatus.isCancled(status) && !hasFills){ return this.CANCELED; } @@ -137,9 +137,7 @@ export default class TransactionPhase { if((TransactionStatus.FILLED == status) || (TransactionStatus.isCancled(status) && hasFills)){ return this.BUY_DONE; } - } - - if(this.isSellPhase(currentPhase)){ + } else if (TransactionPhase.BUY_DONE == currentPhase || TransactionPhase.SELL_IN_PROGRESS == currentPhase){ if(TransactionStatus.isCancled(status)){ return this.BUY_DONE; } @@ -147,8 +145,10 @@ export default class TransactionPhase { if(TransactionStatus.FILLED == status){ return this.FINISHED; } + } else if (TransactionPhase.SELL_DONE == currentPhase){ + return this.FINISHED; } - - return phase; + + return currentPhase; } } \ No newline at end of file diff --git a/src/js/index.js b/src/js/index.js index 9a15eb6..9ff529e 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -34,6 +34,24 @@ try{ console.log(e); } +/*const logger = new Logger("C:/Users/Wlad/Projekte/BinanceBot/binance_bot/data/log"); +const api = new BinanceApiClient(BinanceApiConfig.createTestNetConfig(), logger); +const listener = { + handleAssetUpdate: function(accountUpdateEvent){ + //; + }, + + handleAccountUpdate: function(accountUpdateEvent){ + console.log(accountUpdateEvent); + } +}; +api.registerAccountEventListener(listener); +api.registerAssetEventListener(listener); +const transaction = new Transaction("test", TradingPairs.GALAUSDT); +transaction.buySettings = new TransactionSettings(0.0343, 874.89064); +setTimeout(() => {api.requestBuy(transaction);}, 5000);*/ + + diff --git a/src/js/logging/Logger.js b/src/js/logging/Logger.js index 7ce3ed8..b9c9318 100644 --- a/src/js/logging/Logger.js +++ b/src/js/logging/Logger.js @@ -45,7 +45,7 @@ export default class Logger { return new winston.transports.DailyRotateFile({ filename: 'application-%DATE%.log', dirname: logPath, - datePattern: 'YYYY-MM-DD-HH', + datePattern: 'YYYY-MM-DD', zippedArchive: true, maxSize: '10m', maxFiles: '7d' @@ -60,7 +60,7 @@ export default class Logger { return new winston.transports.DailyRotateFile({ filename: 'error-%DATE%.log', dirname: logPath, - datePattern: 'YYYY-MM-DD-HH', + datePattern: 'YYYY-MM-DD', zippedArchive: true, maxSize: '20m', maxFiles: '7d', diff --git a/src/js/tradingbot/TradingBot.js b/src/js/tradingbot/TradingBot.js index bfe1e13..ca0920f 100644 --- a/src/js/tradingbot/TradingBot.js +++ b/src/js/tradingbot/TradingBot.js @@ -126,8 +126,11 @@ export default class TradingBot { worker.init(this.#logger, this.#config.getDataDirectory(), this); }); this.#transactions.forEach(transaction => { - const type = transaction.phase === (TransactionPhase.INITIAL || transaction.phase === TransactionPhase.BUY_IN_PROGRESS) ? "buy" : "sell"; - this.#api.requestTransactionUpdate(transaction, type); + if(transaction.buySettings != null && (transaction.phase === TransactionPhase.INITIAL || transaction.phase === TransactionPhase.BUY_IN_PROGRESS)){ + this.#api.requestTransactionUpdate(transaction, 'buy'); + } else if(transaction.sellSettings != null){ + this.#api.requestTransactionUpdate(transaction, 'sell'); + } }); this.#startTime = Date.now(); @@ -282,7 +285,10 @@ export default class TradingBot { let index = -1; this.#transactions.forEach((v, i) => { - if (v.id == event.transactionId || v.buySettings.apiID == event.apiId || v.sellSettings.apiID == event.apiId) { + if (v.id == event.transactionId + || (v.buySettings != null && v.buySettings.apiID == event.apiId) + || (v.sellSettings != null && v.sellSettings.apiID == event.apiId) + ) { transaction = v; index = i; } @@ -299,14 +305,17 @@ export default class TradingBot { this.#calculateTradeValues(transaction.buySettings, event.fills); } else if (TransactionPhase.isFinalPhase(nextTransactionPhase)) { this.#calculateTradeValues(transaction.sellSettings, event.fills); - transaction.result = (sellSettings.quantity * sellSettings.price) - (buySettings.quantity * buySettings.price) + transaction.result = (transaction.sellSettings.quantity * transaction.sellSettings.price) - (transaction.buySettings.quantity * transaction.buySettings.price) this.#transactionHistory.push(transaction); this.#transactions.splice(index, 1); } transaction.phase = nextTransactionPhase; + transaction.lockedForUpdate = false; this.#saveTransactions(); } + + transaction.lockedForUpdate = false; } /** @@ -323,7 +332,7 @@ export default class TradingBot { }); taSettings.quantity = quantity; - taSettings.price = priceSum / quantity; + taSettings.price = quantity != 0 ? priceSum / quantity : 0; } /** @@ -349,8 +358,8 @@ export default class TradingBot { } const transaction = new Transaction(worker.getId(), worker.getTradingPair()); - transaction.buySettings.orderPrice = price; - transaction.buySettings.orderQuantity = Math.round(this.#config.getPerTradeLimit() / price); + transaction.buySettings = new TransactionSettings(price, this.#config.getPerTradeLimit() / price); + transaction.lockedForUpdate = true; this.#transactions.push(transaction); this.#saveTransactions(); @@ -360,8 +369,15 @@ export default class TradingBot { /** * @param {AbstractWorker} worker * @param {Transaction} transaction + * @param {number} price */ - requestSell(worker, transaction){ + requestSell(worker, transaction, price){ + if(transaction.lockedForUpdate){ + this.#logger.warn("Attempt to sell a locked transaction!", transaction.id); + return; + } + transaction.lockedForUpdate = true; + transaction.sellSettings = new TransactionSettings(price, parseFloat(transaction.buySettings.quantity)); this.#api.requestSell(transaction); } @@ -370,12 +386,26 @@ export default class TradingBot { * @param {Transaction} transaction */ requestCancle(worker, transaction){ - // not implemented as this class is only an interface! + if(transaction.lockedForUpdate){ + this.#logger.warn("Attempt to cancel a locked transaction!", transaction.id); + return; + } + if(transaction.buySettings != null && transaction.phase == TransactionPhase.BUY_IN_PROGRESS){ + transaction.lockedForUpdate = true; + this.#api.requestBuyCancel(transaction); + } + + if(transaction.sellSettings != null && transaction.phase == TransactionPhase.SELL_IN_PROGRESS){ + transaction.lockedForUpdate = true; + this.#api.requestSellCancel(transaction); + } } #loadTransactions() { try { this.#transactions = SerializableHelper.load(this.#config.getDataDirectory(), 'transactions.sjson', Transaction); + this.#transactions = this.#transactions.filter(t => t.phase != TransactionPhase.INITIAL); + this.#transactions.forEach(t => t.lockedForUpdate = false); } catch (e) { this.#logger.warn("Bot was unable to load any transactions. Assuming no active transactions existing!", e); } diff --git a/src/js/tradingbot/strategy/AvgMinMaxStrategy.js b/src/js/tradingbot/strategy/AvgMinMaxStrategy.js index 2c1461c..0b67e84 100644 --- a/src/js/tradingbot/strategy/AvgMinMaxStrategy.js +++ b/src/js/tradingbot/strategy/AvgMinMaxStrategy.js @@ -1,4 +1,5 @@ import Transaction from "../../apiwrapper/transaction/Transaction.js"; +import TransactionPhase from "../../apiwrapper/transaction/TransactionPhase.js"; import AbstractStrategy from "./AbstractStrategy.js"; export default class AvgMinMaxStrategy extends AbstractStrategy { @@ -52,6 +53,12 @@ export default class AvgMinMaxStrategy extends AbstractStrategy { * @returns {boolean} */ shouldCancle(transaction) { + const price = this.getAssetData().getOrderBook().getBestBid().price; + + if(transaction.buySettings != null && transaction.phase == TransactionPhase.BUY_IN_PROGRESS){ + return transaction.buySettings.orderPrice * 1.002 < price; + } + return false; } diff --git a/src/js/tradingbot/worker/DefaultWorker.js b/src/js/tradingbot/worker/DefaultWorker.js index aba35e1..55c1176 100644 --- a/src/js/tradingbot/worker/DefaultWorker.js +++ b/src/js/tradingbot/worker/DefaultWorker.js @@ -28,7 +28,7 @@ export default class DefaultWorker extends AbstractWorker{ */ onUpdate(assetData, transactions){ this.getStrategy().setAssetData(assetData); - const price = this.getStrategy().getAssetData().getOrderBook().getBestBid().price; + const price = parseFloat(this.getStrategy().getAssetData().getOrderBook().getBestBid().price); if(this.getStrategy().shouldBuy()){ this.getApiProvider().requestBuy(this, Math.round(50 / price), price); @@ -36,8 +36,8 @@ export default class DefaultWorker extends AbstractWorker{ for(let transaction of transactions){ if(transaction.phase == TransactionPhase.BUY_DONE && this.getStrategy().shouldSell(transaction)){ - this.getApiProvider().requestSell(this, transaction); - } else if(this.getStrategy().shouldCancle()){ + this.getApiProvider().requestSell(this, transaction, price); + } else if(this.getStrategy().shouldCancle(transaction)){ this.getApiProvider().requestCancle(this, transaction); } } diff --git a/src/js/tradingbot/worker/WorkerTradingApiProvider.js b/src/js/tradingbot/worker/WorkerTradingApiProvider.js index 849d525..30a73a6 100644 --- a/src/js/tradingbot/worker/WorkerTradingApiProvider.js +++ b/src/js/tradingbot/worker/WorkerTradingApiProvider.js @@ -11,11 +11,12 @@ export default class WorkerTradingApiProvider { // not implemented as this class is only an interface! } - /** + /** * @param {AbstractWorker} worker * @param {Transaction} transaction + * @param {number} price */ - requestSell(worker, transaction){ + requestSell(worker, transaction, price){ // not implemented as this class is only an interface! } diff --git a/src/js/webview/converter/converters/TransactionConverter.js b/src/js/webview/converter/converters/TransactionConverter.js index fe3e0e0..ae010b0 100644 --- a/src/js/webview/converter/converters/TransactionConverter.js +++ b/src/js/webview/converter/converters/TransactionConverter.js @@ -24,8 +24,8 @@ export default class TransactionConverter extends AbstractConverter { phase: obj.phase, timestamp:obj.timestamp, result: obj.result, - buySettings: tsConverter.toRestObject(obj.buySettings), - sellSettings: tsConverter.toRestObject(obj.sellSettings), + buySettings: obj.buySettings == null ? null : tsConverter.toRestObject(obj.buySettings), + sellSettings: obj.sellSettings == null ? null : tsConverter.toRestObject(obj.sellSettings), symbol: obj.symbol.getKey() }; } @@ -43,8 +43,8 @@ export default class TransactionConverter extends AbstractConverter { t.phase = obj.phase; t.timestamp = obj.timestamp; t.result = obj.result; - t.buySettings = tsConverter.fromRestObject(obj.buySettings); - t.sellSettings = tsConverter.fromRestObject(obj.sellSettings); + t.buySettings = obj.buySettings == null ? null : tsConverter.fromRestObject(obj.buySettings); + t.sellSettings = obj.sellSettings == null ? null : tsConverter.fromRestObject(obj.sellSettings); t.symbol = TradingPairs.fromString(obj.symbol); return t;