Compare commits

..

No commits in common. "2b3a0e3102555bb520a1ddbbc99d9c8820056dc6" and "ccc1945ee40743d79286b522ee1346720b7b9dfd" have entirely different histories.

12 changed files with 92 additions and 250 deletions

View File

@ -29,11 +29,6 @@ export default class BinanceApiClient extends APIClient {
*/ */
#restClient = null; #restClient = null;
/**
* @type {Map<string, import("binance").SymbolExchangeInfo>}
*/
#assetPermissions = new Map();
/** /**
* @param {BinanceApiConfig} config * @param {BinanceApiConfig} config
* @param {Logger} logger * @param {Logger} logger
@ -42,7 +37,6 @@ export default class BinanceApiClient extends APIClient {
super(config, logger); super(config, logger);
this.#wsClient = new WebsocketClient(config.getWSClientConfig()); this.#wsClient = new WebsocketClient(config.getWSClientConfig());
this.#restClient = new MainClient(config.getRestClientConfig()); this.#restClient = new MainClient(config.getRestClientConfig());
this.#loadAssetInfo();
this.#wsClient.on('formattedMessage', this.#wsMessageHandler.bind(this)); this.#wsClient.on('formattedMessage', this.#wsMessageHandler.bind(this));
this.#wsClient.on('open', this.#connectionOpened.bind(this)); this.#wsClient.on('open', this.#connectionOpened.bind(this));
this.#wsClient.on('reconnecting', this.#reconnected.bind(this)); this.#wsClient.on('reconnecting', this.#reconnected.bind(this));
@ -104,8 +98,8 @@ export default class BinanceApiClient extends APIClient {
} }
const event = new TradeEvent(tradingPair); const event = new TradeEvent(tradingPair);
event.price = parseFloat(data.price); event.price = data.price;
event.quantity = parseFloat(data.quantity); event.quantity = data.quantity;
this.dispatchAssetEvent(event); this.dispatchAssetEvent(event);
} }
@ -121,12 +115,12 @@ export default class BinanceApiClient extends APIClient {
const event = new OrderBookUpdateEvent(tradingPair); const event = new OrderBookUpdateEvent(tradingPair);
event.updateId = data.lastUpdateId; event.updateId = data.lastUpdateId;
if(data.bids && data.asks){ if(data.bids && data.asks){
data.bids.forEach(b => event.bids.push(new OrderBookEntry(parseFloat(b[0]), parseFloat(b[1])))); data.bids.forEach(b => event.bids.push(new OrderBookEntry(b[0], b[1])));
data.asks.forEach(a => event.asks.push(new OrderBookEntry(parseFloat(a[0]), parseFloat(a[1])))); data.asks.forEach(a => event.asks.push(new OrderBookEntry(a[0], a[1])));
event.isReset = true; event.isReset = true;
} else { } else {
data.bidDepthDelta.forEach(b => event.bids.push(new OrderBookEntry(parseFloat(b.price), parseFloat(b.quantity)))); data.bidDepthDelta.forEach(b => event.bids.push(new OrderBookEntry(b.price, b.quantity)));
data.askDepthDelta .forEach(a => event.asks.push(new OrderBookEntry(parseFloat(a.price), parseFloat(a.quantity)))); data.askDepthDelta .forEach(a => event.asks.push(new OrderBookEntry(a.price, a.quantity)));
} }
@ -142,16 +136,16 @@ export default class BinanceApiClient extends APIClient {
} }
const event = new Ticker24hEvent(tradingPair); const event = new Ticker24hEvent(tradingPair);
event.priceChange = parseFloat(data.priceChange); event.priceChange = data.priceChange;
event.priceChangePercent = parseFloat(data.priceChangePercent); event.priceChangePercent = data.priceChangePercent;
event.weightedAveragePrice = parseFloat(data.weightedAveragePrice); event.weightedAveragePrice = data.weightedAveragePrice;
event.bestBidPrice = parseFloat(data.bestBid); event.bestBidPrice = data.bestBid;
event.bestBidQuantity = parseFloat(data.bestBidQuantity); event.bestBidQuantity = data.bestBidQuantity;
event.bestAskPrice = parseFloat(data.bestAskPrice); event.bestAskPrice = data.bestAskPrice;
event.bestAskQuantity = parseFloat(data.bestAskQuantity); event.bestAskQuantity = data.bestAskQuantity;
event.high = parseFloat(data.high); event.high = data.high;
event.low = parseFloat(data.low); event.low = data.low;
event.trades = parseFloat(data.trades); event.trades = data.trades;
this.dispatchAssetEvent(event); this.dispatchAssetEvent(event);
} }
@ -164,10 +158,10 @@ export default class BinanceApiClient extends APIClient {
return; return;
} }
const event = new KLineEvent(tradingPair, parseFloat(data.kline.high), parseFloat(data.kline.low), parseFloat(data.kline.open), parseFloat(data.kline.close)); const event = new KLineEvent(tradingPair, data.kline.high, data.kline.low, data.kline.open, data.kline.close);
event.isClosed = data.kline.final; event.isClosed = data.kline.final;
event.startTime = parseFloat(data.kline.startTime); event.startTime = data.kline.startTime;
event.endTime = parseFloat(data.kline.endTime); event.endTime = data.kline.endTime;
this.dispatchAssetEvent(event); this.dispatchAssetEvent(event);
} }
@ -178,7 +172,7 @@ export default class BinanceApiClient extends APIClient {
async #fireAccountBalanceChangeEvent(data) { async #fireAccountBalanceChangeEvent(data) {
const event = new BalanceChangeEvent(); const event = new BalanceChangeEvent();
data.balances.forEach(balanceUpdate => { data.balances.forEach(balanceUpdate => {
event.setChangedBalance(balanceUpdate.asset, parseFloat(balanceUpdate.availableBalance)); event.setChangedBalance(balanceUpdate.asset, balanceUpdate.availableBalance);
}); });
this.dispatchAccountEvent(event); this.dispatchAccountEvent(event);
@ -240,7 +234,7 @@ export default class BinanceApiClient extends APIClient {
const event = new BalanceChangeEvent(); const event = new BalanceChangeEvent();
resp.forEach(coinInfo => { resp.forEach(coinInfo => {
if (coinInfo.free > 0) { if (coinInfo.free > 0) {
event.setChangedBalance(coinInfo.coin, parseFloat(coinInfo.free)); event.setChangedBalance(coinInfo.coin, coinInfo.free);
} }
}); });
@ -266,18 +260,17 @@ export default class BinanceApiClient extends APIClient {
"symbol": transaction.symbol.getKey(), "symbol": transaction.symbol.getKey(),
"side": "BUY", "side": "BUY",
"type": "LIMIT", "type": "LIMIT",
"price": this.#correctPriceForAsset(transaction.buySettings.orderPrice, transaction.symbol.getKey()), "price": transaction.buySettings.orderPrice,
"quantity": this.#correctQuantityForAsset(transaction.buySettings.orderQuantity, transaction.symbol.getKey()), "quantity" : transaction.buySettings.orderQuantity,
"newClientOrderId": transaction.id, "newClientOrderId": transaction.id,
"timeInForce": "GTC", "timeInForce": "GTC",
"timestamp": Date.now() "timestamp": Date.now()
}).then(resp => { }).then(resp => {
transaction.buySettings.apiID = resp.orderId; transaction.buySettings.apiID = resp.orderId;
transaction.phase = TransactionPhase.BUY_IN_PROGRESS; transaction.phase = TransactionPhase.BUY_IN_PROGRESS;
transaction.lockedForUpdate = false;
}).catch(e => { }).catch(e => {
this.getLogger().error("Unable to create buy order for " + transaction.id, {code: e.code, msg: e.message, url: e.requestUrl}); 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); this.dispatchAccountEvent(event);
}); });
} }
@ -290,18 +283,17 @@ export default class BinanceApiClient extends APIClient {
"symbol": transaction.symbol.getKey(), "symbol": transaction.symbol.getKey(),
"side": "SELL", "side": "SELL",
"type": "LIMIT", "type": "LIMIT",
"price": this.#correctPriceForAsset(transaction.sellSettings.orderPrice, transaction.symbol.getKey()), "price": transaction.sellSettings.orderPrice,
"quantity" : this.#correctQuantityForAsset(transaction.sellSettings.orderQuantity, transaction.symbol.getKey()), "quantity" : transaction.sellSettings.orderQuantity,
"newClientOrderId": transaction.id, "newClientOrderId": transaction.id,
"timeInForce": "GTC", "timeInForce": "GTC",
"timestamp": Date.now() "timestamp": Date.now()
}).then(resp => { }).then(resp => {
transaction.sellSettings.apiID = resp.orderId; transaction.sellSettings.apiID = resp.orderId;
transaction.phase = TransactionPhase.SELL_IN_PROGRESS; transaction.phase = TransactionPhase.SELL_IN_PROGRESS;
transaction.lockedForUpdate = false;
}).catch(e => { }).catch(e => {
this.getLogger().error("Unable to create sell order for " + transaction.id, {code: e.code, msg: e.message, url: e.requestUrl}); 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); this.dispatchAccountEvent(event);
}); });
} }
@ -310,41 +302,29 @@ export default class BinanceApiClient extends APIClient {
* @param {Transaction} transaction * @param {Transaction} transaction
*/ */
requestBuyCancel(transaction){ requestBuyCancel(transaction){
this.#requestCancel(transaction.buySettings.apiID, transaction.id, transaction.symbol.getKey()); 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});
});
} }
/** /**
* @param {Transaction} transaction * @param {Transaction} transaction
*/ */
requestSellCancel(transaction){ requestSellCancel(transaction){
this.#requestCancel(transaction.sellSettings.apiID, transaction.id, transaction.symbol.getKey()); this.#restClient.cancelOrder({
} "symbol": transaction.symbol.getKey(),
"orderId": transaction.sellSettings.apiID,
/** "origClientOrderId": transaction.id
* @param {string} orderId }).then(resp =>{
* @param {string} clientId this.requestTransactionUpdate(transaction, "sell");
* @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 => { }).catch(e => {
this.getLogger().error("Unable to cancle order for " + clientId, {code: e.code, msg: e.message, url: e.requestUrl}); this.getLogger().error("Unable to cancle sell order for " + transaction.id, {code: e.code, msg: e.message, url: e.requestUrl});
}); });
} }
@ -363,28 +343,13 @@ export default class BinanceApiClient extends APIClient {
* @param {string} symbol * @param {string} symbol
*/ */
#requestOrderUpdate(orderId, clientId, symbol){ #requestOrderUpdate(orderId, clientId, symbol){
/** this.#restClient.getOrder({
* @type {import("binance").GetOrderParams}
*/
const orderParams = {
"symbol": symbol, "symbol": symbol,
}; "orderId": orderId,
"origClientOrderId": clientId,
if(clientId != null && clientId != ""){ }).then(resp => {
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); const event = new TransactionUpdateEvent(resp.clientOrderId, resp.orderId, resp.status, resp.updateTime);
if(resp.status == "CANCELED" && parseFloat(resp.executedQty) <= 0){ this.#requestTransactionFills(orderId, clientId, symbol, event);
this.dispatchAccountEvent(event);
} else {
this.#requestTransactionFills(orderId, clientId, symbol, event);
}
}).catch(e => { }).catch(e => {
this.getLogger().error("Unable to get order status for " + clientId + "(" + orderId + ")", {code: e.code, msg: e.message, url: e.requestUrl}); this.getLogger().error("Unable to get order status for " + clientId + "(" + orderId + ")", {code: e.code, msg: e.message, url: e.requestUrl});
}); });
@ -401,69 +366,10 @@ export default class BinanceApiClient extends APIClient {
"symbol": symbol, "symbol": symbol,
"orderId": orderId, "orderId": orderId,
}).then(resp => { }).then(resp => {
resp.forEach(trade => event.fills.push(new TransactionFill(parseFloat(trade.price), parseFloat(trade.qty)))); resp.forEach(trade => event.fills.push(new TransactionFill(trade.price, trade.qty)));
this.dispatchAccountEvent(event); this.dispatchAccountEvent(event);
}).catch(e => { }).catch(e => {
this.getLogger().error("Unable to get order fills for " + clientId + "(" + orderId + ")", {code: e.code, msg: e.message, url: e.requestUrl}); 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);
}
} }

