[BOT] implement buy und sell propagating from worker to api

This commit is contained in:
darkeye 2025-01-11 21:37:01 +01:00
parent adbe4f30c8
commit ed9ca77def
6 changed files with 95 additions and 7 deletions

View File

@ -16,6 +16,7 @@ import Transaction from "../../apiwrapper/transaction/Transaction.js";
import TransactionUpdateEvent from "../../apiwrapper/event/TransactionUpdateEvent.js"; import TransactionUpdateEvent from "../../apiwrapper/event/TransactionUpdateEvent.js";
import TransactionFill from "../../apiwrapper/transaction/TransactionFill.js"; import TransactionFill from "../../apiwrapper/transaction/TransactionFill.js";
import TransactionStatus from "../../apiwrapper/transaction/TransactionStatus.js"; import TransactionStatus from "../../apiwrapper/transaction/TransactionStatus.js";
import TransactionPhase from "../../apiwrapper/transaction/TransactionPhase.js";
export default class BinanceApiClient extends APIClient { export default class BinanceApiClient extends APIClient {
/** /**
@ -266,6 +267,7 @@ export default class BinanceApiClient extends APIClient {
"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;
}).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)
@ -288,6 +290,7 @@ export default class BinanceApiClient extends APIClient {
"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;
}).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)

View File

@ -45,6 +45,10 @@ export default class Transaction extends Serializable {
*/ */
timestamp = 0; timestamp = 0;
/**
* @param {string} initiator
* @param {TradingPair} tradingPair
*/
constructor(initiator, tradingPair){ constructor(initiator, tradingPair){
super(); super();
this.initiator = initiator; this.initiator = initiator;

View File

@ -123,7 +123,7 @@ export default class TradingBot {
this.#loadWorker(); this.#loadWorker();
this.#worker.forEach(worker => { this.#worker.forEach(worker => {
this.#registerTradingPair(worker.getTradingPair()); this.#registerTradingPair(worker.getTradingPair());
worker.init(this.#logger, this.#config.getDataDirectory()); worker.init(this.#logger, this.#config.getDataDirectory(), this);
}); });
this.#transactions.forEach(transaction => { this.#transactions.forEach(transaction => {
const type = transaction.phase === (TransactionPhase.INITIAL || transaction.phase === TransactionPhase.BUY_IN_PROGRESS) ? "buy" : "sell"; const type = transaction.phase === (TransactionPhase.INITIAL || transaction.phase === TransactionPhase.BUY_IN_PROGRESS) ? "buy" : "sell";
@ -235,6 +235,7 @@ export default class TradingBot {
handleAccountUpdate(accountUpdateEvent) { handleAccountUpdate(accountUpdateEvent) {
switch (accountUpdateEvent.eventType) { switch (accountUpdateEvent.eventType) {
case APIEventType.BALANCE_CHANGE: case APIEventType.BALANCE_CHANGE:
this.#logger.info("Balance update: ", accountUpdateEvent);
// TODO update changed balances // TODO update changed balances
break; break;
case APIEventType.TRANSACTION_UPDATE: 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() { #loadTransactions() {
try { try {
this.#transactions = SerializableHelper.load(this.#config.getDataDirectory(), 'transactions.sjson', Transaction); 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(), 'transactions.sjson', this.#transactions);
SerializableHelper.save(this.#config.getDataDirectory(), 'transactionHistory.sjson', this.#transactionHistory); SerializableHelper.save(this.#config.getDataDirectory(), 'transactionHistory.sjson', this.#transactionHistory);
} }
@ -366,7 +402,7 @@ export default class TradingBot {
} }
} }
#saveWorker() { async #saveWorker() {
const workerArr = []; const workerArr = [];
Array.from(this.#worker.values()).forEach(w => workerArr.push(w)); Array.from(this.#worker.values()).forEach(w => workerArr.push(w));
SerializableHelper.save(this.#config.getDataDirectory(), 'worker.sjson', workerArr); SerializableHelper.save(this.#config.getDataDirectory(), 'worker.sjson', workerArr);

View File

@ -5,6 +5,7 @@ import Serializable from "../../util/Serializable.js";
import SerializableHelper from "../../util/SerializableHelper.js"; import SerializableHelper from "../../util/SerializableHelper.js";
import AssetData from "../data/asset/AssetData.js"; import AssetData from "../data/asset/AssetData.js";
import AbstractStrategy from "../strategy/AbstractStrategy.js"; import AbstractStrategy from "../strategy/AbstractStrategy.js";
import WorkerTradingApiProvider from "./WorkerTradingApiProvider.js";
export default class AbstractWorker extends Serializable { export default class AbstractWorker extends Serializable {
@ -28,6 +29,11 @@ export default class AbstractWorker extends Serializable {
*/ */
#strategy = null; #strategy = null;
/**
* @type {WorkerTradingApiProvider}
*/
#apiProvider = null
/** /**
* @type {Logger} * @type {Logger}
*/ */
@ -49,9 +55,11 @@ export default class AbstractWorker extends Serializable {
/** /**
* @param {Logger} logger * @param {Logger} logger
* @param {string} dataPath * @param {string} dataPath
* @param {WorkerTradingApiProvider} apiProvider
*/ */
init(logger, dataPath){ init(logger, dataPath, apiProvider){
this.#logger = logger; this.#logger = logger;
this.#apiProvider = apiProvider;
if(this.#strategy != null){ if(this.#strategy != null){
this.#strategy.init(logger); this.#strategy.init(logger);
} }
@ -114,6 +122,13 @@ export default class AbstractWorker extends Serializable {
return this.#logger; return this.#logger;
} }
/**
* @returns {WorkerTradingApiProvider}
*/
getApiProvider(){
return this.#apiProvider;
}
/** /**
* @param {string} dataPath path to data directory * @param {string} dataPath path to data directory
*/ */

View File

@ -28,16 +28,17 @@ export default class DefaultWorker extends AbstractWorker{
*/ */
onUpdate(assetData, transactions){ onUpdate(assetData, transactions){
this.getStrategy().updateAssetData(assetData); this.getStrategy().updateAssetData(assetData);
const price = this.getStrategy().getAssetData().getOrderBook().getBestBid().price;
if(this.getStrategy().shouldBuy()){ if(this.getStrategy().shouldBuy()){
// TODO create transaction! this.getApiProvider().requestBuy(this, Math.round(50 / price), price);
} }
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)){
// TODO request sell! this.getApiProvider().requestSell(this, transaction);
} else if(this.getStrategy().shouldCancle()){ } else if(this.getStrategy().shouldCancle()){
// TODO request cancle this.getApiProvider().requestCancle(this, transaction);
} }
} }
} }

View File

@ -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!
}
}