diff --git a/src/js/apis/binance/BinanceApiClient.js b/src/js/apis/binance/BinanceApiClient.js index 7a386d3..3821988 100644 --- a/src/js/apis/binance/BinanceApiClient.js +++ b/src/js/apis/binance/BinanceApiClient.js @@ -16,6 +16,7 @@ import Transaction from "../../apiwrapper/transaction/Transaction.js"; import TransactionUpdateEvent from "../../apiwrapper/event/TransactionUpdateEvent.js"; import TransactionFill from "../../apiwrapper/transaction/TransactionFill.js"; import TransactionStatus from "../../apiwrapper/transaction/TransactionStatus.js"; +import TransactionPhase from "../../apiwrapper/transaction/TransactionPhase.js"; export default class BinanceApiClient extends APIClient { /** @@ -266,6 +267,7 @@ export default class BinanceApiClient extends APIClient { "timestamp": Date.now() }).then(resp => { transaction.buySettings.apiID = resp.orderId; + transaction.phase = TransactionPhase.BUY_IN_PROGRESS; }).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) @@ -288,6 +290,7 @@ export default class BinanceApiClient extends APIClient { "timestamp": Date.now() }).then(resp => { transaction.sellSettings.apiID = resp.orderId; + transaction.phase = TransactionPhase.SELL_IN_PROGRESS; }).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) diff --git a/src/js/apiwrapper/transaction/Transaction.js b/src/js/apiwrapper/transaction/Transaction.js index 5d78eb2..1d957ca 100644 --- a/src/js/apiwrapper/transaction/Transaction.js +++ b/src/js/apiwrapper/transaction/Transaction.js @@ -45,6 +45,10 @@ export default class Transaction extends Serializable { */ timestamp = 0; + /** + * @param {string} initiator + * @param {TradingPair} tradingPair + */ constructor(initiator, tradingPair){ super(); this.initiator = initiator; diff --git a/src/js/tradingbot/TradingBot.js b/src/js/tradingbot/TradingBot.js index ba3e8a7..ef67dfc 100644 --- a/src/js/tradingbot/TradingBot.js +++ b/src/js/tradingbot/TradingBot.js @@ -123,7 +123,7 @@ export default class TradingBot { this.#loadWorker(); this.#worker.forEach(worker => { this.#registerTradingPair(worker.getTradingPair()); - worker.init(this.#logger, this.#config.getDataDirectory()); + 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"; @@ -235,6 +235,7 @@ export default class TradingBot { handleAccountUpdate(accountUpdateEvent) { switch (accountUpdateEvent.eventType) { case APIEventType.BALANCE_CHANGE: + this.#logger.info("Balance update: ", accountUpdateEvent); // TODO update changed balances break; case APIEventType.TRANSACTION_UPDATE: @@ -337,6 +338,41 @@ export default class TradingBot { } + /** + * @param {AbstractWorker} worker + * @param {number} quantity + * @param {number} price + */ + requestBuy(worker, quantity, price){ + if(this.#config.getUsdTradeLimit() / this.#config.getPerTradeLimit() >= this.#transactions.length){ + return; + } + + const transaction = new Transaction(worker.getId(), worker.getTradingPair()); + transaction.buySettings.oderPrice = price; + transaction.buySettings.orderQuantity = Math.round(this.#config.getPerTradeLimit() / price); + this.#transactions.push(transaction); + this.#saveTransactions(); + + this.#api.requestBuy(transaction); + } + + /** + * @param {AbstractWorker} worker + * @param {Transaction} transaction + */ + requestSell(worker, transaction){ + this.#api.requestSell(transaction); + } + + /** + * @param {AbstractWorker} worker + * @param {Transaction} transaction + */ + requestCancle(worker, transaction){ + // not implemented as this class is only an interface! + } + #loadTransactions() { try { this.#transactions = SerializableHelper.load(this.#config.getDataDirectory(), 'transactions.sjson', Transaction); @@ -351,7 +387,7 @@ export default class TradingBot { } } - #saveTransactions() { + async #saveTransactions() { SerializableHelper.save(this.#config.getDataDirectory(), 'transactions.sjson', this.#transactions); SerializableHelper.save(this.#config.getDataDirectory(), 'transactionHistory.sjson', this.#transactionHistory); } @@ -366,7 +402,7 @@ export default class TradingBot { } } - #saveWorker() { + async #saveWorker() { const workerArr = []; Array.from(this.#worker.values()).forEach(w => workerArr.push(w)); SerializableHelper.save(this.#config.getDataDirectory(), 'worker.sjson', workerArr); diff --git a/src/js/tradingbot/worker/AbstractWorker.js b/src/js/tradingbot/worker/AbstractWorker.js index 0219674..3766d07 100644 --- a/src/js/tradingbot/worker/AbstractWorker.js +++ b/src/js/tradingbot/worker/AbstractWorker.js @@ -5,6 +5,7 @@ import Serializable from "../../util/Serializable.js"; import SerializableHelper from "../../util/SerializableHelper.js"; import AssetData from "../data/asset/AssetData.js"; import AbstractStrategy from "../strategy/AbstractStrategy.js"; +import WorkerTradingApiProvider from "./WorkerTradingApiProvider.js"; export default class AbstractWorker extends Serializable { @@ -28,6 +29,11 @@ export default class AbstractWorker extends Serializable { */ #strategy = null; + /** + * @type {WorkerTradingApiProvider} + */ + #apiProvider = null + /** * @type {Logger} */ @@ -49,9 +55,11 @@ export default class AbstractWorker extends Serializable { /** * @param {Logger} logger * @param {string} dataPath + * @param {WorkerTradingApiProvider} apiProvider */ - init(logger, dataPath){ + init(logger, dataPath, apiProvider){ this.#logger = logger; + this.#apiProvider = apiProvider; if(this.#strategy != null){ this.#strategy.init(logger); } @@ -114,6 +122,13 @@ export default class AbstractWorker extends Serializable { return this.#logger; } + /** + * @returns {WorkerTradingApiProvider} + */ + getApiProvider(){ + return this.#apiProvider; + } + /** * @param {string} dataPath path to data directory */ diff --git a/src/js/tradingbot/worker/DefaultWorker.js b/src/js/tradingbot/worker/DefaultWorker.js index db45817..d66b329 100644 --- a/src/js/tradingbot/worker/DefaultWorker.js +++ b/src/js/tradingbot/worker/DefaultWorker.js @@ -28,16 +28,17 @@ export default class DefaultWorker extends AbstractWorker{ */ onUpdate(assetData, transactions){ this.getStrategy().updateAssetData(assetData); + const price = this.getStrategy().getAssetData().getOrderBook().getBestBid().price; if(this.getStrategy().shouldBuy()){ - // TODO create transaction! + this.getApiProvider().requestBuy(this, Math.round(50 / price), price); } for(let transaction of transactions){ if(transaction.phase == TransactionPhase.BUY_DONE && this.getStrategy().shouldSell(transaction)){ - // TODO request sell! + this.getApiProvider().requestSell(this, transaction); } else if(this.getStrategy().shouldCancle()){ - // TODO request cancle + this.getApiProvider().requestCancle(this, transaction); } } } diff --git a/src/js/tradingbot/worker/WorkerTradingApiProvider.js b/src/js/tradingbot/worker/WorkerTradingApiProvider.js new file mode 100644 index 0000000..849d525 --- /dev/null +++ b/src/js/tradingbot/worker/WorkerTradingApiProvider.js @@ -0,0 +1,29 @@ +import Transaction from "../../apiwrapper/transaction/Transaction.js"; +import AbstractWorker from "./AbstractWorker.js"; + +export default class WorkerTradingApiProvider { + /** + * @param {AbstractWorker} worker + * @param {number} quantity + * @param {number} price + */ + requestBuy(worker, quantity, price){ + // not implemented as this class is only an interface! + } + + /** + * @param {AbstractWorker} worker + * @param {Transaction} transaction + */ + requestSell(worker, transaction){ + // not implemented as this class is only an interface! + } + + /** + * @param {AbstractWorker} worker + * @param {Transaction} transaction + */ + requestCancle(worker, transaction){ + // not implemented as this class is only an interface! + } +} \ No newline at end of file