Skip to content

Commit

Permalink
feat: secondly timeframe (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
Leo4815162342 committed Oct 23, 2022
1 parent 73d64e2 commit 8d83118
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 52 deletions.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -59,7 +59,7 @@ Options:
-i, --instrument <value> Trading instrument
-from, --date-from <value> From date (yyyy-mm-dd)
-to, --date-to <value> To date (yyyy-mm-dd or 'now') (default: "now")
-t, --timeframe <value> Timeframe aggregation (tick, m1, m5, m15, m30, h1, h4, d1, mn1) (default: "d1")
-t, --timeframe <value> Timeframe aggregation (tick, s1, m1, m5, m15, m30, h1, h4, d1, mn1) (default: "d1")
-p, --price-type <value> Price type: (bid, ask) (default: "bid")
-utc, --utc-offset <value> UTC offset in minutes (default: 0)
-v, --volumes Include volumes (default: false)
Expand Down Expand Up @@ -118,7 +118,7 @@ const { getHistoricalRates } = require('dukascopy-node');
|`dates`|`Object`|true||An object with a date range|
|`dates.from`|<p>`Date`</p><p>`String`</p><p>`Number`</p>|true||Date representing the start of the time range. Can be of Date type, string (e.g. `2021-03-04` or `2021-03-04T00:00:00.000Z`), or timestamp integer (e.g. `1614816000000`)|
|`dates.to`|<p>`Date`</p><p>`String`</p><p>`Number`</p>|true||Date representing the end of the time range Can be of Date type, string (e.g. `2021-03-04` or `2021-03-04T00:00:00.000Z`), or timestamp integer (e.g. `1614816000000`)|
|`timeframe`|`String`|false|`d1`|Granularity of aggregation of OHLC (open, high, low, close) data. Supported values:<ul><li>`tick` (every single tick/price change)</li><li>`m1` (1 minute)</li><li>`m5` (5 minutes)</li><li>`m15` (15 minutes)</li><li>`m30` (30 minutes)</li><li>`h1` (1 hour)</li><li>`h4` (4 hours)</li><li>`d1` (1 day)</li><li>`mn1` (1 month)</li></ul>|
|`timeframe`|`String`|false|`d1`|Granularity of aggregation of OHLC (open, high, low, close) data. Supported values:<ul><li>`tick` (every single tick/price change)</li><li>`s1` (1 second)</li><li>`m1` (1 minute)</li><li>`m5` (5 minutes)</li><li>`m15` (15 minutes)</li><li>`m30` (30 minutes)</li><li>`h1` (1 hour)</li><li>`h4` (4 hours)</li><li>`d1` (1 day)</li><li>`mn1` (1 month)</li></ul>|
|`priceType`|`String`|false|`bid`|Type of price (offer side). Supported values:<ul><li>`bid`</li><li>`ask`</li></ul>|
|`format`|`String`|false|`array`|Format of the generated output. Supported values:<ul><li>`array`</li><li>`json`</li><li>`csv`</li></ul>|
|`utcOffset`|`Number`|false|`0`|UTC offset in minutes.|
Expand Down
108 changes: 59 additions & 49 deletions src/aggregator/index.ts
@@ -1,4 +1,9 @@
import { getOHLC, getMinuteOHLCfromTicks, getMonthlyOHLCfromDays } from './ohlc';
import {
getOHLC,
getMinuteOHLCfromTicks,
getMonthlyOHLCfromDays,
getSecondOHLCfromTicks
} from './ohlc';
import { splitArrayInChunks } from '../utils/general';
import { AggregateInput } from './types';
import { Timeframe } from '../config/timeframes';
Expand Down Expand Up @@ -30,54 +35,59 @@ export function aggregate({
);
} else {
if (fromTimeframe === Timeframe.tick) {
const minuteOHLC = getMinuteOHLCfromTicks(data, priceType, startTs, volumes);

if (toTimeframe === Timeframe.m1) {
return minuteOHLC;
}

if (toTimeframe === Timeframe.m5) {
return splitArrayInChunks(minuteOHLC, 5).map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 5 * 1000 * 60,
volumes
})
);
}

if (toTimeframe === Timeframe.m15) {
return splitArrayInChunks(minuteOHLC, 15).map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 15 * 1000 * 60,
volumes
})
);
}