View File

@ -39,7 +39,7 @@ export default class TransactionUpdateEvent extends APIEvent{
super(APIEventType.TRANSACTION_UPDATE); super(APIEventType.TRANSACTION_UPDATE);
this.apiId = apiId; this.apiId = apiId;
this.transactionId = transactionId; this.transactionId = transactionId;
this.status = status; this.statis = status;
this.timestamp = timestamp; this.timestamp = timestamp;
} }
} }

View File

@ -45,11 +45,6 @@ export default class Transaction extends Serializable {
*/ */
timestamp = 0; 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 {string} initiator
* @param {TradingPair} tradingPair * @param {TradingPair} tradingPair
@ -75,7 +70,6 @@ export default class Transaction extends Serializable {
obj.id = this.id; obj.id = this.id;
obj.result = this.result; obj.result = this.result;
obj.phase = this.phase; obj.phase = this.phase;
obj.timestamp = this.timestamp;
obj.symbol = SerializableHelper.serialize(this.symbol); obj.symbol = SerializableHelper.serialize(this.symbol);
obj.buySettings = SerializableHelper.serialize(this.buySettings); obj.buySettings = SerializableHelper.serialize(this.buySettings);
obj.sellSettings = SerializableHelper.serialize(this.sellSettings); obj.sellSettings = SerializableHelper.serialize(this.sellSettings);
@ -95,7 +89,6 @@ export default class Transaction extends Serializable {
obj.id = tmpObj.id; obj.id = tmpObj.id;
obj.result = tmpObj.result; obj.result = tmpObj.result;
obj.phase = tmpObj.phase; obj.phase = tmpObj.phase;
obj.timestamp = tmpObj.timestamp;
obj.symbol = SerializableHelper.deserialize(tmpObj.symbol); obj.symbol = SerializableHelper.deserialize(tmpObj.symbol);
obj.buySettings = SerializableHelper.deserialize(tmpObj.buySettings); obj.buySettings = SerializableHelper.deserialize(tmpObj.buySettings);
obj.sellSettings = SerializableHelper.deserialize(tmpObj.sellSettings); obj.sellSettings = SerializableHelper.deserialize(tmpObj.sellSettings);

View File

@ -129,7 +129,7 @@ export default class TransactionPhase {
return phase; return phase;
} }
if(TransactionPhase.INITIAL == currentPhase || TransactionPhase.BUY_IN_PROGRESS == currentPhase){ if(this.isBuyPhase(currentPhase)){
if(TransactionStatus.isCancled(status) && !hasFills){ if(TransactionStatus.isCancled(status) && !hasFills){
return this.CANCELED; return this.CANCELED;
} }
@ -137,7 +137,9 @@ export default class TransactionPhase {
if((TransactionStatus.FILLED == status) || (TransactionStatus.isCancled(status) && hasFills)){ if((TransactionStatus.FILLED == status) || (TransactionStatus.isCancled(status) && hasFills)){
return this.BUY_DONE; return this.BUY_DONE;
} }
} else if (TransactionPhase.BUY_DONE == currentPhase || TransactionPhase.SELL_IN_PROGRESS == currentPhase){ }
if(this.isSellPhase(currentPhase)){
if(TransactionStatus.isCancled(status)){ if(TransactionStatus.isCancled(status)){
return this.BUY_DONE; return this.BUY_DONE;
} }
@ -145,10 +147,8 @@ export default class TransactionPhase {
if(TransactionStatus.FILLED == status){ if(TransactionStatus.FILLED == status){
return this.FINISHED; return this.FINISHED;
} }
} else if (TransactionPhase.SELL_DONE == currentPhase){
return this.FINISHED;
} }
return currentPhase; return phase;
} }
} }

