[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 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)

View File

@ -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;

View File

@ -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);

View File

@ -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
*/

View File

@ -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);
}
}
}

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