if (toTimeframe === Timeframe.m30) {
return splitArrayInChunks(minuteOHLC, 30).map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 30 * 1000 * 60,
volumes
})
);
}

if (toTimeframe === Timeframe.h1) {
return [minuteOHLC].map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 60 * 1000 * 60,
volumes
})
);
if (toTimeframe === Timeframe.s1) {
const secondOHLC = getSecondOHLCfromTicks(data, priceType, startTs, volumes);
return secondOHLC;
} else {
const minuteOHLC = getMinuteOHLCfromTicks(data, priceType, startTs, volumes);

if (toTimeframe === Timeframe.m1) {
return minuteOHLC;
}

if (toTimeframe === Timeframe.m5) {
return splitArrayInChunks(minuteOHLC, 5).map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 5 * 1000 * 60,
volumes
})
);
}

if (toTimeframe === Timeframe.m15) {
return splitArrayInChunks(minuteOHLC, 15).map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 15 * 1000 * 60,
volumes
})
);
}

if (toTimeframe === Timeframe.m30) {
return splitArrayInChunks(minuteOHLC, 30).map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 30 * 1000 * 60,
volumes
})
);
}

if (toTimeframe === Timeframe.h1) {
return [minuteOHLC].map((d, i) =>
getOHLC({
input: d,
filterFlats: ignoreFlats,
startTs: startTs + i * 60 * 1000 * 60,
volumes
})
);
}
}
}

Expand Down
21 changes: 21 additions & 0 deletions src/aggregator/ohlc.ts
Expand Up @@ -168,6 +168,26 @@ function getMinuteOHLCfromTicks(
return ohlc;
}

function getSecondOHLCfromTicks(
ticks: number[][],
priceType: PriceType,
startTs: number,
volumes: boolean
): number[][] {
const ticksBySecond = breakdownByInterval(
ticks,
3600,
d => d.getUTCMinutes() * 60 + d.getUTCSeconds()
);
const ohlc = ticksBySecond.map((data, i) =>
data.length > 0
? ticksToOHLC({ ticks: data, priceType, startTs: startTs + i * 1000, volumes })
: []
);

return ohlc;
}

function getMonthlyOHLCfromDays(dailyCandles: number[][], volumes: boolean): number[][] {
const breakdown = breakdownByInterval(dailyCandles, 12, d => d.getUTCMonth());
const ohlc = breakdown.map(data => (data.length > 0 ? getOHLC({ input: data, volumes }) : []));
Expand All @@ -179,6 +199,7 @@ export {
getOHLC,
ticksToOHLC,
breakdownByInterval,
getSecondOHLCfromTicks,
getMinuteOHLCfromTicks,
getMonthlyOHLCfromDays
};
4 changes: 4 additions & 0 deletions src/config/timeframes.ts
Expand Up @@ -3,6 +3,10 @@ export enum Timeframe {
* Every single price change. No aggregation of OHLC price data
*/
tick = 'tick',
/**
* secondly `(1 second)` aggregation of OHLC price data
*/
s1 = 's1',
/**
* minutely `(1 minute)` aggregation of OHLC price data
*/
Expand Down
1 change: 1 addition & 0 deletions src/dates-normaliser/index.ts
Expand Up @@ -28,6 +28,7 @@ export function normaliseDates({
startYearForDailyCandles
} = instrumentMetaData[instrument];

// start for tick and second timeframes
let minFromIsoDate = startHourForTicks;

if (
Expand Down
2 changes: 2 additions & 0 deletions src/url-generator/index.ts
Expand Up @@ -75,7 +75,9 @@ function getDateLimit(startDate: Date, endDate: Date, timeframe: TimeframeType):

if (
timeframe === Timeframe.tick ||
timeframe === Timeframe.s1 ||
timeframe === Timeframe.m1 ||
timeframe === Timeframe.m5 ||
timeframe === Timeframe.m15 ||
timeframe === Timeframe.m30
) {
Expand Down
3 changes: 2 additions & 1 deletion src/utils/range.ts
Expand Up @@ -16,7 +16,8 @@ const rangeInferMap: RangeInferMap = {
m15: ['day', 'hour'],
m5: ['day', 'hour'],
m1: ['day', 'hour'],
tick: ['hour']
tick: ['hour'],
s1: ['hour']
};

function getLowerRange(range: TimeRange): TimeRange {
Expand Down

0 comments on commit 8d83118

Please sign in to comment.