@hoox-sh/pynets 0.2.0 → 0.4.0
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/CHANGELOG.md +56 -0
- package/README.md +21 -12
- package/dist/browser.js +89 -84
- package/dist/index.js +21011 -12972
- package/package.json +1 -1
- package/src/ast/builder.ts +235 -12
- package/src/ast/nodes.ts +114 -4
- package/src/ast/unparse.ts +192 -17
- package/src/cli/rich.ts +1 -1
- package/src/generated/PinescriptLexer.interp +5 -1
- package/src/generated/PinescriptLexer.tokens +97 -95
- package/src/generated/PinescriptLexer.ts +512 -502
- package/src/generated/PinescriptParser.interp +8 -1
- package/src/generated/PinescriptParser.tokens +97 -95
- package/src/generated/PinescriptParser.ts +1907 -1287
- package/src/generated/PinescriptParserVisitor.ts +36 -1
- package/src/index.ts +1 -0
- package/src/runtime/array.ts +238 -68
- package/src/runtime/compile/collections.ts +191 -34
- package/src/runtime/compile/draw.ts +609 -19
- package/src/runtime/compile/emit.ts +346 -9
- package/src/runtime/compile/emit_call.ts +737 -44
- package/src/runtime/compile/runtime.ts +82 -2
- package/src/runtime/compile/strategy.ts +339 -6
- package/src/runtime/compile/types.ts +2 -0
- package/src/runtime/drawings.ts +405 -8
- package/src/runtime/interpret.ts +2352 -277
- package/src/runtime/matrix.ts +371 -42
- package/src/runtime/strategy.ts +695 -33
- package/src/runtime/ta.ts +1046 -20
- package/src/runtime/timeframe.ts +199 -0
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
mathMin,
|
|
33
33
|
mathPow,
|
|
34
34
|
mathRound,
|
|
35
|
+
mathRoundToMintick,
|
|
35
36
|
mathSign,
|
|
36
37
|
mathSin,
|
|
37
38
|
mathSqrt,
|
|
@@ -58,7 +59,8 @@ import { LogBook, formatLogParts } from "../log.ts";
|
|
|
58
59
|
import { resolveCurrencyRate } from "../request.ts";
|
|
59
60
|
import { TaEngine } from "../ta.ts";
|
|
60
61
|
import { utcPartsFromMs, timestamp, weekOfYear, timeTradingDay, type UtcParts } from "../time.ts";
|
|
61
|
-
import {
|
|
62
|
+
import { TickerId } from "../ticker.ts";
|
|
63
|
+
import { timeframeInSeconds, timeframePeriodChanged } from "../timeframe.ts";
|
|
62
64
|
import { compileArray, compileMap, compileMatrix } from "./collections.ts";
|
|
63
65
|
import { createCompileDraw } from "./draw.ts";
|
|
64
66
|
import {
|
|
@@ -81,6 +83,7 @@ import {
|
|
|
81
83
|
safeNeg,
|
|
82
84
|
safeSub,
|
|
83
85
|
} from "./helpers.ts";
|
|
86
|
+
import { isStrategyCashAmount } from "../strategy.ts";
|
|
84
87
|
import { createCompileStrategy } from "./strategy.ts";
|
|
85
88
|
import { udtGet, udtNew, udtRegister, udtSet } from "./udt_runtime.ts";
|
|
86
89
|
|
|
@@ -94,10 +97,17 @@ export function createCompileHelpers(opts?: CompileHelperOpts) {
|
|
|
94
97
|
const ta = new TaEngine();
|
|
95
98
|
const inputs = opts?.inputs;
|
|
96
99
|
const book = new LogBook();
|
|
100
|
+
// Python `timeframe_in_seconds` defaults None / "" to "D" (daily, 86400)
|
|
101
|
+
// before parsing — builtins/timeframe.py:189-190. The shared helper in
|
|
102
|
+
// timeframe.ts maps na / empty → null, so compile wraps it here to match.
|
|
103
|
+
const timeframeInSecondsOrDaily = (p: string | null): number | null =>
|
|
104
|
+
timeframeInSeconds(p == null || p === "" ? "D" : p);
|
|
97
105
|
return {
|
|
98
106
|
na: NA_FN,
|
|
99
107
|
nz,
|
|
100
108
|
naNum,
|
|
109
|
+
plotNum,
|
|
110
|
+
timenow,
|
|
101
111
|
isNa: isNaCell,
|
|
102
112
|
eq: pineEq,
|
|
103
113
|
ne: pineNe,
|
|
@@ -174,7 +184,11 @@ export function createCompileHelpers(opts?: CompileHelperOpts) {
|
|
|
174
184
|
timestamp,
|
|
175
185
|
weekOfYear,
|
|
176
186
|
timeTradingDay,
|
|
177
|
-
timeframeInSeconds,
|
|
187
|
+
timeframeInSeconds: timeframeInSecondsOrDaily,
|
|
188
|
+
timeframeChange,
|
|
189
|
+
timeClose,
|
|
190
|
+
roundToMintick,
|
|
191
|
+
syminfoPrefix,
|
|
178
192
|
strLength,
|
|
179
193
|
strContains,
|
|
180
194
|
strStartsWith,
|
|
@@ -253,6 +267,23 @@ function iff(cond: unknown, thenV: unknown, elseV: unknown): unknown {
|
|
|
253
267
|
return mathIff(cond as number | null, thenV as number | null, elseV as number | null);
|
|
254
268
|
}
|
|
255
269
|
|
|
270
|
+
/** Plot cell: tagged `strategy.cash` unwraps to its number (interpret `unwrap`). */
|
|
271
|
+
function plotNum(v: unknown): number | null {
|
|
272
|
+
if (isStrategyCashAmount(v)) return Number.isFinite(v.value) ? v.value : null;
|
|
273
|
+
return naNum(v);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Dataset last-bar time on every bar. Interpret `beginBar` stores that on
|
|
278
|
+
* `ctx.timenow` (host seed is overwritten). Not wall-clock, not current bar.
|
|
279
|
+
*/
|
|
280
|
+
function timenow(timeArr: unknown): number {
|
|
281
|
+
if (!Array.isArray(timeArr) || timeArr.length === 0) return 0;
|
|
282
|
+
const last = timeArr[timeArr.length - 1];
|
|
283
|
+
if (typeof last === "number" && Number.isFinite(last)) return last;
|
|
284
|
+
return (timeArr.length - 1) * 60_000;
|
|
285
|
+
}
|
|
286
|
+
|
|
256
287
|
function NA_FN(v?: unknown): number | null {
|
|
257
288
|
if (arguments.length === 0) return null;
|
|
258
289
|
return isNaCell(v) ? 1 : 0;
|
|
@@ -295,3 +326,52 @@ function calendarDayofweek(t: unknown): number | null {
|
|
|
295
326
|
function logBar(bar: unknown): number {
|
|
296
327
|
return typeof bar === "number" && Number.isFinite(bar) ? bar : 0;
|
|
297
328
|
}
|
|
329
|
+
|
|
330
|
+
/** `timeframe.change`: bar 0 is a new period; empty / unknown tf → false. */
|
|
331
|
+
function timeframeChange(tf: unknown, curr: unknown, prev: unknown, barIndex: unknown): number {
|
|
332
|
+
const s =
|
|
333
|
+
typeof tf === "string"
|
|
334
|
+
? tf
|
|
335
|
+
: typeof tf === "number" && Number.isFinite(tf)
|
|
336
|
+
? String(tf)
|
|
337
|
+
: null;
|
|
338
|
+
if (s == null || s.trim() === "") return 0;
|
|
339
|
+
if (typeof curr !== "number" || !Number.isFinite(curr)) return 0;
|
|
340
|
+
const idx = typeof barIndex === "number" && Number.isFinite(barIndex) ? barIndex : null;
|
|
341
|
+
return timeframePeriodChanged(curr, prev, s, idx) ? 1 : 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Next bar open, last bar `time + 86400000`. `0` next-open counts as missing
|
|
346
|
+
* (interpret beginBar). Compile has no per-bar `time_close` column.
|
|
347
|
+
*/
|
|
348
|
+
function timeClose(barIdx: unknown, timeArr: unknown): number {
|
|
349
|
+
const i = typeof barIdx === "number" && Number.isFinite(barIdx) ? Math.trunc(barIdx) : 0;
|
|
350
|
+
const arr = Array.isArray(timeArr) ? (timeArr as Array<number | null | undefined>) : [];
|
|
351
|
+
const t = arr[i];
|
|
352
|
+
const tNum = typeof t === "number" && Number.isFinite(t) ? t : 0;
|
|
353
|
+
if (i + 1 < arr.length) {
|
|
354
|
+
const next = arr[i + 1] ?? 0;
|
|
355
|
+
return next !== 0 ? next : tNum;
|
|
356
|
+
}
|
|
357
|
+
return Math.trunc(tNum) + 86_400_000;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function roundToMintick(x: unknown): number | null {
|
|
361
|
+
return mathRoundToMintick(naNum(x), 0.01);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/** Python `split_symbol` / `extract_prefix`: text before the first `:`, else `""`. */
|
|
365
|
+
function extractPrefix(symbol: string): string {
|
|
366
|
+
const s = symbol.trim();
|
|
367
|
+
const i = s.indexOf(":");
|
|
368
|
+
return i < 0 ? "" : s.slice(0, i);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function syminfoPrefix(tickerid?: unknown): string {
|
|
372
|
+
if (arguments.length === 0 || tickerid === undefined) return extractPrefix("SYMBOL");
|
|
373
|
+
if (tickerid instanceof TickerId) return extractPrefix(tickerid.symbol);
|
|
374
|
+
if (typeof tickerid === "string") return extractPrefix(tickerid);
|
|
375
|
+
if (typeof tickerid === "number" && Number.isFinite(tickerid)) return extractPrefix(String(tickerid));
|
|
376
|
+
return "";
|
|
377
|
+
}
|
|
@@ -6,13 +6,19 @@
|
|
|
6
6
|
* Injected as `__h.strategy`. Wraps StrategyBook (fills + ledger + risk).
|
|
7
7
|
*/
|
|
8
8
|
import {
|
|
9
|
+
isStrategyCashAmount,
|
|
10
|
+
strategyCashAmount,
|
|
9
11
|
StrategyBook,
|
|
12
|
+
type BarOhlc,
|
|
10
13
|
type BrokerSettings,
|
|
11
14
|
type PlaceEntryOpts,
|
|
15
|
+
type PlaceExitOpts,
|
|
16
|
+
type StrategyCashAmount,
|
|
12
17
|
type StrategyDirection,
|
|
13
18
|
type StrategyEvent,
|
|
14
19
|
type StrategySummary,
|
|
15
20
|
} from "../strategy.ts";
|
|
21
|
+
import { naNum } from "./helpers.ts";
|
|
16
22
|
|
|
17
23
|
function asDir(v: unknown): StrategyDirection | number | string {
|
|
18
24
|
if (v == null) return "long";
|
|
@@ -36,16 +42,72 @@ function str(v: unknown, fallback = ""): string {
|
|
|
36
42
|
return String(v);
|
|
37
43
|
}
|
|
38
44
|
|
|
45
|
+
function optNum(v: unknown): number | null {
|
|
46
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function asExitOpts(v: unknown): PlaceExitOpts {
|
|
51
|
+
if (v == null || typeof v !== "object" || Array.isArray(v)) return {};
|
|
52
|
+
const rec = v as Record<string, unknown>;
|
|
53
|
+
return {
|
|
54
|
+
from_entry: rec.from_entry == null || rec.from_entry === "" ? null : String(rec.from_entry),
|
|
55
|
+
qty: optNum(rec.qty),
|
|
56
|
+
qty_percent: optNum(rec.qty_percent),
|
|
57
|
+
profit: optNum(rec.profit),
|
|
58
|
+
limit: optNum(rec.limit),
|
|
59
|
+
loss: optNum(rec.loss),
|
|
60
|
+
stop: optNum(rec.stop),
|
|
61
|
+
trail_price: optNum(rec.trail_price),
|
|
62
|
+
trail_points: optNum(rec.trail_points),
|
|
63
|
+
trail_offset: optNum(rec.trail_offset),
|
|
64
|
+
comment: rec.comment == null ? undefined : String(rec.comment),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
39
68
|
export class CompileStrategy {
|
|
40
69
|
readonly book: StrategyBook;
|
|
41
70
|
private bar = 0;
|
|
42
71
|
private mark = 0;
|
|
43
72
|
private time = 0;
|
|
73
|
+
private ohlc: BarOhlc = {};
|
|
44
74
|
|
|
45
75
|
constructor(settings?: BrokerSettings) {
|
|
46
76
|
this.book = new StrategyBook(settings);
|
|
47
77
|
}
|
|
48
78
|
|
|
79
|
+
/** Apply `strategy(...)` declaration kwargs (every bar, same as interpret). */
|
|
80
|
+
configure(settings?: unknown): void {
|
|
81
|
+
if (settings == null || typeof settings !== "object" || Array.isArray(settings)) return;
|
|
82
|
+
const rec = settings as Record<string, unknown>;
|
|
83
|
+
const broker: BrokerSettings = {};
|
|
84
|
+
const commission = optNum(rec.commission);
|
|
85
|
+
if (commission != null) broker.commission = commission;
|
|
86
|
+
const slippage = optNum(rec.slippage);
|
|
87
|
+
if (slippage != null) broker.slippage = slippage;
|
|
88
|
+
if (rec.pyramiding !== undefined) {
|
|
89
|
+
const pyr = optNum(rec.pyramiding);
|
|
90
|
+
broker.pyramiding = pyr == null ? undefined : Math.trunc(pyr);
|
|
91
|
+
}
|
|
92
|
+
if (typeof rec.avg_price_model === "string") broker.avg_price_model = rec.avg_price_model;
|
|
93
|
+
const leverage = optNum(rec.leverage);
|
|
94
|
+
if (leverage != null) broker.leverage = leverage;
|
|
95
|
+
const ml = optNum(rec.margin_long);
|
|
96
|
+
if (ml != null) broker.margin_long = ml;
|
|
97
|
+
const ms = optNum(rec.margin_short);
|
|
98
|
+
if (ms != null) broker.margin_short = ms;
|
|
99
|
+
// String sentinel or tagged free-cash (`strategy.cash`) both select cash sizing.
|
|
100
|
+
if (typeof rec.default_qty_type === "string" || isStrategyCashAmount(rec.default_qty_type)) {
|
|
101
|
+
broker.default_qty_type = rec.default_qty_type;
|
|
102
|
+
}
|
|
103
|
+
const dqv = optNum(rec.default_qty_value);
|
|
104
|
+
if (dqv != null) broker.default_qty_value = dqv;
|
|
105
|
+
if (typeof rec.currency === "string" && rec.currency !== "") broker.currency = rec.currency;
|
|
106
|
+
if (Object.keys(broker).length) this.book.configure(broker);
|
|
107
|
+
const capital = optNum(rec.initial_capital);
|
|
108
|
+
if (capital != null) this.book.initialCapital = capital;
|
|
109
|
+
}
|
|
110
|
+
|
|
49
111
|
beginBar(
|
|
50
112
|
bar: unknown,
|
|
51
113
|
open: unknown,
|
|
@@ -63,14 +125,16 @@ export class CompileStrategy {
|
|
|
63
125
|
low: num(low, this.mark),
|
|
64
126
|
close: this.mark,
|
|
65
127
|
};
|
|
128
|
+
this.ohlc = ohlc;
|
|
66
129
|
this.book.processPending(this.bar, ohlc);
|
|
67
130
|
this.book.markOpenTrades(ohlc.high, ohlc.low, ohlc.close);
|
|
68
131
|
}
|
|
69
132
|
|
|
70
133
|
entry(id?: unknown, direction?: unknown, qty?: unknown, opts?: PlaceEntryOpts): void {
|
|
71
|
-
const
|
|
134
|
+
const fillPrice = opts?.price ?? this.mark;
|
|
135
|
+
const q = qty == null ? this.book.resolveDefaultQty(fillPrice) : num(qty, 1);
|
|
72
136
|
this.book.placeEntry(this.bar, str(id, "entry"), asDir(direction), q, {
|
|
73
|
-
price:
|
|
137
|
+
price: fillPrice,
|
|
74
138
|
time: opts?.time ?? this.time,
|
|
75
139
|
limit: opts?.limit,
|
|
76
140
|
stop: opts?.stop,
|
|
@@ -80,8 +144,11 @@ export class CompileStrategy {
|
|
|
80
144
|
});
|
|
81
145
|
}
|
|
82
146
|
|
|
83
|
-
close(id?: unknown,
|
|
84
|
-
this.book.fillClose(this.bar, str(id, ""), this.mark, {
|
|
147
|
+
close(id?: unknown, qty?: unknown): void {
|
|
148
|
+
this.book.fillClose(this.bar, str(id, ""), this.mark, {
|
|
149
|
+
time: this.time,
|
|
150
|
+
qty: optNum(qty),
|
|
151
|
+
});
|
|
85
152
|
}
|
|
86
153
|
|
|
87
154
|
close_all(_comment?: unknown): void {
|
|
@@ -100,8 +167,14 @@ export class CompileStrategy {
|
|
|
100
167
|
this.book.cancelAll(this.bar);
|
|
101
168
|
}
|
|
102
169
|
|
|
103
|
-
exit(
|
|
104
|
-
|
|
170
|
+
exit(id?: unknown, opts?: unknown): void {
|
|
171
|
+
const parsed = asExitOpts(opts);
|
|
172
|
+
this.book.placeExit(this.bar, str(id, "exit"), {
|
|
173
|
+
...parsed,
|
|
174
|
+
price: this.mark,
|
|
175
|
+
time: this.time,
|
|
176
|
+
ohlc: this.ohlc,
|
|
177
|
+
});
|
|
105
178
|
}
|
|
106
179
|
|
|
107
180
|
position_size(): number {
|
|
@@ -131,6 +204,266 @@ export class CompileStrategy {
|
|
|
131
204
|
return this.book.closedtrades;
|
|
132
205
|
}
|
|
133
206
|
|
|
207
|
+
leverage(): number {
|
|
208
|
+
return this.book.leverage;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
margin_liquidation_price(): number | null {
|
|
212
|
+
return this.book.marginLiquidationPrice();
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
position_avg_price(): number | null {
|
|
216
|
+
return this.book.position.avgPrice;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
initial_capital(): number {
|
|
220
|
+
return this.book.initialCapital;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/** Free cash tagged so `default_qty_type=strategy.cash` still selects cash sizing. */
|
|
224
|
+
cash(): StrategyCashAmount {
|
|
225
|
+
return strategyCashAmount(this.book.cash(this.mark));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
position_entry_name(): string {
|
|
229
|
+
return this.book.position_entry_name;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
account_currency(): string {
|
|
233
|
+
return this.book.account_currency;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Zero-arg form is `percent_equity=100` (interpret `strategy.default_entry_qty`). */
|
|
237
|
+
default_entry_qty(percent?: unknown): number | null {
|
|
238
|
+
const pct = arguments.length === 0 || percent === undefined ? 100 : naNum(percent);
|
|
239
|
+
if (pct == null) return null;
|
|
240
|
+
return (this.book.initialCapital * (pct / 100)) / 100;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Identity FX stub. Missing arg → 1; na / non-finite → na. */
|
|
244
|
+
convert_to(value?: unknown): number | null {
|
|
245
|
+
if (arguments.length === 0 || value === undefined) return 1;
|
|
246
|
+
return naNum(value);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
max_contracts_held_all(): number {
|
|
250
|
+
return this.book.maxContractsHeldAll();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
max_contracts_held_long(): number {
|
|
254
|
+
return this.book.maxContractsHeldLong();
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
max_contracts_held_short(): number {
|
|
258
|
+
return this.book.maxContractsHeldShort();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
capital_held(): number {
|
|
262
|
+
return this.book.capitalHeld();
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
closedtrades_first_index(): number {
|
|
266
|
+
return this.book.closedtrades_first_index;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
wintrades(): number {
|
|
270
|
+
return this.book.wintrades;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
losstrades(): number {
|
|
274
|
+
return this.book.losstrades;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
eventrades(): number {
|
|
278
|
+
return this.book.eventrades;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
grossprofit(): number {
|
|
282
|
+
return this.book.grossprofit;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
grossloss(): number {
|
|
286
|
+
return this.book.grossloss;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
avg_trade(): number {
|
|
290
|
+
return this.book.avgTrade();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
avg_winning_trade(): number {
|
|
294
|
+
return this.book.avgWinningTrade();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
avg_losing_trade(): number {
|
|
298
|
+
return this.book.avgLosingTrade();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
netprofit_percent(): number {
|
|
302
|
+
return this.book.netprofitPercent(this.mark);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
openprofit_percent(): number {
|
|
306
|
+
return this.book.openprofitPercent(this.mark);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
grossprofit_percent(): number {
|
|
310
|
+
return this.book.grossprofitPercent();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
grossloss_percent(): number {
|
|
314
|
+
return this.book.grosslossPercent();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
avg_trade_percent(): number {
|
|
318
|
+
return this.book.avgTradePercent();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
avg_winning_trade_percent(): number {
|
|
322
|
+
return this.book.avgWinningTradePercent();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
avg_losing_trade_percent(): number {
|
|
326
|
+
return this.book.avgLosingTradePercent();
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
max_drawdown(): number {
|
|
330
|
+
return this.book.maxDrawdown();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
max_drawdown_percent(): number {
|
|
334
|
+
return this.book.maxDrawdownPercent();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
max_runup(): number {
|
|
338
|
+
return this.book.maxRunup();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
max_runup_percent(): number {
|
|
342
|
+
return this.book.maxRunupPercent();
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
percent_profitable(): number {
|
|
346
|
+
return this.book.percentProfitable();
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
winrate(): number {
|
|
350
|
+
return this.book.percentProfitable();
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
profitfactor(): number {
|
|
354
|
+
return this.book.profitFactor();
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
profit_factor(): number {
|
|
358
|
+
return this.book.profitFactor();
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
closedtrades_entry_bar_index(i?: unknown): number {
|
|
362
|
+
return this.book.closedEntryBar(this.idx(i)) ?? 0;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
closedtrades_entry_price(i?: unknown): number {
|
|
366
|
+
return this.book.closedEntryPrice(this.idx(i)) ?? 0;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
closedtrades_exit_bar_index(i?: unknown): number {
|
|
370
|
+
return this.book.closedExitBar(this.idx(i)) ?? 0;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
closedtrades_exit_price(i?: unknown): number {
|
|
374
|
+
return this.book.closedExitPrice(this.idx(i)) ?? 0;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
closedtrades_profit(i?: unknown): number {
|
|
378
|
+
return this.book.closedProfit(this.idx(i)) ?? 0;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
closedtrades_size(i?: unknown): number {
|
|
382
|
+
return this.book.closedSize(this.idx(i)) ?? 0;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
closedtrades_entry_id(i?: unknown): string {
|
|
386
|
+
return this.book.closedId(this.idx(i)) ?? "";
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
closedtrades_exit_id(i?: unknown): string {
|
|
390
|
+
return this.book.closedId(this.idx(i)) ?? "";
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
closedtrades_commission(i?: unknown): number {
|
|
394
|
+
return this.book.closedCommission(this.idx(i)) ?? 0;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
closedtrades_entry_time(i?: unknown): number {
|
|
398
|
+
return this.book.closedEntryTime(this.idx(i));
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
closedtrades_exit_time(i?: unknown): number {
|
|
402
|
+
return this.book.closedExitTime(this.idx(i));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
closedtrades_entry_comment(i?: unknown): string {
|
|
406
|
+
return this.book.closedEntryComment(this.idx(i));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
closedtrades_exit_comment(i?: unknown): string {
|
|
410
|
+
return this.book.closedExitComment(this.idx(i));
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
closedtrades_max_drawdown(i?: unknown): number {
|
|
414
|
+
return this.book.closedMaxDrawdown(this.idx(i));
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
closedtrades_max_runup(i?: unknown): number {
|
|
418
|
+
return this.book.closedMaxRunup(this.idx(i));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
opentrades_entry_bar_index(i?: unknown): number {
|
|
422
|
+
return this.book.openEntryBar(this.idx(i)) ?? 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
opentrades_entry_price(i?: unknown): number {
|
|
426
|
+
return this.book.openEntryPrice(this.idx(i)) ?? 0;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
opentrades_size(i?: unknown): number {
|
|
430
|
+
return this.book.openSize(this.idx(i)) ?? 0;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
opentrades_entry_id(i?: unknown): string {
|
|
434
|
+
return this.book.openId(this.idx(i)) ?? "";
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
opentrades_profit(i?: unknown): number {
|
|
438
|
+
return this.book.openProfit(this.idx(i), this.mark) ?? 0;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
opentrades_entry_time(i?: unknown): number {
|
|
442
|
+
return this.book.openEntryTime(this.idx(i));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
opentrades_entry_comment(i?: unknown): string {
|
|
446
|
+
return this.book.openEntryComment(this.idx(i));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
opentrades_commission(i?: unknown): number {
|
|
450
|
+
return this.book.openCommission(this.idx(i));
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
opentrades_max_drawdown(i?: unknown): number {
|
|
454
|
+
return this.book.openMaxDrawdown(this.idx(i));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
opentrades_max_runup(i?: unknown): number {
|
|
458
|
+
return this.book.openMaxRunup(this.idx(i));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
private idx(i: unknown): number {
|
|
462
|
+
if (typeof i === "boolean") return i ? 1 : 0;
|
|
463
|
+
if (typeof i === "number" && Number.isFinite(i)) return Math.trunc(i);
|
|
464
|
+
return 0;
|
|
465
|
+
}
|
|
466
|
+
|
|
134
467
|
risk_allow_entry_in(value?: unknown): void {
|
|
135
468
|
this.book.riskAllowEntryIn(value);
|
|
136
469
|
}
|
|
@@ -58,6 +58,8 @@ export interface EmitCtx {
|
|
|
58
58
|
usesStrategy: boolean;
|
|
59
59
|
nextSite: number;
|
|
60
60
|
errors: string[];
|
|
61
|
+
/** `once`-structure fired flags, declared in the bar-loop frame. */
|
|
62
|
+
onceFlags: string[];
|
|
61
63
|
/** Pine UDF name currently being emitted (null at script scope). */
|
|
62
64
|
currentFunc: string | null;
|
|
63
65
|
/** Formal names of the current UDF. */
|