Skip to content

Commit

Permalink
v. 1.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
balmli committed Jan 1, 2023
1 parent 6ee0be9 commit 4375614
Show file tree
Hide file tree
Showing 12 changed files with 5,711 additions and 5,018 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ root = true
[*]
end_of_line = lf
indent_style = space
indent_size = 2
indent_size = 4
trim_trailing_whitespace = true
max_line_length = 100
95 changes: 82 additions & 13 deletions lib/NordpoolApi.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
import moment, {Moment, MomentInput} from 'moment-timezone';
import {
NordpoolColumn,
NordpoolData,
NordpoolOptions,
NordpoolPrice,
NordpoolPrices
} from "./types";

const http = require('http.min');

import {NordpoolData, NordpoolOptions, NordpoolPrice, NordpoolPrices} from "./types";

export class NordpoolApi {

logger: any;

constructor({logger}: {
logger: any
}) {
this.logger = logger;
}

/**
* Fetch prices from Nordpool, for yesterday, today and tomorrow.
*
Expand All @@ -29,7 +43,7 @@ export class NordpoolApi {
.map(r => r as NordpoolPrice)
.sort((a, b) => a.time - b.time);
} catch (err) {
console.log('Fetching prices failed: ', err);
this.logger.error('Fetching prices failed: ', err);
}

return [];
Expand All @@ -43,24 +57,57 @@ export class NordpoolApi {
*/
fetchPricesForDay = async (aDate: MomentInput, opts: NordpoolOptions): Promise<NordpoolPrices | undefined> => {
try {
const ops = [
this.getHourlyPrices(moment(aDate), opts),
];
return await this.getHourlyPrices(moment(aDate), opts);
} catch (err) {
this.logger.error('Fetching prices failed: ', err);
}

const result = await Promise.all(ops.filter(o => !!o));
return undefined;
};

return result
.filter(r => r && typeof r === 'object' && r.length > 0)
.flatMap(r => r)
.map(r => r as NordpoolPrice)
.sort((a, b) => a.time - b.time);
/**
* Fetch daily average prices for a month.
*
* @param aDate
* @param opts
*/
fetchDailyPrices = async (aDate: MomentInput, opts: NordpoolOptions): Promise<NordpoolPrices | undefined> => {
try {
return this.getDailyPrices(moment(aDate), opts);
} catch (err) {
console.log('Fetching prices failed: ', err);
this.logger.error('Fetching daily average prices failed: ', err);
}

return undefined;
};

/**
* Fetch monhtly average price for a month.
*
* @param aDate
* @param opts
*/
fetchMonthlyAverage = async (aDate: MomentInput, opts: NordpoolOptions): Promise<number | undefined> => {
try {
const dailyPrices = await this.fetchDailyPrices(moment(aDate), opts);
if (!dailyPrices) {
return undefined;
}

let sumPrice = 0;
let days = 0;
for (let row of dailyPrices) {
sumPrice += row.price;
days += 1;
}

return days > 0 ? sumPrice / days : 0;
} catch (err) {
this.logger.error('Fetching monthly average failed: ', err);
}
return undefined;
};

private getHourlyPrices = async (momnt: Moment, opts: NordpoolOptions): Promise<NordpoolPrices> => {
try {
const data = await http.json({
Expand All @@ -76,6 +123,24 @@ export class NordpoolApi {
}
};

private getDailyPrices = async (momnt: Moment, opts: NordpoolOptions): Promise<NordpoolPrices> => {
try {
const startOfMonth = momnt.startOf('month');
const startOfNextMonth = moment(startOfMonth).add(1, 'month');

const data = await http.json({
uri: 'https://www.nordpoolgroup.com/api/marketdata/page/24?' +
'currency=,' + opts.currency + ',' + opts.currency + ',' + opts.currency,
timeout: 30000
}
);
const prices = this.parseResult(data as NordpoolData, opts);
return prices.filter(p => p.startsAt.isSameOrAfter(startOfMonth) && p.startsAt.isBefore(startOfNextMonth));
} catch (err) {
throw err;
}
};

private parseResult = (data: NordpoolData, opts: NordpoolOptions): NordpoolPrices => {
const timeZone = moment().tz();
const result: NordpoolPrices = [];
Expand All @@ -99,7 +164,7 @@ export class NordpoolApi {
continue;
}

const price = Math.round(100000 * (parseFloat(column.Value.replace(/,/, '.').replace(' ', '')) / 1000.0)) / 100000;
const price = this.parsePrice(column);
if (isNaN(price)) {
continue;
}
Expand All @@ -113,4 +178,8 @@ export class NordpoolApi {
return result;
};

parsePrice = (column: NordpoolColumn): number => {
return Math.round(100000 * (parseFloat(column.Value.replace(/,/, '.').replace(' ', '')) / 1000.0)) / 100000;
}

}
64 changes: 64 additions & 0 deletions lib/PriceComparer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,4 +482,68 @@ export class PriceComparer {

return !state.high_price && highPriceNow.length === 0 || state.high_price && highPriceNow.length === 1;
}

/**
* Current price is !{{|not}} among the [[num_hours]] highest / lowest hours before [[time]]
*
* Condition to check if the current price is among the X highest / lowest hours before a time of day.
*
* @param args
* @param state
* @param curTime
*/
currentPriceAmongBeforeTimeComparer(args: { num_hours: number, time: number | string }, state: { high_price: boolean }, curTime?: Moment): boolean {
if (args.time === undefined
|| args.num_hours <= 0
|| args.num_hours >= 24) {
this.logDebug(`currentPriceAmongBeforeTimeComparer: missing params`);
return false;
}
if (!this._prices) {
this.logDebug(`currentPriceAmongBeforeTimeComparer: missing prices`);
return false;
}

const localTime = curTime ? curTime : moment();
const {startTs, endTs} = this._priceApi.daysPeriod(localTime, localTime.hour(), args.time);

//console.log(args, state, localTime, startTs, endTs);

return state.high_price ?
this._priceApi.pricesHighestInPeriod(this._prices, localTime, startTs, endTs, args.num_hours) :
this._priceApi.pricesLowestInPeriod(this._prices, localTime, startTs, endTs, args.num_hours);
}

/**
* Current price is !{{|not}} among the [[num_hours]] highest / lowest hours in the next [[next_hours]] hours
*
* Condition to check if the current price is among the X highest / lowest hours the next Y hours.
*
* @param args
* @param state
* @param curTime
*/
currentPriceAmongNextHoursComparer(args: { num_hours: number, next_hours: number }, state: { high_price: boolean }, curTime?: Moment): boolean {
if (args.num_hours <= 0
|| args.num_hours >= 24
|| args.next_hours <= 0
|| args.next_hours >= 24
) {
this.logDebug(`currentPriceAmongNextHoursComparer: missing params`);
return false;
}
if (!this._prices) {
this.logDebug(`currentPriceAmongNextHoursComparer: missing prices`);
return false;
}

const localTime = curTime ? curTime : moment();

const startTs = moment(localTime).startOf('hour');
const endTs = moment(localTime).startOf('hour').add(args.next_hours, 'hour');

return state.high_price ?
this._priceApi.pricesHighestInPeriod(this._prices, localTime, startTs, endTs, args.num_hours) :
this._priceApi.pricesLowestInPeriod(this._prices, localTime, startTs, endTs, args.num_hours);
}
}
3 changes: 2 additions & 1 deletion lib/PricesFetchClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ const STORE_PREFIX = 'prices-';
export class PricesFetchClient {

logger: any;
nordpool = new NordpoolApi();
nordpool: NordpoolApi;

constructor({logger}: {
logger: any
}) {
this.logger = logger;
this.nordpool = new NordpoolApi({logger});
}

/**
Expand Down
Loading

0 comments on commit 4375614

Please sign in to comment.