View File

@ -34,24 +34,6 @@ try{
console.log(e); 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);*/

View File

@ -45,7 +45,7 @@ export default class Logger {
return new winston.transports.DailyRotateFile({ return new winston.transports.DailyRotateFile({
filename: 'application-%DATE%.log', filename: 'application-%DATE%.log',
dirname: logPath, dirname: logPath,
datePattern: 'YYYY-MM-DD', datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true, zippedArchive: true,
maxSize: '10m', maxSize: '10m',
maxFiles: '7d' maxFiles: '7d'
@ -60,7 +60,7 @@ export default class Logger {
return new winston.transports.DailyRotateFile({ return new winston.transports.DailyRotateFile({
filename: 'error-%DATE%.log', filename: 'error-%DATE%.log',
dirname: logPath, dirname: logPath,
datePattern: 'YYYY-MM-DD', datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true, zippedArchive: true,
maxSize: '20m', maxSize: '20m',
maxFiles: '7d', maxFiles: '7d',

View File

@ -126,11 +126,8 @@ export default class TradingBot {
worker.init(this.#logger, this.#config.getDataDirectory(), this); worker.init(this.#logger, this.#config.getDataDirectory(), this);
}); });
this.#transactions.forEach(transaction => { this.#transactions.forEach(transaction => {
if(transaction.buySettings != null && (transaction.phase === TransactionPhase.INITIAL || transaction.phase === TransactionPhase.BUY_IN_PROGRESS)){ const type = transaction.phase === (TransactionPhase.INITIAL || transaction.phase === TransactionPhase.BUY_IN_PROGRESS) ? "buy" : "sell";
this.#api.requestTransactionUpdate(transaction, 'buy'); this.#api.requestTransactionUpdate(transaction, type);
} else if(transaction.sellSettings != null){
this.#api.requestTransactionUpdate(transaction, 'sell');
}
}); });
this.#startTime = Date.now(); this.#startTime = Date.now();
@ -238,7 +235,8 @@ export default class TradingBot {
handleAccountUpdate(accountUpdateEvent) { handleAccountUpdate(accountUpdateEvent) {
switch (accountUpdateEvent.eventType) { switch (accountUpdateEvent.eventType) {
case APIEventType.BALANCE_CHANGE: case APIEventType.BALANCE_CHANGE:
this.#accountInfo.updateBalances(accountUpdateEvent); this.#logger.info("Balance update: ", accountUpdateEvent);
// TODO update changed balances
break; break;
case APIEventType.TRANSACTION_UPDATE: case APIEventType.TRANSACTION_UPDATE:
this.#updateTransaction(accountUpdateEvent); this.#updateTransaction(accountUpdateEvent);
@ -284,10 +282,7 @@ export default class TradingBot {
let index = -1; let index = -1;
this.#transactions.forEach((v, i) => { this.#transactions.forEach((v, i) => {
if (v.id == event.transactionId if (v.id == event.transactionId || v.buySettings.apiID == event.apiId || v.sellSettings.apiID == event.apiId) {
|| (v.buySettings != null && v.buySettings.apiID == event.apiId)
|| (v.sellSettings != null && v.sellSettings.apiID == event.apiId)
) {
transaction = v; transaction = v;
index = i; index = i;
} }
@ -304,17 +299,14 @@ export default class TradingBot {
this.#calculateTradeValues(transaction.buySettings, event.fills); this.#calculateTradeValues(transaction.buySettings, event.fills);
} else if (TransactionPhase.isFinalPhase(nextTransactionPhase)) { } else if (TransactionPhase.isFinalPhase(nextTransactionPhase)) {
this.#calculateTradeValues(transaction.sellSettings, event.fills); this.#calculateTradeValues(transaction.sellSettings, event.fills);
transaction.result = (transaction.sellSettings.quantity * transaction.sellSettings.price) - (transaction.buySettings.quantity * transaction.buySettings.price) transaction.result = (sellSettings.quantity * sellSettings.price) - (buySettings.quantity * buySettings.price)
this.#transactionHistory.push(transaction); this.#transactionHistory.push(transaction);
this.#transactions.splice(index, 1); this.#transactions.splice(index, 1);
} }
transaction.phase = nextTransactionPhase; transaction.phase = nextTransactionPhase;
transaction.lockedForUpdate = false;
this.#saveTransactions(); this.#saveTransactions();
} }
transaction.lockedForUpdate = false;
} }
/** /**
@ -331,7 +323,7 @@ export default class TradingBot {
}); });
taSettings.quantity = quantity; taSettings.quantity = quantity;
taSettings.price = quantity != 0 ? priceSum / quantity : 0; taSettings.price = priceSum / quantity;
} }
/** /**
@ -357,8 +349,8 @@ export default class TradingBot {
} }
const transaction = new Transaction(worker.getId(), worker.getTradingPair()); const transaction = new Transaction(worker.getId(), worker.getTradingPair());
transaction.buySettings = new TransactionSettings(price, this.#config.getPerTradeLimit() / price); transaction.buySettings.orderPrice = price;
transaction.lockedForUpdate = true; transaction.buySettings.orderQuantity = Math.round(this.#config.getPerTradeLimit() / price);
this.#transactions.push(transaction); this.#transactions.push(transaction);
this.#saveTransactions(); this.#saveTransactions();
@ -368,15 +360,8 @@ export default class TradingBot {
/** /**
* @param {AbstractWorker} worker * @param {AbstractWorker} worker
* @param {Transaction} transaction * @param {Transaction} transaction
* @param {number} price
*/ */
requestSell(worker, transaction, price){ requestSell(worker, transaction){
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); this.#api.requestSell(transaction);
} }
@ -385,26 +370,12 @@ export default class TradingBot {
* @param {Transaction} transaction * @param {Transaction} transaction
*/ */
requestCancle(worker, transaction){ requestCancle(worker, transaction){
if(transaction.lockedForUpdate){ // not implemented as this class is only an interface!
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() { #loadTransactions() {
try { try {
this.#transactions = SerializableHelper.load(this.#config.getDataDirectory(), 'transactions.sjson', Transaction); 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) { } catch (e) {
this.#logger.warn("Bot was unable to load any transactions. Assuming no active transactions existing!", e); this.#logger.warn("Bot was unable to load any transactions. Assuming no active transactions existing!", e);
} }

View File

@ -1,4 +1,3 @@
import BalanceChangeEvent from "../../../apiwrapper/event/BalanceChangeEvent.js";
import AccountRights from "./AccountRights.js"; import AccountRights from "./AccountRights.js";
import AccountStatus from "./AccountStatus.js"; import AccountStatus from "./AccountStatus.js";
@ -24,14 +23,9 @@ export default class AccountInfo {
*/ */
#balances = new Map(); #balances = new Map();
/**
* @type {number}
*/
#lastBalanceUpdate = 0;
constructor() { constructor() {
//super();
this.#updateTime = Date.now(); this.#updateTime = Date.now();
this.#lastBalanceUpdate = Date.now();
} }
/** /**
@ -90,19 +84,23 @@ export default class AccountInfo {
} }
/** /**
* @param {BalanceChangeEvent} balanceEvent * @param {AccountInformation} accountInfo
*/ */
updateBalances(balanceEvent){ update(accountInfo) {
if(balanceEvent.timestamp < this.#lastBalanceUpdate){ // TODO
/*if (accountInfo == null) {
return; return;
} }
balanceEvent.changedBalances.forEach((balance, symbol) => { this.#updateTime = accountInfo.updateTime;
if(balance > 0 || this.#balances.has(symbol)){ this.#canDeposit = accountInfo.canDeposit;
this.#balances.set(symbol, balance); this.#canTrade = accountInfo.canTrade;
} this.#canWithdraw = accountInfo.canWithdraw;
});
this.#lastBalanceUpdate = balanceEvent.timestamp; for (let balance of accountInfo.balances) {
if (balance.free > 0) {
this.#balances.set(balance.asset, balance.free);
}
}*/
} }
} }

View File

@ -1,5 +1,4 @@
import Transaction from "../../apiwrapper/transaction/Transaction.js"; import Transaction from "../../apiwrapper/transaction/Transaction.js";
import TransactionPhase from "../../apiwrapper/transaction/TransactionPhase.js";
import AbstractStrategy from "./AbstractStrategy.js"; import AbstractStrategy from "./AbstractStrategy.js";
export default class AvgMinMaxStrategy extends AbstractStrategy { export default class AvgMinMaxStrategy extends AbstractStrategy {
@ -53,12 +52,6 @@ export default class AvgMinMaxStrategy extends AbstractStrategy {
* @returns {boolean} * @returns {boolean}
*/ */
shouldCancle(transaction) { 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; return false;
} }

View File

@ -28,7 +28,7 @@ export default class DefaultWorker extends AbstractWorker{
*/ */
onUpdate(assetData, transactions){ onUpdate(assetData, transactions){
this.getStrategy().setAssetData(assetData); this.getStrategy().setAssetData(assetData);
const price = parseFloat(this.getStrategy().getAssetData().getOrderBook().getBestBid().price); const price = this.getStrategy().getAssetData().getOrderBook().getBestBid().price;
if(this.getStrategy().shouldBuy()){ if(this.getStrategy().shouldBuy()){
this.getApiProvider().requestBuy(this, Math.round(50 / price), price); this.getApiProvider().requestBuy(this, Math.round(50 / price), price);
@ -36,8 +36,8 @@ export default class DefaultWorker extends AbstractWorker{
for(let transaction of transactions){ for(let transaction of transactions){
if(transaction.phase == TransactionPhase.BUY_DONE && this.getStrategy().shouldSell(transaction)){ if(transaction.phase == TransactionPhase.BUY_DONE && this.getStrategy().shouldSell(transaction)){
this.getApiProvider().requestSell(this, transaction, price); this.getApiProvider().requestSell(this, transaction);
} else if(this.getStrategy().shouldCancle(transaction)){ } else if(this.getStrategy().shouldCancle()){
this.getApiProvider().requestCancle(this, transaction); this.getApiProvider().requestCancle(this, transaction);
} }
} }

View File

@ -11,12 +11,11 @@ export default class WorkerTradingApiProvider {
// not implemented as this class is only an interface! // not implemented as this class is only an interface!
} }
/** /**
* @param {AbstractWorker} worker * @param {AbstractWorker} worker
* @param {Transaction} transaction * @param {Transaction} transaction
* @param {number} price
*/ */
requestSell(worker, transaction, price){ requestSell(worker, transaction){
// not implemented as this class is only an interface! // not implemented as this class is only an interface!
} }

View File

@ -24,8 +24,8 @@ export default class TransactionConverter extends AbstractConverter {
phase: obj.phase, phase: obj.phase,
timestamp:obj.timestamp, timestamp:obj.timestamp,
result: obj.result, result: obj.result,
buySettings: obj.buySettings == null ? null : tsConverter.toRestObject(obj.buySettings), buySettings: tsConverter.toRestObject(obj.buySettings),
sellSettings: obj.sellSettings == null ? null : tsConverter.toRestObject(obj.sellSettings), sellSettings: tsConverter.toRestObject(obj.sellSettings),
symbol: obj.symbol.getKey() symbol: obj.symbol.getKey()
}; };
} }
@ -43,8 +43,8 @@ export default class TransactionConverter extends AbstractConverter {
t.phase = obj.phase; t.phase = obj.phase;
t.timestamp = obj.timestamp; t.timestamp = obj.timestamp;
t.result = obj.result; t.result = obj.result;
t.buySettings = obj.buySettings == null ? null : tsConverter.fromRestObject(obj.buySettings); t.buySettings = tsConverter.fromRestObject(obj.buySettings);
t.sellSettings = obj.sellSettings == null ? null : tsConverter.fromRestObject(obj.sellSettings); t.sellSettings = tsConverter.fromRestObject(obj.sellSettings);
t.symbol = TradingPairs.fromString(obj.symbol); t.symbol = TradingPairs.fromString(obj.symbol);
return t; return t;