@tomei/live-price
Version:
Tomei live-price Package
469 lines • 22.5 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceFeed = void 0;
const general_1 = require("@tomei/general");
const log_1 = require("@tomei/log");
const feed_cut_off_history_1 = require("../feed-cut-off-history/feed-cut-off-history");
const source_feed_repository_1 = require("./source-feed.repository");
const feed_enum_1 = require("../../enum/feed.enum");
const feed_history_1 = require("../feed-history/feed-history");
const feed_1 = require("../feed/feed");
const feed_history_repository_1 = require("../feed-history/feed-history.repository");
const sequelize_1 = require("sequelize");
const sso_1 = require("@tomei/sso");
const activity_history_1 = require("@tomei/activity-history");
class SourceFeed extends feed_1.Feed {
constructor(sourcefeedAttr, feedAttr) {
var _a, _b, _c;
super(feedAttr);
if (sourcefeedAttr) {
this.FeedName = sourcefeedAttr.FeedName;
this.Status = sourcefeedAttr.Status;
this.IsSourceFeedAvailableYN = (_a = sourcefeedAttr.IsSourceFeedAvailableYN) !== null && _a !== void 0 ? _a : 'Y';
this.IsFeedCutOffYN = (_b = sourcefeedAttr.IsFeedCutOffYN) !== null && _b !== void 0 ? _b : 'N';
this.IsManualPriceActivatedYN = (_c = feedAttr === null || feedAttr === void 0 ? void 0 : feedAttr.IsManualPriceActivatedYN) !== null && _c !== void 0 ? _c : 'N';
this.CurrentCutOffHistoryId = sourcefeedAttr.CurrentCutOffHistoryId;
this.AutomaticCutOffVariance = sourcefeedAttr.AutomaticCutOffVariance;
this.AutomaticCutOffMinutes = sourcefeedAttr.AutomaticCutOffMinutes;
}
}
static init(dbTransaction, FeedName) {
return __awaiter(this, void 0, void 0, function* () {
try {
if (FeedName) {
const sourcefeed = yield SourceFeed._SourceRepo.findByPk(FeedName, dbTransaction);
const feed = yield feed_1.Feed._Repo.findByPk(FeedName, dbTransaction);
if (sourcefeed) {
return new SourceFeed(sourcefeed, feed);
}
else {
const error = new general_1.ClassError('SourceFeed', 'FeedErrMsg00', 'Feed Not Found');
yield SourceFeed.logger.error({
error,
methodName: 'init',
transaction: dbTransaction,
});
throw error;
}
}
return new SourceFeed();
}
catch (error) {
const wrappedError = new general_1.ClassError('SourceFeed', 'FeedErrMsg01', error.message);
yield SourceFeed.logger.error({
error,
methodName: 'init',
transaction: dbTransaction,
});
throw wrappedError;
}
});
}
save(dbTransaction) {
const _super = Object.create(null, {
save: { get: () => super.save }
});
return __awaiter(this, void 0, void 0, function* () {
try {
yield _super.save.call(this, dbTransaction);
const sourcefeed = yield SourceFeed._SourceRepo.findOne({
where: {
FeedName: this.FeedName,
},
transaction: dbTransaction,
});
const payload = {
FeedName: this.FeedName,
Status: this.Status,
IsSourceFeedAvailableYN: this.IsSourceFeedAvailableYN,
IsFeedCutOffYN: this.IsFeedCutOffYN,
CurrentCutOffHistoryId: this.CurrentCutOffHistoryId,
AutomaticCutOffVariance: this.AutomaticCutOffVariance,
AutomaticCutOffMinutes: this.AutomaticCutOffMinutes,
};
if (sourcefeed) {
yield SourceFeed._SourceRepo.update(payload, {
where: {
FeedName: this.FeedName,
},
transaction: dbTransaction,
});
}
else {
yield SourceFeed._SourceRepo.create(payload, {
transaction: dbTransaction,
});
}
}
catch (error) {
const wrappedError = new general_1.ClassError('Feed', 'FeedErrMsg02', error.message);
yield SourceFeed.logger.error({
error,
methodName: 'save',
transaction: dbTransaction,
});
throw wrappedError;
}
});
}
getCutOffDetails(dbTransaction) {
return __awaiter(this, void 0, void 0, function* () {
try {
if (this.CurrentCutOffHistoryId) {
return yield feed_cut_off_history_1.FeedCutOffHistory.init(this.CurrentCutOffHistoryId, dbTransaction);
}
return null;
}
catch (error) {
const wrappedError = new general_1.ClassError('Feed', 'FeedErrMsg03', error.message);
yield SourceFeed.logger.error({
error,
methodName: 'getCutOffDetails',
transaction: dbTransaction,
});
throw wrappedError;
}
});
}
recordFeedPrice(priceData, dbTransaction) {
return __awaiter(this, void 0, void 0, function* () {
try {
const feedName = this.FeedName;
const sourceFeedBuyPrice = priceData.BuyPrice;
const sourceFeedSellPrice = priceData.SellPrice;
let FeedBuyPrice;
let FeedSellPrice;
let IsManualCutOffYN = 'N';
let IsCircuitBreakerActivatedYN = 'N';
if (this.IsManualPriceActivatedYN === 'Y') {
const manualPriceHistory = yield feed_history_1.FeedHistory.getLatestPrice(this.FeedName, this.FeedName.split('/')[1], null, dbTransaction);
FeedBuyPrice = manualPriceHistory.FeedBuyPrice;
FeedSellPrice = manualPriceHistory.FeedSellPrice;
}
else if (this.IsFeedCutOffYN === 'Y') {
FeedBuyPrice = 0;
FeedSellPrice = 0;
const feedCutOffHistory = yield feed_cut_off_history_1.FeedCutOffHistory.init(this.CurrentCutOffHistoryId, dbTransaction);
if (feedCutOffHistory.ActivationTrigger === feed_enum_1.ActivationTrigger.MANUAL) {
IsManualCutOffYN = 'Y';
}
else {
IsCircuitBreakerActivatedYN = 'Y';
}
}
else {
FeedBuyPrice = priceData.BuyPrice;
FeedSellPrice = priceData.SellPrice;
}
const isSourceFeedAvailableYN = priceData.IsSourceFeedAvailableYN;
const isManualPriceActivatedYN = this.IsManualPriceActivatedYN;
let feedHistory = yield feed_history_1.FeedHistory.init(dbTransaction);
feedHistory = yield feedHistory.saveFeedPrice({
FeedName: feedName,
DateTime: new Date(),
Currency: priceData.Currency,
SourceFeedBuyPrice: sourceFeedBuyPrice,
SourceFeedSellPrice: sourceFeedSellPrice,
FeedBuyPrice: FeedBuyPrice,
FeedSellPrice: FeedSellPrice,
IsSourceFeedAvailableYN: isSourceFeedAvailableYN,
IsCircuitBreakerActivatedYN: IsCircuitBreakerActivatedYN,
IsManualPriceActivatedYN: isManualPriceActivatedYN,
IsManualCutOffYN: IsManualCutOffYN,
}, dbTransaction);
if (FeedBuyPrice !== 0 && FeedSellPrice !== 0) {
this.Status = 'Available';
}
else {
this.Status = 'Unavailable';
}
yield this.save(dbTransaction);
return feedHistory;
}
catch (error) {
yield SourceFeed.logger.error({
error,
methodName: 'recordFeedPrice',
transaction: dbTransaction,
});
throw error;
}
});
}
getPreviousSourceFeedConfig(dbTransaction) {
return __awaiter(this, void 0, void 0, function* () {
try {
const previousActivity = yield SourceFeed._ActivityRepo.findOne({
where: {
EntityType: 'SourceFeed',
EntityId: this.FeedName,
Action: activity_history_1.ActionEnum.UPDATE,
Description: 'Update Circuit Breaker Config for :' + this.FeedName,
},
transaction: dbTransaction,
order: [['PerformedAt', 'DESC']],
});
if (previousActivity) {
const previousConfig = JSON.parse(previousActivity.EntityValueBefore);
return {
AutomaticCutOffVariance: previousConfig.AutomaticCutOffVariance,
AutomaticCutOffMinutes: previousConfig.AutomaticCutOffMinutes,
UpdatedAt: previousActivity.PerformedAt,
};
}
return false;
}
catch (error) {
yield SourceFeed.logger.error({
error,
methodName: 'getPreviousCircuitBreakerConfig',
transaction: dbTransaction,
});
throw error;
}
});
}
circuitBreakerCheck(priceData, priceSource, dbTransaction) {
return __awaiter(this, void 0, void 0, function* () {
try {
let automaticCutOffVariance = this.AutomaticCutOffVariance;
let automaticCutOffMinutes = this.AutomaticCutOffMinutes;
const previousConfig = yield this.getPreviousSourceFeedConfig(dbTransaction);
if (previousConfig) {
const currentTime = new Date().getTime();
const previousConfigUpdatedTime = previousConfig.UpdatedAt.getTime();
const currentConfigTimeWindow = automaticCutOffMinutes * 60000;
const effectiveStartTime = Math.max(previousConfigUpdatedTime + currentConfigTimeWindow, currentTime);
if (currentTime < effectiveStartTime) {
automaticCutOffVariance = previousConfig.AutomaticCutOffVariance;
automaticCutOffMinutes = previousConfig.AutomaticCutOffMinutes;
}
else {
automaticCutOffVariance = this.AutomaticCutOffVariance;
automaticCutOffMinutes = this.AutomaticCutOffMinutes;
}
}
else {
automaticCutOffVariance = this.AutomaticCutOffVariance;
automaticCutOffMinutes = this.AutomaticCutOffMinutes;
}
const feedHistories = yield SourceFeed._FeedHistoryRepo.findAll({
where: {
FeedName: this.FeedName,
DateTime: {
[sequelize_1.Op.gte]: new Date(new Date().getTime() - automaticCutOffMinutes * 60000),
},
},
order: [['DateTime', 'DESC']],
transaction: dbTransaction,
});
if (feedHistories.some(({ IsManualPriceActivatedYN }) => IsManualPriceActivatedYN === 'Y')) {
return true;
}
const validFeedHistories = feedHistories.filter(({ SourceFeedBuyPrice, SourceFeedSellPrice }) => SourceFeedBuyPrice !== 0 && SourceFeedSellPrice !== 0);
if (validFeedHistories.length === 0)
return true;
const buyPrices = validFeedHistories.map(({ SourceFeedBuyPrice }) => SourceFeedBuyPrice);
const sellPrices = validFeedHistories.map(({ SourceFeedSellPrice }) => SourceFeedSellPrice);
buyPrices.push(priceData.BuyPrice);
sellPrices.push(priceData.SellPrice);
const [minBuyPrice, maxBuyPrice] = [
Math.min(...buyPrices),
Math.max(...buyPrices),
];
const [minSellPrice, maxSellPrice] = [
Math.min(...sellPrices),
Math.max(...sellPrices),
];
const buyVariance = minBuyPrice > 0 ? ((maxBuyPrice - minBuyPrice) / minBuyPrice) * 100 : 0;
const sellVariance = minSellPrice > 0
? ((maxSellPrice - minSellPrice) / minSellPrice) * 100
: 0;
if (minBuyPrice <= 0 ||
minSellPrice <= 0 ||
!isFinite(buyVariance) ||
!isFinite(sellVariance) ||
isNaN(buyVariance) ||
isNaN(sellVariance)) {
console.warn(`Circuit breaker: Invalid price data detected for ${this.FeedName}. Skipping variance check.`);
return true;
}
if ((buyVariance > automaticCutOffVariance ||
sellVariance > automaticCutOffVariance) &&
this.IsFeedCutOffYN === 'N') {
yield this.cutOff(priceSource, dbTransaction);
return false;
}
return true;
}
catch (error) {
yield SourceFeed.logger.error({
error,
methodName: 'circuitBreakerCheck',
transaction: dbTransaction,
});
throw error;
}
});
}
cutOff(priceSource, dbTransaction, activatedById) {
return __awaiter(this, void 0, void 0, function* () {
try {
let feedCutOffHistory = yield feed_cut_off_history_1.FeedCutOffHistory.init();
feedCutOffHistory.FeedName = this.FeedName;
let activatedBy = null;
let isManualCutOff = 'N';
let isCircuitBreakerActivated = 'N';
if (activatedById) {
const user = yield sso_1.LoginUser.init(null, parseInt(activatedById));
activatedBy = user.FullName;
feedCutOffHistory.ActivationTrigger = feed_enum_1.ActivationTrigger.MANUAL;
isManualCutOff = 'Y';
}
else {
feedCutOffHistory.ActivationTrigger = feed_enum_1.ActivationTrigger.LIMIT_BREACH;
isCircuitBreakerActivated = 'Y';
}
feedCutOffHistory = yield feedCutOffHistory.create(dbTransaction, activatedById);
let feedHistory = yield feed_history_1.FeedHistory.init(dbTransaction);
const fetchResult = yield priceSource.fetchPrice({
[this.FeedName]: this,
});
const priceData = fetchResult[this.FeedName];
feedHistory = yield feedHistory.saveFeedPrice({
FeedName: this.FeedName,
DateTime: new Date(),
Currency: priceData.Currency,
SourceFeedBuyPrice: priceData.BuyPrice,
SourceFeedSellPrice: priceData.SellPrice,
FeedBuyPrice: 0,
FeedSellPrice: 0,
IsSourceFeedAvailableYN: priceData.IsSourceFeedAvailableYN,
IsManualCutOffYN: isManualCutOff,
IsCircuitBreakerActivatedYN: isCircuitBreakerActivated,
IsManualPriceActivatedYN: 'N',
}, dbTransaction);
yield this.createTomeiPriceHistory(feedHistory, dbTransaction);
this.IsFeedCutOffYN = 'Y';
this.CurrentCutOffHistoryId = feedCutOffHistory.FeedCutOffHistoryId;
yield this.save(dbTransaction);
}
catch (error) {
yield SourceFeed.logger.error({
error,
methodName: 'cutOff',
transaction: dbTransaction,
});
throw error;
}
});
}
deactivateCutOff(priceSource, dbTransaction, deactivatedById) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield feed_cut_off_history_1.FeedCutOffHistory.deactivate(this.FeedName, deactivatedById, dbTransaction);
this.IsFeedCutOffYN = 'N';
yield this.save(dbTransaction);
const fetchResult = yield priceSource.fetchPrice({
[this.FeedName]: this,
});
const priceData = fetchResult[this.FeedName];
const feedHistory = yield this.recordFeedPrice(priceData, dbTransaction);
yield this.createTomeiPriceHistory(feedHistory, dbTransaction);
}
catch (error) {
yield SourceFeed.logger.error({
error,
methodName: 'deactivateCutOff',
transaction: dbTransaction,
});
throw error;
}
});
}
updateCircuitBreakerConfig(AutomaticCutOffVariance, AutomaticCutOffMinutes, dbTransaction, loginUser) {
return __awaiter(this, void 0, void 0, function* () {
try {
const EntityValueBefore = {
AutomaticCutOffVariance: this.AutomaticCutOffVariance,
AutomaticCutOffMinutes: this.AutomaticCutOffMinutes,
FeedName: this.FeedName,
Status: this.Status,
IsSourceFeedAvailableYN: this.IsSourceFeedAvailableYN,
IsFeedCutOffYN: this.IsFeedCutOffYN,
CurrentCutOffHistoryId: this.CurrentCutOffHistoryId,
};
this.AutomaticCutOffVariance = AutomaticCutOffVariance;
this.AutomaticCutOffMinutes = AutomaticCutOffMinutes;
yield SourceFeed._SourceRepo.update({
AutomaticCutOffVariance: this.AutomaticCutOffVariance,
AutomaticCutOffMinutes: this.AutomaticCutOffMinutes,
}, {
where: {
FeedName: this.FeedName,
},
transaction: dbTransaction,
});
const activity = new activity_history_1.Activity();
activity.ActivityId = activity.createId();
activity.Description =
'Update Circuit Breaker Config for :' + this.FeedName;
activity.Action = activity_history_1.ActionEnum.UPDATE;
activity.EntityType = 'SourceFeed';
activity.EntityId = this.FeedName;
activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
activity.EntityValueAfter = JSON.stringify({
AutomaticCutOffVariance: this.AutomaticCutOffVariance,
AutomaticCutOffMinutes: this.AutomaticCutOffMinutes,
FeedName: this.FeedName,
Status: this.Status,
IsSourceFeedAvailableYN: this.IsSourceFeedAvailableYN,
IsFeedCutOffYN: this.IsFeedCutOffYN,
CurrentCutOffHistoryId: this.CurrentCutOffHistoryId,
});
yield activity.create(loginUser.ObjectId, dbTransaction);
}
catch (error) {
yield SourceFeed.logger.error({
error,
methodName: 'updateCircuitBreakerConfig',
transaction: dbTransaction,
});
throw error;
}
});
}
static getAllSourceFeed(dbTransaction) {
return __awaiter(this, void 0, void 0, function* () {
try {
const sourceFeeds = yield SourceFeed._SourceRepo.findAll({
transaction: dbTransaction,
});
return sourceFeeds.map((sourceFeed) => new SourceFeed(sourceFeed));
}
catch (error) {
yield SourceFeed.logger.error({
error,
methodName: 'getAllSourceFeed',
transaction: dbTransaction,
});
throw error;
}
});
}
}
exports.SourceFeed = SourceFeed;
SourceFeed._SourceRepo = new source_feed_repository_1.SourceFeedRepository();
SourceFeed._FeedHistoryRepo = new feed_history_repository_1.FeedHistoryRepository();
SourceFeed._ActivityRepo = new activity_history_1.ActivityHistoryRepository();
SourceFeed.logger = new log_1.ApplicationLog('live-price', 'SourceFeed');
//# sourceMappingURL=source-feed.js.map