pinets 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/pinets.min.browser.es.js +18 -18
- package/dist/pinets.min.browser.es.js.map +1 -1
- package/dist/pinets.min.browser.js +18 -18
- package/dist/pinets.min.browser.js.map +1 -1
- package/dist/pinets.min.cjs +18 -18
- package/dist/pinets.min.cjs.map +1 -1
- package/dist/pinets.min.es.js +20 -20
- package/dist/pinets.min.es.js.map +1 -1
- package/dist/types/Context.class.d.ts +2 -0
- package/dist/types/PineTS.class.d.ts +17 -0
- package/dist/types/errors/PineRuntimeError.d.ts +27 -0
- package/dist/types/index.d.ts +10 -1
- package/dist/types/marketData/Alpaca/AlpacaProvider.class.d.ts +115 -0
- package/dist/types/marketData/BaseProvider.d.ts +92 -0
- package/dist/types/marketData/Binance/BinanceProvider.class.d.ts +15 -5
- package/dist/types/marketData/FMP/FMPProvider.class.d.ts +69 -0
- package/dist/types/marketData/IProvider.d.ts +26 -1
- package/dist/types/marketData/Mock/MockProvider.class.d.ts +12 -22
- package/dist/types/marketData/Provider.class.d.ts +4 -1
- package/dist/types/marketData/aggregation.d.ts +38 -0
- package/dist/types/marketData/types.d.ts +106 -0
- package/dist/types/namespaces/Log.d.ts +1 -0
- package/dist/types/transpiler/analysis/ScopeManager.d.ts +2 -0
- package/dist/types/transpiler/pineToJS/codegen.d.ts +1 -1
- package/dist/types/transpiler/transformers/StatementTransformer.d.ts +10 -0
- package/package.json +4 -4
|
@@ -31,6 +31,23 @@ export declare class PineTS {
|
|
|
31
31
|
private _isSecondaryContext;
|
|
32
32
|
markAsSecondary(): void;
|
|
33
33
|
private _syminfo;
|
|
34
|
+
private _chartTimezone;
|
|
35
|
+
/**
|
|
36
|
+
* Set the chart display timezone (like TradingView's timezone picker).
|
|
37
|
+
* This only affects log timestamp formatting — it does NOT change the timezone
|
|
38
|
+
* used by computation functions (timestamp(), dayofmonth, hour, etc.), which
|
|
39
|
+
* always use the exchange timezone from syminfo.timezone.
|
|
40
|
+
* @param timezone IANA timezone name (e.g. 'America/New_York'), UTC offset ('UTC+5'), or 'UTC'
|
|
41
|
+
*/
|
|
42
|
+
setTimezone(timezone: string): void;
|
|
43
|
+
private _maxLoops;
|
|
44
|
+
/**
|
|
45
|
+
* Set the maximum number of iterations allowed per loop.
|
|
46
|
+
* Mirrors TradingView's internal loop protection. If a for/while loop
|
|
47
|
+
* exceeds this limit, a runtime error is thrown.
|
|
48
|
+
* @param maxLoops Maximum iterations per loop (default: 500000)
|
|
49
|
+
*/
|
|
50
|
+
setMaxLoops(maxLoops: number): void;
|
|
34
51
|
constructor(source: IProvider | any[], tickerId?: string, timeframe?: string, limit?: number, sDate?: number, eDate?: number);
|
|
35
52
|
setDebugSettings({ ln, debug }: {
|
|
36
53
|
ln: boolean;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime error thrown by PineTS when a Pine Script runtime violation occurs.
|
|
3
|
+
* Mirrors TradingView behavior where operations like out-of-bounds array/matrix
|
|
4
|
+
* access or infinite loops halt the script with a runtime error.
|
|
5
|
+
*
|
|
6
|
+
* Consumers can catch this specific error type to distinguish Pine runtime
|
|
7
|
+
* errors from general JavaScript errors:
|
|
8
|
+
*
|
|
9
|
+
* ```typescript
|
|
10
|
+
* try {
|
|
11
|
+
* const result = await pineTS.run(code);
|
|
12
|
+
* } catch (err) {
|
|
13
|
+
* if (err instanceof PineRuntimeError) {
|
|
14
|
+
* console.log('Pine runtime error:', err.message);
|
|
15
|
+
* console.log('Method:', err.method); // e.g. 'array.get'
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare class PineRuntimeError extends Error {
|
|
21
|
+
/**
|
|
22
|
+
* The Pine Script method that caused the error (e.g. 'array.get', 'matrix.set').
|
|
23
|
+
* May be undefined for non-method errors (e.g. loop guard).
|
|
24
|
+
*/
|
|
25
|
+
method?: string;
|
|
26
|
+
constructor(message: string, method?: string);
|
|
27
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,4 +2,13 @@ import PineTS from './PineTS.class';
|
|
|
2
2
|
import { Context } from './Context.class';
|
|
3
3
|
import { Provider } from './marketData/Provider.class';
|
|
4
4
|
import { Indicator } from './Indicator';
|
|
5
|
-
|
|
5
|
+
import { PineRuntimeError } from './errors/PineRuntimeError';
|
|
6
|
+
export { BaseProvider } from './marketData/BaseProvider';
|
|
7
|
+
export { BinanceProvider } from './marketData/Binance/BinanceProvider.class';
|
|
8
|
+
export { FMPProvider } from './marketData/FMP/FMPProvider.class';
|
|
9
|
+
export { AlpacaProvider } from './marketData/Alpaca/AlpacaProvider.class';
|
|
10
|
+
export type { IProvider, ISymbolInfo, BaseProviderConfig, ApiKeyProviderConfig } from './marketData/IProvider';
|
|
11
|
+
export type { Kline, PeriodType } from './marketData/types';
|
|
12
|
+
export { computeNextPeriodStart, localTimeToUTC, computeSessionClose, TIMEFRAME_SECONDS, TIMEFRAME_PERIOD_INFO } from './marketData/types';
|
|
13
|
+
export { aggregateCandles, selectSubTimeframe, getAggregationRatio } from './marketData/aggregation';
|
|
14
|
+
export { PineTS, Context, Provider, Indicator, PineRuntimeError };
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ISymbolInfo, ApiKeyProviderConfig } from '@pinets/marketData/IProvider';
|
|
2
|
+
import { BaseProvider } from '@pinets/marketData/BaseProvider';
|
|
3
|
+
import { Kline } from '@pinets/marketData/types';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration for AlpacaProvider.
|
|
6
|
+
*
|
|
7
|
+
* @property apiKey - Alpaca API Key ID
|
|
8
|
+
* @property apiSecret - Alpaca API Secret Key
|
|
9
|
+
* @property paper - Use paper trading endpoint for asset info (default: true)
|
|
10
|
+
* @property feed - Market data feed: 'sip' (paid, full market) or 'iex' (free tier). Default: 'sip'
|
|
11
|
+
* @property dataUrl - Override the market data base URL
|
|
12
|
+
* @property tradingUrl - Override the trading/asset API base URL
|
|
13
|
+
*/
|
|
14
|
+
export interface AlpacaProviderConfig extends ApiKeyProviderConfig {
|
|
15
|
+
apiSecret: string;
|
|
16
|
+
paper?: boolean;
|
|
17
|
+
feed?: 'sip' | 'iex';
|
|
18
|
+
dataUrl?: string;
|
|
19
|
+
tradingUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Alpaca Markets data provider.
|
|
23
|
+
*
|
|
24
|
+
* Supports US stocks and crypto via Alpaca's Market Data API v2.
|
|
25
|
+
* All timeframes (1Min through 1Month) are natively supported.
|
|
26
|
+
*
|
|
27
|
+
* ## Usage
|
|
28
|
+
*
|
|
29
|
+
* ### Direct instantiation:
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const alpaca = new AlpacaProvider({
|
|
32
|
+
* apiKey: 'PK...',
|
|
33
|
+
* apiSecret: '...',
|
|
34
|
+
* });
|
|
35
|
+
* const pineTS = new PineTS(alpaca, 'AAPL', 'D', null, sDate, eDate);
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* ### Via Provider registry:
|
|
39
|
+
* ```typescript
|
|
40
|
+
* Provider.Alpaca.configure({ apiKey: 'PK...', apiSecret: '...' });
|
|
41
|
+
* const pineTS = new PineTS(Provider.Alpaca, 'AAPL', 'D', null, sDate, eDate);
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* ## API Keys
|
|
45
|
+
* Get free API keys at https://alpaca.markets/
|
|
46
|
+
* Free tier provides IEX data; paid plan adds SIP (full market) data.
|
|
47
|
+
*
|
|
48
|
+
* ## Symbol Format
|
|
49
|
+
* - Stocks: `AAPL`, `MSFT`, `SPY`
|
|
50
|
+
* - Crypto: `BTC/USD`, `ETH/USD` (slash notation)
|
|
51
|
+
*/
|
|
52
|
+
export declare class AlpacaProvider extends BaseProvider<AlpacaProviderConfig> {
|
|
53
|
+
private _apiKey;
|
|
54
|
+
private _apiSecret;
|
|
55
|
+
private _dataUrl;
|
|
56
|
+
private _tradingUrl;
|
|
57
|
+
private _feed;
|
|
58
|
+
private _assetCache;
|
|
59
|
+
/** Calendar cache: date string "YYYY-MM-DD" → { open, close } times. */
|
|
60
|
+
private _calendarCache;
|
|
61
|
+
constructor(config?: AlpacaProviderConfig);
|
|
62
|
+
configure(config: AlpacaProviderConfig): void;
|
|
63
|
+
private _headers;
|
|
64
|
+
protected getSupportedTimeframes(): Set<string>;
|
|
65
|
+
protected _getMarketDataNative(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]>;
|
|
66
|
+
/**
|
|
67
|
+
* Fetch all bars with automatic pagination.
|
|
68
|
+
*/
|
|
69
|
+
private _fetchAllBars;
|
|
70
|
+
/**
|
|
71
|
+
* Build the bars URL for stocks or crypto.
|
|
72
|
+
*/
|
|
73
|
+
private _buildBarsUrl;
|
|
74
|
+
getSymbolInfo(tickerId: string): Promise<ISymbolInfo>;
|
|
75
|
+
/**
|
|
76
|
+
* Convert bars for crypto (24/7 — no session boundaries).
|
|
77
|
+
* closeTime = next bar's openTime, or openTime + period for last bar.
|
|
78
|
+
*/
|
|
79
|
+
private _convertBarsCrypto;
|
|
80
|
+
/**
|
|
81
|
+
* Convert bars for stocks using the Alpaca trading calendar.
|
|
82
|
+
* closeTime = exact session close from the calendar (handles early closes, DST).
|
|
83
|
+
*/
|
|
84
|
+
private _convertBarsStock;
|
|
85
|
+
/** Build a Kline from an AlpacaBar + computed times. */
|
|
86
|
+
private _toKline;
|
|
87
|
+
/**
|
|
88
|
+
* Find the last trading day of the week containing `barDate` and return
|
|
89
|
+
* its session close time in UTC ms.
|
|
90
|
+
*/
|
|
91
|
+
private _weeklyCloseFromCalendar;
|
|
92
|
+
/**
|
|
93
|
+
* Find the last trading day of the month containing `barDate` and return
|
|
94
|
+
* its session close time in UTC ms.
|
|
95
|
+
*/
|
|
96
|
+
private _monthlyCloseFromCalendar;
|
|
97
|
+
/**
|
|
98
|
+
* Ensure the calendar cache covers the given date range.
|
|
99
|
+
* Fetches from Alpaca's `GET /v2/calendar` endpoint, which returns
|
|
100
|
+
* per-day trading hours including early closes (data from 1970-2029).
|
|
101
|
+
*/
|
|
102
|
+
private _ensureCalendar;
|
|
103
|
+
private _fetchAsset;
|
|
104
|
+
/**
|
|
105
|
+
* Parse an Alpaca timeframe string (e.g., '1Min', '4Hour', '1Month')
|
|
106
|
+
* into a PeriodType and multiplier for calendar-aware date math.
|
|
107
|
+
*/
|
|
108
|
+
private _parseAlpacaTimeframe;
|
|
109
|
+
/** Resolve PineTS timeframe to Alpaca timeframe string. */
|
|
110
|
+
private _resolveTimeframe;
|
|
111
|
+
/** Heuristic: crypto tickers contain '/'. */
|
|
112
|
+
private _isCrypto;
|
|
113
|
+
/** Add N days to a "YYYY-MM-DD" date string. */
|
|
114
|
+
private _addDaysToDate;
|
|
115
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { IProvider, ISymbolInfo, BaseProviderConfig } from './IProvider';
|
|
2
|
+
import { Kline } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base class for market data providers.
|
|
5
|
+
*
|
|
6
|
+
* Provides shared logic: closeTime normalization, fail-early API key
|
|
7
|
+
* validation, and **automatic candle aggregation** for unsupported
|
|
8
|
+
* timeframes.
|
|
9
|
+
*
|
|
10
|
+
* ## Aggregation
|
|
11
|
+
*
|
|
12
|
+
* When a provider doesn't natively support a timeframe, `getMarketData()`
|
|
13
|
+
* automatically:
|
|
14
|
+
* 1. Selects the best sub-timeframe the provider supports
|
|
15
|
+
* 2. Fetches sub-candles via `_getMarketDataNative()`
|
|
16
|
+
* 3. Aggregates them into the requested timeframe
|
|
17
|
+
*
|
|
18
|
+
* Providers declare native support via `getSupportedTimeframes()` and
|
|
19
|
+
* implement `_getMarketDataNative()` for the actual API call.
|
|
20
|
+
*
|
|
21
|
+
* ## Usage
|
|
22
|
+
*
|
|
23
|
+
* ```typescript
|
|
24
|
+
* class MyProvider extends BaseProvider<MyConfig> {
|
|
25
|
+
* protected getSupportedTimeframes() {
|
|
26
|
+
* return new Set(['1', '5', '15', '60', 'D']);
|
|
27
|
+
* }
|
|
28
|
+
* protected async _getMarketDataNative(...) { ... }
|
|
29
|
+
* }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare abstract class BaseProvider<TConfig extends BaseProviderConfig = BaseProviderConfig> implements IProvider {
|
|
33
|
+
private _configured;
|
|
34
|
+
private _requiresApiKey;
|
|
35
|
+
private _providerName;
|
|
36
|
+
private _aggregationSubTimeframe;
|
|
37
|
+
constructor(options: {
|
|
38
|
+
requiresApiKey: boolean;
|
|
39
|
+
providerName: string;
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Fail-early check — call at the top of `_getMarketDataNative()` / `getSymbolInfo()`
|
|
43
|
+
* in providers that require an API key.
|
|
44
|
+
*/
|
|
45
|
+
protected ensureConfigured(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Base configure — marks the provider as configured.
|
|
48
|
+
* Subclasses override to store their specific config, and must call `super.configure(config)`.
|
|
49
|
+
*/
|
|
50
|
+
configure(config: TConfig): void;
|
|
51
|
+
/** Whether this provider has been configured (always true for keyless providers). */
|
|
52
|
+
get isConfigured(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Shared closeTime normalization utility.
|
|
55
|
+
* Delegates to the standalone `normalizeCloseTime()` from `types.ts`.
|
|
56
|
+
*/
|
|
57
|
+
protected normalizeCloseTime(data: Kline[]): void;
|
|
58
|
+
/**
|
|
59
|
+
* Override the sub-timeframe used for aggregation.
|
|
60
|
+
* When set, this timeframe is used instead of auto-selecting the best divisor.
|
|
61
|
+
* Set to `null` to re-enable automatic selection.
|
|
62
|
+
*/
|
|
63
|
+
setAggregationSubTimeframe(subTimeframe: string | null): void;
|
|
64
|
+
/**
|
|
65
|
+
* Return the set of timeframes this provider supports natively.
|
|
66
|
+
*
|
|
67
|
+
* Override in subclasses. Default: all canonical timeframes (no aggregation).
|
|
68
|
+
* Use canonical keys: '1','3','5','15','30','45','60','120','180','240','D','W','M'
|
|
69
|
+
* and optionally second-based: '1S','5S','10S','15S','30S'.
|
|
70
|
+
*/
|
|
71
|
+
protected getSupportedTimeframes(): Set<string>;
|
|
72
|
+
/**
|
|
73
|
+
* Fetch market data — delegates to native fetch or aggregates from sub-candles.
|
|
74
|
+
*
|
|
75
|
+
* 1. If the timeframe is natively supported, delegates to `_getMarketDataNative()`.
|
|
76
|
+
* 2. Otherwise, selects the best sub-timeframe, fetches sub-candles, and aggregates.
|
|
77
|
+
*/
|
|
78
|
+
getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]>;
|
|
79
|
+
/**
|
|
80
|
+
* Fetch market data natively from the provider's API.
|
|
81
|
+
*
|
|
82
|
+
* Subclasses MUST implement this. It is called by the BaseProvider
|
|
83
|
+
* orchestrator either for the requested timeframe (if natively supported)
|
|
84
|
+
* or for a sub-candle timeframe (if aggregation is needed).
|
|
85
|
+
*/
|
|
86
|
+
protected abstract _getMarketDataNative(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]>;
|
|
87
|
+
abstract getSymbolInfo(tickerId: string): Promise<ISymbolInfo>;
|
|
88
|
+
/**
|
|
89
|
+
* Compute how many sub-candles to fetch to produce `limit` aggregated candles.
|
|
90
|
+
*/
|
|
91
|
+
private _computeSubLimit;
|
|
92
|
+
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { ISymbolInfo } from '@pinets/marketData/IProvider';
|
|
2
|
+
import { BaseProvider } from '@pinets/marketData/BaseProvider';
|
|
3
|
+
import { Kline } from '@pinets/marketData/types';
|
|
4
|
+
/** Config for BinanceProvider (no API key needed). */
|
|
5
|
+
export interface BinanceProviderConfig {
|
|
6
|
+
}
|
|
7
|
+
export declare class BinanceProvider extends BaseProvider<BinanceProviderConfig> {
|
|
3
8
|
private cacheManager;
|
|
4
9
|
private activeApiUrl;
|
|
5
10
|
constructor();
|
|
@@ -9,13 +14,18 @@ export declare class BinanceProvider implements IProvider {
|
|
|
9
14
|
* Caches the working endpoint for future calls.
|
|
10
15
|
*/
|
|
11
16
|
private getBaseUrl;
|
|
12
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Fetch a single chunk of raw kline data from the Binance API (no closeTime normalization).
|
|
19
|
+
* Used internally by pagination methods that assemble chunks before normalizing.
|
|
20
|
+
*/
|
|
21
|
+
private _fetchRawChunk;
|
|
22
|
+
getMarketDataInterval(tickerId: string, timeframe: string, sDate: number, eDate: number): Promise<Kline[]>;
|
|
13
23
|
private getMarketDataBackwards;
|
|
14
|
-
|
|
24
|
+
protected getSupportedTimeframes(): Set<string>;
|
|
25
|
+
protected _getMarketDataNative(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]>;
|
|
15
26
|
/**
|
|
16
27
|
* Determines if pagination is needed based on the parameters
|
|
17
28
|
*/
|
|
18
29
|
private shouldPaginate;
|
|
19
30
|
getSymbolInfo(tickerId: string): Promise<ISymbolInfo>;
|
|
20
|
-
configure(config: any): void;
|
|
21
31
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ISymbolInfo, ApiKeyProviderConfig } from '@pinets/marketData/IProvider';
|
|
2
|
+
import { BaseProvider } from '@pinets/marketData/BaseProvider';
|
|
3
|
+
import { Kline } from '@pinets/marketData/types';
|
|
4
|
+
/** Configuration for FMPProvider — requires an API key. */
|
|
5
|
+
export interface FMPProviderConfig extends ApiKeyProviderConfig {
|
|
6
|
+
/** Optional: override the base URL (e.g. for proxy or self-hosted). */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Financial Modeling Prep (FMP) market data provider.
|
|
11
|
+
*
|
|
12
|
+
* Supports stocks, ETFs, crypto, and forex via FMP's stable API.
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ### Direct instantiation:
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const fmp = new FMPProvider({ apiKey: 'your-key' });
|
|
19
|
+
* const pineTS = new PineTS(fmp, 'AAPL', 'D', null, sDate, eDate);
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* ### Via Provider registry:
|
|
23
|
+
* ```typescript
|
|
24
|
+
* Provider.FMP.configure({ apiKey: 'your-key' });
|
|
25
|
+
* const pineTS = new PineTS(Provider.FMP, 'AAPL', 'D', null, sDate, eDate);
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* ## API Key
|
|
29
|
+
* Get a free API key (250 req/day) at https://financialmodelingprep.com/
|
|
30
|
+
* Intraday data (1min, 5min, 15min, 30min, 1h, 4h) requires a paid plan.
|
|
31
|
+
*
|
|
32
|
+
* ## Symbol Format
|
|
33
|
+
* Use standard ticker symbols: `AAPL`, `MSFT`, `SPY`, `BTCUSD`, `EURUSD`
|
|
34
|
+
*/
|
|
35
|
+
export declare class FMPProvider extends BaseProvider<FMPProviderConfig> {
|
|
36
|
+
private _apiKey;
|
|
37
|
+
private _baseUrl;
|
|
38
|
+
private _profileCache;
|
|
39
|
+
constructor(config?: FMPProviderConfig);
|
|
40
|
+
configure(config: FMPProviderConfig): void;
|
|
41
|
+
protected getSupportedTimeframes(): Set<string>;
|
|
42
|
+
protected _getMarketDataNative(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]>;
|
|
43
|
+
/**
|
|
44
|
+
* Fetch daily EOD data from FMP and convert to Kline format.
|
|
45
|
+
*/
|
|
46
|
+
private _fetchDailyData;
|
|
47
|
+
/**
|
|
48
|
+
* Fetch intraday chart data from FMP and convert to Kline format.
|
|
49
|
+
* Note: Requires a paid FMP plan.
|
|
50
|
+
*/
|
|
51
|
+
private _fetchIntradayData;
|
|
52
|
+
getSymbolInfo(tickerId: string): Promise<ISymbolInfo>;
|
|
53
|
+
private _fetchProfile;
|
|
54
|
+
/**
|
|
55
|
+
* Resolve session string and timezone for a ticker by fetching its profile.
|
|
56
|
+
* Falls back to NYSE defaults if profile is unavailable.
|
|
57
|
+
*/
|
|
58
|
+
private _resolveSessionInfo;
|
|
59
|
+
/** Convert ms timestamp to FMP date string "YYYY-MM-DD". */
|
|
60
|
+
private _msToDateStr;
|
|
61
|
+
/** Convert FMP date string "YYYY-MM-DD" to ms timestamp (UTC midnight). */
|
|
62
|
+
private _dateStrToMs;
|
|
63
|
+
/** Convert FMP datetime string "YYYY-MM-DD HH:MM:SS" to ms timestamp. */
|
|
64
|
+
private _dateTimeStrToMs;
|
|
65
|
+
/** Heuristic: crypto tickers end with USD/USDT/BTC/ETH. */
|
|
66
|
+
private _isCrypto;
|
|
67
|
+
/** Heuristic: forex pairs are 6 chars, two 3-letter currency codes. */
|
|
68
|
+
private _isForex;
|
|
69
|
+
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import type { Kline } from './types';
|
|
2
|
+
/** Base config — all providers extend this (may be empty for keyless providers). */
|
|
3
|
+
export interface BaseProviderConfig {
|
|
4
|
+
}
|
|
5
|
+
/** Config for providers that require an API key (FMP, Alpaca, etc.). */
|
|
6
|
+
export interface ApiKeyProviderConfig extends BaseProviderConfig {
|
|
7
|
+
apiKey: string;
|
|
8
|
+
}
|
|
1
9
|
export type ISymbolInfo = {
|
|
2
10
|
current_contract: string;
|
|
3
11
|
description: string;
|
|
@@ -40,8 +48,25 @@ export type ISymbolInfo = {
|
|
|
40
48
|
target_price_low: number;
|
|
41
49
|
target_price_median: number;
|
|
42
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Market data provider interface.
|
|
53
|
+
*
|
|
54
|
+
* ## closeTime convention
|
|
55
|
+
* Providers MUST return `closeTime` as the **session close time** for the bar,
|
|
56
|
+
* mirroring TradingView's `time_close` built-in variable.
|
|
57
|
+
*
|
|
58
|
+
* - **Stocks / regulated markets**: `closeTime` = the session close time on
|
|
59
|
+
* the bar's trading day (e.g., 16:00 ET for NYSE daily bars, 13:00 ET for
|
|
60
|
+
* early-close days). For weekly/monthly bars, use the session close of the
|
|
61
|
+
* last trading day in the period.
|
|
62
|
+
* - **24/7 markets (crypto)**: `closeTime` = the start of the next bar
|
|
63
|
+
* (equivalent to `openTime + barDuration`), since there are no session gaps.
|
|
64
|
+
*
|
|
65
|
+
* Use `computeSessionClose()` from `types.ts` for session-aware computation,
|
|
66
|
+
* or the Alpaca Calendar API for exact per-day close times including early closes.
|
|
67
|
+
*/
|
|
43
68
|
export interface IProvider {
|
|
44
|
-
getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<
|
|
69
|
+
getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]>;
|
|
45
70
|
getSymbolInfo(tickerId: string): Promise<ISymbolInfo>;
|
|
46
71
|
configure(config: any): void;
|
|
47
72
|
}
|
|
@@ -1,17 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
close: number;
|
|
8
|
-
volume: number;
|
|
9
|
-
closeTime: number;
|
|
10
|
-
quoteAssetVolume: number;
|
|
11
|
-
numberOfTrades: number;
|
|
12
|
-
takerBuyBaseAssetVolume: number;
|
|
13
|
-
takerBuyQuoteAssetVolume: number;
|
|
14
|
-
ignore: number | string;
|
|
1
|
+
import { ISymbolInfo } from '@pinets/marketData/IProvider';
|
|
2
|
+
import { BaseProvider } from '@pinets/marketData/BaseProvider';
|
|
3
|
+
import { Kline } from '@pinets/marketData/types';
|
|
4
|
+
/** Config for MockProvider. */
|
|
5
|
+
export interface MockProviderConfig {
|
|
6
|
+
dataDirectory?: string;
|
|
15
7
|
}
|
|
16
8
|
/**
|
|
17
9
|
* Mock Market Data Provider for Unit Tests
|
|
@@ -30,14 +22,12 @@ interface Kline {
|
|
|
30
22
|
*
|
|
31
23
|
* Example: BTCUSDC-1h-1704067200000-1763683199000.json
|
|
32
24
|
*/
|
|
33
|
-
export declare class MockProvider
|
|
25
|
+
export declare class MockProvider extends BaseProvider<MockProviderConfig> {
|
|
34
26
|
private dataCache;
|
|
35
27
|
private exchangeInfoCache;
|
|
36
28
|
private dataDirectory;
|
|
37
|
-
constructor(
|
|
38
|
-
configure(
|
|
39
|
-
dataDirectory?: string;
|
|
40
|
-
}): void;
|
|
29
|
+
constructor(dataDirectoryOrConfig?: string | MockProviderConfig);
|
|
30
|
+
configure(config: MockProviderConfig): void;
|
|
41
31
|
/**
|
|
42
32
|
* Generates a cache key for the data file
|
|
43
33
|
*/
|
|
@@ -58,8 +48,9 @@ export declare class MockProvider implements IProvider {
|
|
|
58
48
|
* Normalizes timeframe to match file naming convention
|
|
59
49
|
*/
|
|
60
50
|
private normalizeTimeframe;
|
|
51
|
+
protected getSupportedTimeframes(): Set<string>;
|
|
61
52
|
/**
|
|
62
|
-
* Implements
|
|
53
|
+
* Implements _getMarketDataNative
|
|
63
54
|
*
|
|
64
55
|
* @param tickerId - Symbol (e.g., 'BTCUSDC')
|
|
65
56
|
* @param timeframe - Timeframe (e.g., '1h', '60', 'D')
|
|
@@ -68,7 +59,7 @@ export declare class MockProvider implements IProvider {
|
|
|
68
59
|
* @param eDate - Optional end date (timestamp in milliseconds)
|
|
69
60
|
* @returns Promise<Kline[]> - Array of candle data
|
|
70
61
|
*/
|
|
71
|
-
|
|
62
|
+
protected _getMarketDataNative(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<Kline[]>;
|
|
72
63
|
/**
|
|
73
64
|
* Loads exchange info from JSON file
|
|
74
65
|
*/
|
|
@@ -89,4 +80,3 @@ export declare class MockProvider implements IProvider {
|
|
|
89
80
|
*/
|
|
90
81
|
setDataDirectory(directory: string): void;
|
|
91
82
|
}
|
|
92
|
-
export {};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { IProvider } from './IProvider';
|
|
2
|
+
export { BinanceProvider } from './Binance/BinanceProvider.class';
|
|
3
|
+
export { FMPProvider } from './FMP/FMPProvider.class';
|
|
4
|
+
export { AlpacaProvider } from './Alpaca/AlpacaProvider.class';
|
|
5
|
+
export { BaseProvider } from './BaseProvider';
|
|
2
6
|
type TProvider = {
|
|
3
7
|
[key: string]: IProvider;
|
|
4
8
|
};
|
|
5
9
|
export declare const Provider: TProvider;
|
|
6
10
|
export declare function registerProvider(name: string, provider: IProvider): void;
|
|
7
|
-
export {};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Kline } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Given a target timeframe and a set of supported timeframes, select the
|
|
4
|
+
* best sub-timeframe to aggregate from.
|
|
5
|
+
*
|
|
6
|
+
* Strategy:
|
|
7
|
+
* - **W/M targets**: always use `'D'` (calendar-based grouping).
|
|
8
|
+
* - **All others**: pick the largest supported timeframe whose duration
|
|
9
|
+
* evenly divides the target duration (using `TIMEFRAME_SECONDS`).
|
|
10
|
+
*
|
|
11
|
+
* @returns The best sub-timeframe, or `null` if none found.
|
|
12
|
+
*/
|
|
13
|
+
export declare function selectSubTimeframe(targetTimeframe: string, supportedTimeframes: Set<string>): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Compute how many sub-candles fit into one aggregated candle.
|
|
16
|
+
*
|
|
17
|
+
* For fixed-duration aggregation: `targetSeconds / subSeconds`.
|
|
18
|
+
* For calendar-based (W/M from D): returns `Infinity` to signal variable grouping.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getAggregationRatio(targetTimeframe: string, subTimeframe: string): number;
|
|
21
|
+
/**
|
|
22
|
+
* Aggregate sub-candles into higher-timeframe candles.
|
|
23
|
+
*
|
|
24
|
+
* Three modes:
|
|
25
|
+
* 1. **Fixed-ratio** (intraday → higher intraday): groups every N consecutive
|
|
26
|
+
* sub-candles, with session-boundary detection to avoid cross-session merging.
|
|
27
|
+
* 2. **Weekly from daily**: groups daily bars by ISO week number.
|
|
28
|
+
* 3. **Monthly from daily**: groups daily bars by calendar year+month.
|
|
29
|
+
*
|
|
30
|
+
* OHLCV merge:
|
|
31
|
+
* - `open` = first sub-candle's open
|
|
32
|
+
* - `high` = max of all highs
|
|
33
|
+
* - `low` = min of all lows
|
|
34
|
+
* - `close` = last sub-candle's close
|
|
35
|
+
* - `volume` = sum
|
|
36
|
+
* - `closeTime` = last sub-candle's closeTime (preserves session-aware close)
|
|
37
|
+
*/
|
|
38
|
+
export declare function aggregateCandles(subCandles: Kline[], targetTimeframe: string, subTimeframe: string): Kline[];
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standardized candlestick / kline data shape used by all providers.
|
|
3
|
+
*/
|
|
4
|
+
export interface Kline {
|
|
5
|
+
openTime: number;
|
|
6
|
+
open: number;
|
|
7
|
+
high: number;
|
|
8
|
+
low: number;
|
|
9
|
+
close: number;
|
|
10
|
+
volume: number;
|
|
11
|
+
closeTime: number;
|
|
12
|
+
quoteAssetVolume: number;
|
|
13
|
+
numberOfTrades: number;
|
|
14
|
+
takerBuyBaseAssetVolume: number;
|
|
15
|
+
takerBuyQuoteAssetVolume: number;
|
|
16
|
+
ignore: number | string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Interval duration in milliseconds, keyed by normalized interval strings.
|
|
20
|
+
* Used by providers for pagination and date-range estimation.
|
|
21
|
+
*
|
|
22
|
+
* These use Binance-style interval keys ('1m', '1h', '1d', etc.)
|
|
23
|
+
* which are also the de-facto standard across most market data APIs.
|
|
24
|
+
*/
|
|
25
|
+
export declare const INTERVAL_DURATION_MS: Record<string, number>;
|
|
26
|
+
/**
|
|
27
|
+
* Period types for timeframe-aware date arithmetic.
|
|
28
|
+
*/
|
|
29
|
+
export type PeriodType = 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month';
|
|
30
|
+
/**
|
|
31
|
+
* Duration in seconds for each canonical timeframe.
|
|
32
|
+
*
|
|
33
|
+
* Uses seconds (not minutes) to naturally accommodate TradingView's
|
|
34
|
+
* sub-minute timeframes ('1S', '5S', etc.) without fractional values.
|
|
35
|
+
* D/W/M values are approximate — used for ratio math, not calendar grouping.
|
|
36
|
+
*/
|
|
37
|
+
export declare const TIMEFRAME_SECONDS: Record<string, number>;
|
|
38
|
+
/**
|
|
39
|
+
* Map from canonical timeframe to { periodType, multiplier }.
|
|
40
|
+
* Used by aggregation to determine grouping strategy.
|
|
41
|
+
*/
|
|
42
|
+
export declare const TIMEFRAME_PERIOD_INFO: Record<string, {
|
|
43
|
+
periodType: PeriodType;
|
|
44
|
+
multiplier: number;
|
|
45
|
+
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Compute the start of the next period given an openTime (fixed duration math).
|
|
48
|
+
*
|
|
49
|
+
* For intraday / daily / weekly: adds fixed duration.
|
|
50
|
+
* For monthly: uses calendar math to land on the 1st of the next month.
|
|
51
|
+
*
|
|
52
|
+
* **Suitable for 24/7 crypto markets** where there are no session gaps.
|
|
53
|
+
* For stock/regulated markets, prefer `computeSessionClose()` or
|
|
54
|
+
* the Alpaca Calendar API which account for session boundaries,
|
|
55
|
+
* early closes, and holidays.
|
|
56
|
+
*
|
|
57
|
+
* @param openTimeMs - The bar's open time in epoch milliseconds
|
|
58
|
+
* @param periodType - The period unit ('minute', 'hour', 'day', 'week', 'month')
|
|
59
|
+
* @param multiplier - How many units per bar (e.g., 3 for 3Min, 4 for 4Hour). Default: 1
|
|
60
|
+
*/
|
|
61
|
+
export declare function computeNextPeriodStart(openTimeMs: number, periodType: PeriodType, multiplier?: number): number;
|
|
62
|
+
/**
|
|
63
|
+
* Convert a local date + time string in a given IANA timezone to UTC milliseconds.
|
|
64
|
+
*
|
|
65
|
+
* Uses `Intl.DateTimeFormat` for DST-correct conversion — no external dependencies.
|
|
66
|
+
*
|
|
67
|
+
* @param dateStr - Date in "YYYY-MM-DD" format
|
|
68
|
+
* @param timeStr - Time in "HH:MM" format (24h)
|
|
69
|
+
* @param timezone - IANA timezone name (e.g. "America/New_York", "Etc/UTC")
|
|
70
|
+
* @returns UTC epoch milliseconds
|
|
71
|
+
*/
|
|
72
|
+
export declare function localTimeToUTC(dateStr: string, timeStr: string, timezone: string): number;
|
|
73
|
+
/**
|
|
74
|
+
* Compute the session close time for a bar.
|
|
75
|
+
*
|
|
76
|
+
* For providers without a per-day calendar API (like FMP), this computes
|
|
77
|
+
* closeTime from the session string and exchange timezone.
|
|
78
|
+
*
|
|
79
|
+
* Logic by period type:
|
|
80
|
+
* - **24x7 session**: closeTime = openTime + barDuration (no gaps)
|
|
81
|
+
* - **Intraday** (minute, hour): min(openTime + barDuration, sessionEndOnThatDay)
|
|
82
|
+
* - **Daily** (day): same date at session end time in timezone
|
|
83
|
+
* - **Weekly** (week): Friday of that week at session end (approximation: no holiday calendar)
|
|
84
|
+
* - **Monthly** (month): last weekday of month at session end (approximation: no holiday calendar)
|
|
85
|
+
*
|
|
86
|
+
* @param openTimeMs - Bar open time in UTC epoch milliseconds
|
|
87
|
+
* @param session - Session string, e.g. "0930-1600" or "24x7"
|
|
88
|
+
* @param timezone - IANA timezone name, e.g. "America/New_York"
|
|
89
|
+
* @param periodType - The period unit
|
|
90
|
+
* @param multiplier - How many units per bar (default: 1)
|
|
91
|
+
*/
|
|
92
|
+
export declare function computeSessionClose(openTimeMs: number, session: string, timezone: string, periodType: PeriodType, multiplier?: number): number;
|
|
93
|
+
/**
|
|
94
|
+
* Normalize closeTime for 24/7 crypto providers (Binance, Mock).
|
|
95
|
+
*
|
|
96
|
+
* Many crypto APIs (e.g. Binance) return closeTime as `nextBarOpen - 1ms`.
|
|
97
|
+
* For 24/7 markets, `nextBar.openTime == sessionClose`, so this is correct:
|
|
98
|
+
* - For bars 0..N-2: closeTime = next bar's openTime (exact for 24/7)
|
|
99
|
+
* - For the last bar: closeTime = raw closeTime + 1ms (best estimate)
|
|
100
|
+
*
|
|
101
|
+
* **Not suitable for stock/regulated market providers** — those must use
|
|
102
|
+
* `computeSessionClose()` or the Alpaca Calendar API for session-aware close times.
|
|
103
|
+
*
|
|
104
|
+
* Mutates the array in place.
|
|
105
|
+
*/
|
|
106
|
+
export declare function normalizeCloseTime(data: Kline[]): void;
|
|
@@ -4,6 +4,7 @@ export declare class Log {
|
|
|
4
4
|
constructor(context: Context);
|
|
5
5
|
private logFormat;
|
|
6
6
|
param(source: any, index?: number, name?: string): any;
|
|
7
|
+
private _formatTimestamp;
|
|
7
8
|
warning(message: string, ...args: any[]): void;
|
|
8
9
|
error(message: string, ...args: any[]): void;
|
|
9
10
|
info(message: string, ...args: any[]): void;
|