Skip to content

Commit 5996bf5

Browse files
committed
initial request.security() support
1 parent 7a4a6e0 commit 5996bf5

File tree

7 files changed

+123
-12
lines changed

7 files changed

+123
-12
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Change Log
22

3+
## [0.1.2] - 2025-02-05 - initial request.security() support
4+
5+
### Added
6+
7+
- Support for request.security() function : in this build we only support the security() function for timeframes higher than the current timeframe, also, gaps, ignore_invalid_symbol, currency and calc_bars_count parameters are supported yet
8+
39
## [0.1.1] - 2025-02-01 - array namespace
410

511
### Added

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "pinets",
3-
"version": "0.1.1",
3+
"version": "0.1.2",
44
"description": "",
5-
"main": "dist/pinets.min.es.js",
5+
"main": "dist/pinets.dev.es.js",
66
"types": "dist/types/index.d.ts",
77
"files": [
88
"dist"

src/Context.class.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import PineMath from '@pinets/namespaces/PineMath';
77
import { PineRequest } from '@pinets/namespaces/PineRequest';
88
import TechnicalAnalysis from '@pinets/namespaces/TechnicalAnalysis';
99
import { PineArray } from './namespaces/PineArray';
10+
import { IProvider } from './marketData/IProvider';
1011

1112
export class Context {
1213
public data: any = {
@@ -19,6 +20,7 @@ export class Context {
1920
hlc3: [],
2021
ohlc4: [],
2122
};
23+
public cache: any = {};
2224

2325
public NA: any = NaN;
2426

@@ -39,13 +41,48 @@ export class Context {
3941

4042
public result: any = undefined;
4143
public plots: any = {};
44+
45+
public marketData: any;
46+
public source: IProvider | any[];
47+
public tickerId: string;
4248
public timeframe: string = '';
49+
public limit: number;
50+
public sDate: number;
51+
public eDate: number;
52+
53+
public pineTSCode: Function | String;
54+
55+
constructor({
56+
marketData,
57+
source,
58+
tickerId,
59+
timeframe,
60+
limit,
61+
sDate,
62+
eDate,
63+
}: {
64+
marketData: any;
65+
source: IProvider | any[];
66+
tickerId?: string;
67+
timeframe?: string;
68+
limit?: number;
69+
sDate?: number;
70+
eDate?: number;
71+
}) {
72+
this.marketData = marketData;
73+
this.source = source;
74+
this.tickerId = tickerId;
75+
this.timeframe = timeframe;
76+
this.limit = limit;
77+
this.sDate = sDate;
78+
this.eDate = eDate;
4379

44-
constructor(public marketData: any) {
4580
this.math = new PineMath(this);
81+
4682
this.ta = new TechnicalAnalysis(this);
4783
this.input = new Input(this);
4884
this.request = new PineRequest(this);
85+
4986
this.array = new PineArray(this);
5087
const core = new Core(this);
5188
this.core = {

src/PineTS.class.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,24 @@ export class PineTS {
9494
return this._readyPromise;
9595
}
9696

97-
public async run(fn: Function | String, n?: number) {
97+
public async run(pineTSCode: Function | String, n?: number): Promise<Context> {
9898
await this.ready();
9999
if (!n) n = this._periods;
100-
const context = new Context(this.data);
101-
context.timeframe = this.timeframe;
102100

103-
const transformer = transpile.bind(this);
101+
const context = new Context({
102+
marketData: this.data,
103+
source: this.source,
104+
tickerId: this.tickerId,
105+
timeframe: this.timeframe,
106+
limit: this.limit,
107+
sDate: this.sDate,
108+
eDate: this.eDate,
109+
});
104110

105-
let transformedFn = transformer(fn);
111+
context.pineTSCode = pineTSCode;
112+
113+
const transformer = transpile.bind(this);
114+
let transpiledFn = transformer(pineTSCode);
106115

107116
//console.log('>>> transformedFn: ', transformedFn.toString());
108117

@@ -121,7 +130,7 @@ export class PineTS {
121130
context.data.openTime = this.openTime.slice(idx);
122131
context.data.closeTime = this.closeTime.slice(idx);
123132

124-
const result = await transformedFn(context);
133+
const result = await transpiledFn(context);
125134

126135
//collect results
127136
if (typeof result === 'object') {

src/marketData/Binance/BinanceProvider.class.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const timeframe_to_binance = {
2323
import { IProvider } from '@pinets/marketData/IProvider';
2424

2525
export class BinanceProvider implements IProvider {
26+
//TODO : allow querying more than 1000 klines
27+
//TODO : immplement cache
2628
async getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<any> {
2729
try {
2830
const interval = timeframe_to_binance[timeframe.toUpperCase()];

src/marketData/Provider.class.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ import { BinanceProvider } from './Binance/BinanceProvider.class';
44

55
export const Provider = {
66
Binance: new BinanceProvider(),
7+
//TODO : add other providers (polygon, etc.)
78
};

src/namespaces/PineRequest.ts

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// SPDX-License-Identifier: AGPL-3.0-only
22

3+
import { PineTS, Provider } from '@pinets/index';
4+
//Pine Script Timeframes
5+
const TIMEFRAMES = ['1', '3', '5', '15', '30', '45', '60', '120', '180', '240', 'D', 'W', 'M'];
6+
37
export class PineRequest {
48
private _cache = {};
59
constructor(private context: any) {}
@@ -19,8 +23,60 @@ export class PineRequest {
1923
}
2024
}
2125

22-
async security(symbol: any, timeframe: any, expression: any) {
23-
//not implemented
24-
throw new Error('Not implemented');
26+
async security(
27+
symbol: any,
28+
timeframe: any,
29+
expression: any,
30+
gaps = false,
31+
lookahead = false,
32+
ignore_invalid_symbol = false,
33+
currency = null,
34+
calc_bars_count = null
35+
) {
36+
const _symbol = symbol[0];
37+
const _timeframe = timeframe[0];
38+
const _expression = expression[0];
39+
const _expression_name = expression[1];
40+
41+
const ctxTimeframeIdx = TIMEFRAMES.indexOf(this.context.timeframe);
42+
const reqTimeframeIdx = TIMEFRAMES.indexOf(_timeframe);
43+
44+
if (ctxTimeframeIdx == -1 || reqTimeframeIdx == -1) {
45+
throw new Error('Invalid timeframe');
46+
}
47+
if (ctxTimeframeIdx > reqTimeframeIdx) {
48+
throw new Error('Only higher timeframes are supported for now');
49+
}
50+
51+
if (ctxTimeframeIdx === reqTimeframeIdx) {
52+
return _expression;
53+
}
54+
55+
const myOpenTime = this.context.data.openTime[0];
56+
const myCloseTime = this.context.data.closeTime[0];
57+
58+
if (this.context.cache[_expression_name]) {
59+
const secContext = this.context.cache[_expression_name];
60+
const secContextIdx = this._findSecContextIdx(myOpenTime, myCloseTime, secContext.data.openTime, secContext.data.closeTime, lookahead);
61+
return secContextIdx == -1 ? NaN : secContext.params[_expression_name][secContextIdx];
62+
}
63+
64+
const pineTS = new PineTS(this.context.source, _symbol, _timeframe, this.context.limit || 1000, this.context.sDate, this.context.eDate);
65+
66+
const secContext = await pineTS.run(this.context.pineTSCode);
67+
68+
this.context.cache[_expression_name] = secContext;
69+
70+
const secContextIdx = this._findSecContextIdx(myOpenTime, myCloseTime, secContext.data.openTime, secContext.data.closeTime, lookahead);
71+
return secContextIdx == -1 ? NaN : secContext.params[_expression_name][secContextIdx];
72+
}
73+
74+
private _findSecContextIdx(myOpenTime: number, myCloseTime: number, openTime: number[], closeTime: number[], lookahead = false) {
75+
for (let i = 0; i < openTime.length; i++) {
76+
if (openTime[i] <= myOpenTime && myCloseTime <= closeTime[i]) {
77+
return i + (lookahead ? 1 : 2); //lookahead_on +1 lookahead_off +2
78+
}
79+
}
80+
return -1;
2581
}
2682
}

0 commit comments

Comments
 (0)