Compare commits

...

2 Commits

Author SHA1 Message Date
e002eeec44 [#20] Bugfix 2025-01-11 15:15:28 +01:00
69a548a200 [#21] Bug fixes and rest service and point for aggregated data 2025-01-11 14:16:44 +01:00
8 changed files with 133 additions and 14 deletions

View File

@ -1,6 +1,7 @@
import TradingPairs from "../apiwrapper/assets/TraidingPairs.js";
import Transaction from "../apiwrapper/transaction/Transaction.js";
import WebViewDataProvider from "../webview/WebViewDataProvider.js";
import AssetData from "./data/asset/AssetData.js";
import TradingBot from "./TradingBot.js";
export default class TradingBotRestDataProvider extends WebViewDataProvider {
@ -29,7 +30,15 @@ export default class TradingBotRestDataProvider extends WebViewDataProvider {
* @returns {OrderBook}
*/
getOrderBook(symbol) {
return this.#tradingBot.getAssetDataCollector().getAssetData(symbol).getOrderBook();
return this.getAssetData(symbol).getOrderBook();
}
/**
* @param {*} symbol
* @returns {AssetData}
*/
getAssetData(symbol){
return this.#tradingBot.getAssetDataCollector().getAssetData(symbol);
}
/**

View File

@ -1,4 +1,3 @@
import TradeEvent from "../../../apiwrapper/event/TradeEvent.js";
import OrderBookEntry from "../../../apiwrapper/orderbook/OrderBookEntry.js";
export default class AggregatedDataPoint {
@ -61,12 +60,12 @@ export default class AggregatedDataPoint {
/**
* @type {OrderBookEntry}
*/
bestAsk = 0;
bestAsk = null;
/**
* @type {OrderBookEntry}
*/
bestBid = 0;
bestBid = null;
/**
* @type {number}
@ -107,12 +106,14 @@ export default class AggregatedDataPoint {
pushBid(bid){
if(!this.hasValues){
this.startPrice = bid.price;
this.minPrice = bid.price;
this.maxPrice = bid.price;
}
this.hasValues = true;
this.minPrice = Math.min(this.minPrice, bid.price);
this.maxPrice = Math.max(this.maxPrice = bid.price);
this.maxPrice = Math.max(this.maxPrice, bid.price);
this.endPrice = bid.price;
this.#priceValues.push(bid.price);
this.#priceValues.push(parseFloat(bid.price));
if(this.bestBid == null || this.bestBid.price < bid.price){
this.bestBid = bid;
}
@ -142,12 +143,18 @@ export default class AggregatedDataPoint {
*/
end(endTime){
this.endTime = endTime;
let priceSum = 0;
this.#priceValues.forEach(p => priceSum += p);
this.avgPrice = priceSum / this.#priceValues.length;
if(this.#priceValues.length > 0){
let priceSum = 0;
this.#priceValues.forEach(p => priceSum += p);
this.avgPrice = priceSum / this.#priceValues.length;
}
console.log(this.minPrice, this.maxPrice, this.avgPrice, this.minPrice <= this.avgPrice, this.avgPrice <= this.maxPrice);
this.#priceValues = [];
}
/**
* @returns {AggregatedDataPoint} returns a deep copy of this object
*/
clone(){
const dp = new AggregatedDataPoint(this.startTime, this.duration);
dp.startPrice = this.startPrice;
@ -163,5 +170,7 @@ export default class AggregatedDataPoint {
dp.tradeVolume = this.tradeVolume;
dp.bestAsk = this.bestAsk;
dp.bestBid = this.bestBid;
return dp;
}
}

View File

@ -47,7 +47,7 @@ export default class AssetDataAggregator {
const tmpSlot = new AggregatedDataPoint(timestamp - AssetDataAggregator.#SLOT_1_MIN, AssetDataAggregator.#SLOT_1_MIN);
for(let i = this.#perSecondDataPoints.length - 60; i >= 0; i--){
for(let i = this.#perSecondDataPoints.length - 60; i < this.#perSecondDataPoints.length; i++){
const dp = this.#perSecondDataPoints[i];
tmpSlot.pushAsk(dp.bestAsk);
tmpSlot.pushBid(dp.bestBid);
@ -71,10 +71,13 @@ export default class AssetDataAggregator {
return;
}
const tmpSlot = this.#currentDataPoint;
let tmpSlot = this.#currentDataPoint;
if(!tmpSlot.hasValues){
tmpSlot = this.#perSecondDataPoints[this.#perSecondDataPoints.length - 1].clone();
tmpSlot.startTime = timestamp - AssetDataAggregator.#SLOT_1_SEC;
tmpSlot.endTime = timestamp;
tmpSlot.tradeCount = 0;
tmpSlot.tradeVolume = 0;
}
this.#currentDataPoint = new AggregatedDataPoint(timestamp, AssetDataAggregator.#SLOT_1_SEC);
tmpSlot.end(timestamp);
@ -102,6 +105,8 @@ export default class AssetDataAggregator {
const newPoint = point.clone();
newPoint.startTime = point.startTime + duration * (i + 1);
newPoint.endTime = newPoint.startTime + duration;
newPoint.tradeCount = 0;
newPoint.tradeVolume = 0;
targetArr.push(newPoint);
}
}
@ -119,7 +124,7 @@ export default class AssetDataAggregator {
* @returns {AggregatedDataPoint[]}
*/
getData(duration, start = -1){
const result = [];
let result = [];
const durationInMs = UnitHelper.formatedDurationToMs(duration);
// if seconds data required
@ -132,7 +137,7 @@ export default class AssetDataAggregator {
const dpCount = Math.round(durationInMs / AssetDataAggregator.#SLOT_1_MIN);
const pointOffset = start == -1 ? 0 : Math.round(start / AssetDataAggregator.#SLOT_1_MIN);
if(pointOffset + dpCount <= this.#perMinuteDataPoints.length){
result = this.#perMinuteDataPoints.slice(-1 * (pointOffset + dpCount), -1 * pointOffset);
result = this.#perMinuteDataPoints.slice(-1 * (pointOffset + dpCount), pointOffset > 0 ? -1 * pointOffset : this.#perMinuteDataPoints.length);
}
}

View File

@ -74,7 +74,7 @@ export default class UnitHelper {
if (match) {
return match[1] * UnitHelper.#CHRONO_UNIT_TO_MS_MAPPING.get(match[2]);
} else {
return 0;
return isNaN(duration) ? 0 : parseFloat(duration);
}
}
}

View File

@ -3,6 +3,7 @@ import bodyParser from "body-parser";
import cors from "cors";
import WebViewDataProvider from "./WebViewDataProvider.js";
import ConverterFactory from "./converter/ConverterFactory.js";
import AggregatedDataPointConverter from "./converter/converters/AggregatedDataPointConverter.js";
export default class RestClient {
@ -63,6 +64,10 @@ export default class RestClient {
res.json(this.#getAssetOrderBook(req.params.symbol));
});
this.#service.get("/asset/:symbol/aggregated", (req, res, next) => {
res.json(this.#getAggregatedAssetData(req.params.symbol, req.query.startTime, req.query.duration));
});
this.#service.get("/worker/:name/attribute/:attr", (req, res, next) => {
res.json(this.#getWorkerAttribute(req.params.name, req.params.attr));
});
@ -76,6 +81,19 @@ export default class RestClient {
}
#getAggregatedAssetData(asset, starttime, duration){
const st = starttime != null ? starttime : -1;
const d = duration != null ? duration : 3600000;
const data = this.#dataProvider.getAssetData(asset).assetDataAggregator.getData(d, st);
const converter = new AggregatedDataPointConverter();
const result = [];
data.forEach(dp => result.push(converter.toRestObject(dp)));
return result;
}
#getAssetOrderBook(asset){
const orderBook = this.#dataProvider.getOrderBook(asset);

View File

@ -1,5 +1,6 @@
import OrderBook from "../apiwrapper/orderbook/OrderBook.js";
import Transaction from "../apiwrapper/transaction/Transaction.js";
import AssetData from "../tradingbot/data/asset/AssetData.js";
import AbstractWorker from "../tradingbot/worker/AbstractWorker.js";
export default class WebViewDataProvider {
@ -54,4 +55,12 @@ export default class WebViewDataProvider {
getWorkers(){
return [];
}
/**
* @param {*} symbol
* @returns {AssetData}
*/
getAssetData(symbol){
return null;
}
}

View File

@ -1,12 +1,15 @@
import OrderBook from "../../apiwrapper/orderbook/OrderBook.js";
import OrderBookEntry from "../../apiwrapper/orderbook/OrderBookEntry.js";
import AggregatedDataPoint from "../../tradingbot/data/asset/AggregatedDataPoint.js";
import AbstractConverter from "./AbstractConverter.js";
import AggregatedDataPointConverter from "./converters/AggregatedDataPointConverter.js";
import OrderBookConverter from "./converters/OrderBookConverter.js";
import OrderBookEntryConverter from "./converters/OrderBookEntryConverter.js";
export default class ConverterFactory {
static OBJECT_TYPE_ORDER_BOOK = 'order_book';
static OBJECT_TYPE_ORDER_BOOK_ENTRY = 'order_book_entry';
static OBJECT_TYPE_AGGREGATED_DATA_POINT = 'aggregated_data_point';
/**
* @param {any} obj
@ -25,6 +28,8 @@ export default class ConverterFactory {
return new OrderBookConverter();
} else if (obj instanceof OrderBookEntry) {
return new OrderBookEntryConverter();
} else if (obj instanceof AggregatedDataPoint){
return new AggregatedDataPointConverter();
}
throw new Error("No converter defined for: " + JSON.stringify(obj));
@ -40,6 +45,8 @@ export default class ConverterFactory {
return new OrderBookConverter();
case this.OBJECT_TYPE_ORDER_BOOK_ENTRY:
return new OrderBookEntryConverter();
case this.OBJECT_TYPE_AGGREGATED_DATA_POINT:
return new AggregatedDataPointConverter();
default:
throw new Error("No converter defined for type: " + type);
}

View File

@ -0,0 +1,62 @@
import AggregatedDataPoint from "../../../tradingbot/data/asset/AggregatedDataPoint.js";
import AbstractConverter from "../AbstractConverter.js";
import ConverterFactory from "../ConverterFactory.js";
import OrderBookEntryConverter from "./OrderBookEntryConverter.js";
export default class AggregatedDataPointConverter extends AbstractConverter {
constructor(){
super();
}
/**
* @param {AggregatedDataPoint} obj
* @returns {object}
*/
toRestObject(obj) {
const obeConverter = new OrderBookEntryConverter();
return {
_objectType: ConverterFactory.OBJECT_TYPE_AGGREGATED_DATA_POINT,
startTime: obj.startTime,
endTime: obj.endTime,
duration: obj.duration,
tradeCount: obj.tradeCount,
tradeVolume: obj.tradeVolume,
minPrice: obj.minPrice,
maxPrice: obj.maxPrice,
avgPrice: obj.avgPrice,
startPrice: obj.startPrice,
endPrice: obj.endPrice,
bestAsk: obeConverter.toRestObject(obj.bestAsk),
bestBid: obeConverter.toRestObject(obj.bestBid),
peakStarts: obj.peakStarts,
peakEnds: obj.peakEnds
};
}
/**
* @param {object} obj
* @returns {AggregatedDataPoint}
*/
fromRestObject(obj) {
const obeConverter = new OrderBookEntryConverter();
const dp = new AggregatedDataPoint(obj.startTime, obj.duration);
dp.startTime = obj.startTime;
dp.endTime = obj.endTime;
dp.duration = obj.duration;
dp.tradeCount = obj.tradeCount;
dp.tradeVolume = obj.tradeVolume;
dp.minPrice = obj.minPrice;
dp.maxPrice = obj.maxPrice;
dp.avgPrice = obj.avgPrice;
dp.startPrice = obj.startPrice;
dp.endPrice = obj.endPrice;
dp.bestAsk = obeConverter.fromRestObject(obj.bestAsk);
dp.bestBid = obeConverter.fromRestObject(obj.bestBid);
dp.peakStarts = obj.peakStarts;
dp.peakEnds = obj.peakEnds;
return dp;
}
}