@hoox-sh/pynets 0.5.0 → 0.7.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 +35 -0
- package/README.md +6 -4
- package/dist/browser.js +68 -68
- package/dist/index.js +2551 -205
- package/package.json +1 -1
- package/src/cli/rich.ts +1 -1
- package/src/generated/PinescriptLexerBase.ts +22 -4
- package/src/parser/LexerBase.ts +22 -4
- package/src/runtime/compile/draw.ts +26 -2
- package/src/runtime/compile/emit_call.ts +485 -6
- package/src/runtime/compile/helpers.ts +2 -1
- package/src/runtime/compile/runtime.ts +20 -5
- package/src/runtime/compile/strategy.ts +5 -2
- package/src/runtime/interpret.ts +680 -41
- package/src/runtime/str.ts +3 -2
- package/src/runtime/strategy.ts +164 -12
- package/src/runtime/ta.ts +1667 -149
- package/src/runtime/time.ts +12 -0
package/src/runtime/interpret.ts
CHANGED
|
@@ -114,7 +114,7 @@ import {
|
|
|
114
114
|
mathToDegrees,
|
|
115
115
|
mathToRadians,
|
|
116
116
|
} from "./math.ts";
|
|
117
|
-
import { utcPartsFromMs, timestamp, timeTradingDay, weekOfYear } from "./time.ts";
|
|
117
|
+
import { utcPartsFromMs, timestamp, timestampToMs, timeTradingDay, weekOfYear } from "./time.ts";
|
|
118
118
|
import {
|
|
119
119
|
LibraryModule,
|
|
120
120
|
LibraryRegistry,
|
|
@@ -588,6 +588,7 @@ export function interpretTree(
|
|
|
588
588
|
high: num(env.ctx.high) ?? undefined,
|
|
589
589
|
low: num(env.ctx.low) ?? undefined,
|
|
590
590
|
close: num(env.ctx.close) ?? undefined,
|
|
591
|
+
time: num(env.ctx.time) ?? undefined,
|
|
591
592
|
});
|
|
592
593
|
env.book.markOpenTrades(
|
|
593
594
|
num(env.ctx.high) ?? num(env.ctx.close) ?? 0,
|
|
@@ -937,6 +938,35 @@ function seriesGet(env: Env, id: string): PineSeries | undefined {
|
|
|
937
938
|
return env.series.get(id);
|
|
938
939
|
}
|
|
939
940
|
|
|
941
|
+
/** Bare series names Python's evaluator exposes as `PineSeries` objects. */
|
|
942
|
+
const BARE_SERIES_NAMES = new Set([
|
|
943
|
+
"open",
|
|
944
|
+
"high",
|
|
945
|
+
"low",
|
|
946
|
+
"close",
|
|
947
|
+
"volume",
|
|
948
|
+
"hl2",
|
|
949
|
+
"hlc3",
|
|
950
|
+
"ohlc4",
|
|
951
|
+
"hlcc4",
|
|
952
|
+
"time",
|
|
953
|
+
"time_close",
|
|
954
|
+
]);
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* True when an argument expression is a series reference rather than a scalar.
|
|
958
|
+
* Python handlers that do `args[0] if isinstance(args[0], list) else [args[0]]`
|
|
959
|
+
* (e.g. `ta.parkinson`, `ta.garman_klass`) see the `PineSeries` object in bar
|
|
960
|
+
* mode and therefore return `na`.
|
|
961
|
+
*/
|
|
962
|
+
function isSeriesExpr(value: expr, env: Env): boolean {
|
|
963
|
+
if (value.kind === "Name") {
|
|
964
|
+
if (BARE_SERIES_NAMES.has(value.id)) return true;
|
|
965
|
+
return seriesGet(env, value.id) != null;
|
|
966
|
+
}
|
|
967
|
+
return false;
|
|
968
|
+
}
|
|
969
|
+
|
|
940
970
|
function bindName(env: Env, id: string, value: Value): void {
|
|
941
971
|
const stored: Value = typeof value === "number" && !Number.isFinite(value) ? NA : value;
|
|
942
972
|
const frame = activeFrame(env);
|
|
@@ -2075,6 +2105,39 @@ function evalCall(node: Call, env: Env): Value {
|
|
|
2075
2105
|
evalAsString(callArg(node.args, 3, ["adjust", "adjustment"]), env) ?? undefined,
|
|
2076
2106
|
});
|
|
2077
2107
|
}
|
|
2108
|
+
if (fname === "ticker.inherit") {
|
|
2109
|
+
const first = evalExpr(callArg(node.args, 0, ["ticker", "tickerid", "from", "source"]), env);
|
|
2110
|
+
const secondArg = callArg(node.args, 1, ["symbol", "ticker"]);
|
|
2111
|
+
if (secondArg != null) {
|
|
2112
|
+
const target = evalExpr(secondArg, env);
|
|
2113
|
+
const sym =
|
|
2114
|
+
target instanceof TickerId
|
|
2115
|
+
? target.symbol
|
|
2116
|
+
: typeof target === "string"
|
|
2117
|
+
? target
|
|
2118
|
+
: env.symbol;
|
|
2119
|
+
if (first instanceof TickerId) return tickerNew(sym, first.session, first.adjust);
|
|
2120
|
+
return tickerNew(sym);
|
|
2121
|
+
}
|
|
2122
|
+
if (first instanceof TickerId) {
|
|
2123
|
+
return tickerNew(first.symbol, first.session, first.adjust);
|
|
2124
|
+
}
|
|
2125
|
+
const sym = typeof first === "string" ? first : env.symbol;
|
|
2126
|
+
return tickerNew(sym);
|
|
2127
|
+
}
|
|
2128
|
+
if (
|
|
2129
|
+
fname === "renko" ||
|
|
2130
|
+
fname === "kagi" ||
|
|
2131
|
+
fname === "linebreak" ||
|
|
2132
|
+
fname === "pointfigure"
|
|
2133
|
+
) {
|
|
2134
|
+
const raw = evalExpr(callArg(node.args, 0, ["ticker", "tickerid", "symbol"]), env);
|
|
2135
|
+
const sym = raw instanceof TickerId ? raw.symbol : typeof raw === "string" ? raw : env.symbol;
|
|
2136
|
+
if (fname === "renko") return tickerRenko(sym);
|
|
2137
|
+
if (fname === "kagi") return tickerKagi(sym);
|
|
2138
|
+
if (fname === "linebreak") return tickerLinebreak(sym);
|
|
2139
|
+
return tickerPointfigure(sym);
|
|
2140
|
+
}
|
|
2078
2141
|
const mapVal = evalMapCall(fname, node, env);
|
|
2079
2142
|
if (mapVal !== undefined) return mapVal;
|
|
2080
2143
|
const matrixVal = evalMatrixCall(fname, node, env);
|
|
@@ -2107,6 +2170,7 @@ function evalCall(node: Call, env: Env): Value {
|
|
|
2107
2170
|
time: num(env.ctx.time) ?? undefined,
|
|
2108
2171
|
oca_name: evalAsString(callArg(node.args, -1, ["oca_name"]), env),
|
|
2109
2172
|
oca_type: evalAsString(callArg(node.args, -1, ["oca_type"]), env),
|
|
2173
|
+
deferMarket: true,
|
|
2110
2174
|
});
|
|
2111
2175
|
return NA;
|
|
2112
2176
|
}
|
|
@@ -2115,12 +2179,10 @@ function evalCall(node: Call, env: Env): Value {
|
|
|
2115
2179
|
const mark = num(env.ctx.close);
|
|
2116
2180
|
const qtyExpr = callArg(node.args, 1, ["qty"]);
|
|
2117
2181
|
const qtyRaw = qtyExpr == null ? undefined : unwrap(evalExpr(qtyExpr, env));
|
|
2118
|
-
|
|
2119
|
-
env.
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
});
|
|
2123
|
-
} else env.book.close(env.barIndex, id);
|
|
2182
|
+
env.book.placeClose(env.barIndex, id, mark ?? undefined, {
|
|
2183
|
+
time: num(env.ctx.time) ?? undefined,
|
|
2184
|
+
qty: qtyRaw ?? undefined,
|
|
2185
|
+
});
|
|
2124
2186
|
return NA;
|
|
2125
2187
|
}
|
|
2126
2188
|
if (fname === "strategy.exit") {
|
|
@@ -2140,17 +2202,12 @@ function evalCall(node: Call, env: Env): Value {
|
|
|
2140
2202
|
trail_offset: unwrap(evalExpr(callArg(node.args, 10, ["trail_offset"]), env)),
|
|
2141
2203
|
price: num(env.ctx.close) ?? undefined,
|
|
2142
2204
|
time: num(env.ctx.time) ?? undefined,
|
|
2143
|
-
|
|
2144
|
-
open: num(env.ctx.open) ?? undefined,
|
|
2145
|
-
high: num(env.ctx.high) ?? undefined,
|
|
2146
|
-
low: num(env.ctx.low) ?? undefined,
|
|
2147
|
-
close: num(env.ctx.close) ?? undefined,
|
|
2148
|
-
},
|
|
2205
|
+
deferMarket: true,
|
|
2149
2206
|
});
|
|
2150
2207
|
return NA;
|
|
2151
2208
|
}
|
|
2152
2209
|
if (fname === "strategy.close_all") {
|
|
2153
|
-
env.book.
|
|
2210
|
+
env.book.placeCloseAll(env.barIndex, num(env.ctx.close) ?? undefined);
|
|
2154
2211
|
return NA;
|
|
2155
2212
|
}
|
|
2156
2213
|
if (fname === "strategy.cancel") {
|
|
@@ -2173,6 +2230,7 @@ function evalCall(node: Call, env: Env): Value {
|
|
|
2173
2230
|
time: num(env.ctx.time) ?? undefined,
|
|
2174
2231
|
oca_name: evalAsString(callArg(node.args, -1, ["oca_name"]), env),
|
|
2175
2232
|
oca_type: evalAsString(callArg(node.args, -1, ["oca_type"]), env),
|
|
2233
|
+
deferMarket: true,
|
|
2176
2234
|
});
|
|
2177
2235
|
return NA;
|
|
2178
2236
|
}
|
|
@@ -3816,7 +3874,7 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
3816
3874
|
lenArg(node, env, 1),
|
|
3817
3875
|
);
|
|
3818
3876
|
}
|
|
3819
|
-
if (fname === "ta.percentile_nearest_rank") {
|
|
3877
|
+
if (fname === "ta.percentile_nearest_rank" || fname === "percentile_nearest_rank") {
|
|
3820
3878
|
if (typeof ta.percentileNearest !== "function") return NA;
|
|
3821
3879
|
return (ta.percentileNearest as TaEngine["percentileNearest"]).call(
|
|
3822
3880
|
env.ta,
|
|
@@ -3826,7 +3884,7 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
3826
3884
|
lenOrDefault(node, env, 2, ["percentage", "percent"], 50),
|
|
3827
3885
|
);
|
|
3828
3886
|
}
|
|
3829
|
-
if (fname === "ta.percentile_linear_interpolation") {
|
|
3887
|
+
if (fname === "ta.percentile_linear_interpolation" || fname === "percentile_linear_interpolation") {
|
|
3830
3888
|
if (typeof ta.percentileLinear !== "function") return NA;
|
|
3831
3889
|
return (ta.percentileLinear as TaEngine["percentileLinear"]).call(
|
|
3832
3890
|
env.ta,
|
|
@@ -4024,17 +4082,6 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
4024
4082
|
const r = env.ta.aroon(site, num(env.ctx.high), num(env.ctx.low), length ?? 14);
|
|
4025
4083
|
return tupleOf([r.down, r.up]);
|
|
4026
4084
|
}
|
|
4027
|
-
if (fname === "ta.pivot_point_levels") {
|
|
4028
|
-
if (typeof ta.pivotPoints !== "function") return NA;
|
|
4029
|
-
const r = (ta.pivotPoints as TaEngine["pivotPoints"]).call(
|
|
4030
|
-
env.ta,
|
|
4031
|
-
site,
|
|
4032
|
-
num(env.ctx.high),
|
|
4033
|
-
num(env.ctx.low),
|
|
4034
|
-
num(env.ctx.close),
|
|
4035
|
-
);
|
|
4036
|
-
return tupleOf([r.pp, r.r1, r.s1, r.r2, r.s2]);
|
|
4037
|
-
}
|
|
4038
4085
|
if (fname === "ta.uo" || fname === "uo") {
|
|
4039
4086
|
return env.ta.uo(
|
|
4040
4087
|
site,
|
|
@@ -4126,11 +4173,11 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
4126
4173
|
}
|
|
4127
4174
|
if (fname === "ta.engulfing" || fname === "engulfing") {
|
|
4128
4175
|
const msg = "ta.engulfing() requires 4 arguments: open, high, low, close";
|
|
4129
|
-
unwrap(evalExpr(wave10aArg(0, ["open"], msg), env));
|
|
4176
|
+
const open = unwrap(evalExpr(wave10aArg(0, ["open"], msg), env));
|
|
4130
4177
|
unwrap(evalExpr(wave10aArg(1, ["high"], msg), env));
|
|
4131
4178
|
unwrap(evalExpr(wave10aArg(2, ["low"], msg), env));
|
|
4132
|
-
unwrap(evalExpr(wave10aArg(3, ["close"], msg), env));
|
|
4133
|
-
return env.ta.engulfing();
|
|
4179
|
+
const close = unwrap(evalExpr(wave10aArg(3, ["close"], msg), env));
|
|
4180
|
+
return env.ta.engulfing(site, open, close);
|
|
4134
4181
|
}
|
|
4135
4182
|
if (fname === "ta.gap_detector" || fname === "gap_detector") {
|
|
4136
4183
|
const msg = "ta.gap_detector() requires 3 arguments: high, low, prev_close";
|
|
@@ -4142,15 +4189,20 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
4142
4189
|
}
|
|
4143
4190
|
if (fname === "ta.double_top_bottom" || fname === "double_top_bottom") {
|
|
4144
4191
|
const msg = "ta.double_top_bottom() requires 3 arguments";
|
|
4145
|
-
unwrap(evalExpr(wave10aArg(0, ["high"], msg), env));
|
|
4146
|
-
unwrap(evalExpr(wave10aArg(1, ["low"], msg), env));
|
|
4147
|
-
return env.ta.doubleTopBottom(
|
|
4192
|
+
const high = unwrap(evalExpr(wave10aArg(0, ["high"], msg), env));
|
|
4193
|
+
const low = unwrap(evalExpr(wave10aArg(1, ["low"], msg), env));
|
|
4194
|
+
return env.ta.doubleTopBottom(
|
|
4195
|
+
site,
|
|
4196
|
+
high,
|
|
4197
|
+
low,
|
|
4198
|
+
unwrap(evalExpr(wave10aArg(2, ["period", "length"], msg), env)),
|
|
4199
|
+
);
|
|
4148
4200
|
}
|
|
4149
4201
|
if (fname === "ta.inside_bar_pattern" || fname === "inside_bar_pattern") {
|
|
4150
4202
|
const msg = "ta.inside_bar_pattern() requires 2 arguments";
|
|
4151
|
-
unwrap(evalExpr(wave10aArg(0, ["high"], msg), env));
|
|
4152
|
-
unwrap(evalExpr(wave10aArg(1, ["low"], msg), env));
|
|
4153
|
-
return env.ta.insideBarPattern();
|
|
4203
|
+
const high = unwrap(evalExpr(wave10aArg(0, ["high"], msg), env));
|
|
4204
|
+
const low = unwrap(evalExpr(wave10aArg(1, ["low"], msg), env));
|
|
4205
|
+
return env.ta.insideBarPattern(site, high, low);
|
|
4154
4206
|
}
|
|
4155
4207
|
if (fname === "ta.macd_signal" || fname === "macd_signal") {
|
|
4156
4208
|
const msg = "ta.macd_signal() requires 2 arguments: macd_line, signal_line";
|
|
@@ -4170,6 +4222,7 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
4170
4222
|
if (fname === "ta.stoch_smooth" || fname === "stoch_smooth") {
|
|
4171
4223
|
const msg = "ta.stoch_smooth() requires 6 arguments: high, low, close, period, smooth_k, smooth_d";
|
|
4172
4224
|
return env.ta.stochSmooth(
|
|
4225
|
+
site,
|
|
4173
4226
|
unwrap(evalExpr(wave10aArg(0, ["high"], msg), env)),
|
|
4174
4227
|
unwrap(evalExpr(wave10aArg(1, ["low"], msg), env)),
|
|
4175
4228
|
unwrap(evalExpr(wave10aArg(2, ["close"], msg), env)),
|
|
@@ -4213,7 +4266,11 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
4213
4266
|
}
|
|
4214
4267
|
if (fname === "ta.rsi_divergence" || fname === "rsi_divergence") {
|
|
4215
4268
|
const period = unwrap(evalExpr(callArg(node.args, 1, ["period", "length"]), env));
|
|
4216
|
-
return env.ta.rsiDivergence(
|
|
4269
|
+
return env.ta.rsiDivergence(
|
|
4270
|
+
site,
|
|
4271
|
+
seriesLastCell(evalExpr(callArg(node.args, 0, ["rsi_series", "series", "source"]), env)),
|
|
4272
|
+
period ?? 0,
|
|
4273
|
+
);
|
|
4217
4274
|
}
|
|
4218
4275
|
if (fname === "ta.cumulative_delta" || fname === "cumulative_delta") {
|
|
4219
4276
|
const period = unwrap(evalExpr(callArg(node.args, 2, ["period", "length"]), env));
|
|
@@ -4260,6 +4317,549 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
|
|
|
4260
4317
|
);
|
|
4261
4318
|
}
|
|
4262
4319
|
// wave10b-end
|
|
4320
|
+
// wave11-start — advanced / statistical ta.* surface
|
|
4321
|
+
const wave11 = (index: number, names: string[], message: string): Value =>
|
|
4322
|
+
unwrap(evalExpr(wave10aArg(index, names, message), env));
|
|
4323
|
+
const wave11n = (index: number, names: string[], message: string): Cell =>
|
|
4324
|
+
num(wave11(index, names, message));
|
|
4325
|
+
const wave13v = (index: number, names: string[], message: string): Value =>
|
|
4326
|
+
evalExpr(wave10aArg(index, names, message), env);
|
|
4327
|
+
if (fname === "ta.sma_weighted" || fname === "sma_weighted") {
|
|
4328
|
+
const msg = "ta.sma_weighted() requires at least 2 arguments: series, period";
|
|
4329
|
+
const weightArg = callArg(node.args, 2, ["weight_type", "weight"]);
|
|
4330
|
+
return env.ta.smaWeighted(
|
|
4331
|
+
site,
|
|
4332
|
+
wave11n(0, ["series", "source"], msg),
|
|
4333
|
+
wave11n(1, ["period", "length"], msg),
|
|
4334
|
+
weightArg == null ? "linear" : evalExpr(weightArg, env),
|
|
4335
|
+
);
|
|
4336
|
+
}
|
|
4337
|
+
if (fname === "ta.apo" || fname === "apo") {
|
|
4338
|
+
const msg = "ta.apo() requires 3 arguments: series, fast_period, slow_period";
|
|
4339
|
+
return env.ta.apo(
|
|
4340
|
+
site,
|
|
4341
|
+
wave11n(0, ["series", "source"], msg),
|
|
4342
|
+
wave11n(1, ["fast_period", "fast"], msg),
|
|
4343
|
+
wave11n(2, ["slow_period", "slow"], msg),
|
|
4344
|
+
);
|
|
4345
|
+
}
|
|
4346
|
+
if (fname === "ta.atr_normalized" || fname === "atr_normalized") {
|
|
4347
|
+
const msg = "ta.atr_normalized() requires 4 arguments: high, low, close, period";
|
|
4348
|
+
return env.ta.atrNormalized(
|
|
4349
|
+
site,
|
|
4350
|
+
wave11n(0, ["high"], msg),
|
|
4351
|
+
wave11n(1, ["low"], msg),
|
|
4352
|
+
wave11n(2, ["close"], msg),
|
|
4353
|
+
wave11n(3, ["period", "length"], msg),
|
|
4354
|
+
);
|
|
4355
|
+
}
|
|
4356
|
+
if (fname === "ta.beta" || fname === "beta") {
|
|
4357
|
+
const msg = "ta.beta() requires 3 arguments: series1, series2, length";
|
|
4358
|
+
return env.ta.beta(
|
|
4359
|
+
site,
|
|
4360
|
+
wave11n(0, ["series1", "series", "source"], msg),
|
|
4361
|
+
wave11n(1, ["series2", "series_b", "other"], msg),
|
|
4362
|
+
wave11n(2, ["length", "period"], msg),
|
|
4363
|
+
);
|
|
4364
|
+
}
|
|
4365
|
+
if (fname === "ta.r_squared" || fname === "r_squared") {
|
|
4366
|
+
const msg = "ta.r_squared() requires 3 arguments: series1, series2, length";
|
|
4367
|
+
return env.ta.rSquared(
|
|
4368
|
+
site,
|
|
4369
|
+
wave11n(0, ["series1", "series", "source"], msg),
|
|
4370
|
+
wave11n(1, ["series2", "series_b", "other"], msg),
|
|
4371
|
+
wave11n(2, ["length", "period"], msg),
|
|
4372
|
+
);
|
|
4373
|
+
}
|
|
4374
|
+
if (fname === "ta.comovement" || fname === "comovement") {
|
|
4375
|
+
const msg = "ta.comovement() requires 3 arguments: series1, series2, length";
|
|
4376
|
+
return env.ta.comovement(
|
|
4377
|
+
site,
|
|
4378
|
+
wave11n(0, ["series1", "series", "source"], msg),
|
|
4379
|
+
wave11n(1, ["series2", "series_b", "other"], msg),
|
|
4380
|
+
wave11n(2, ["length", "period"], msg),
|
|
4381
|
+
);
|
|
4382
|
+
}
|
|
4383
|
+
if (fname === "ta.skewness" || fname === "skewness") {
|
|
4384
|
+
const msg = "ta.skewness() requires 2 arguments: series, period";
|
|
4385
|
+
return env.ta.skewness(
|
|
4386
|
+
site,
|
|
4387
|
+
wave11n(0, ["series", "source"], msg),
|
|
4388
|
+
wave11n(1, ["period", "length"], msg),
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
4391
|
+
if (fname === "ta.kurtosis" || fname === "kurtosis") {
|
|
4392
|
+
const msg = "ta.kurtosis() requires 2 arguments: series, period";
|
|
4393
|
+
return env.ta.kurtosis(
|
|
4394
|
+
site,
|
|
4395
|
+
wave11n(0, ["series", "source"], msg),
|
|
4396
|
+
wave11n(1, ["period", "length"], msg),
|
|
4397
|
+
);
|
|
4398
|
+
}
|
|
4399
|
+
if (fname === "ta.volume_weighted_momentum" || fname === "volume_weighted_momentum") {
|
|
4400
|
+
const msg = "ta.volume_weighted_momentum() requires 3 arguments: series, volume, length";
|
|
4401
|
+
return env.ta.volumeWeightedMomentum(
|
|
4402
|
+
site,
|
|
4403
|
+
wave11n(0, ["series", "source"], msg),
|
|
4404
|
+
wave11n(1, ["volume"], msg),
|
|
4405
|
+
wave11n(2, ["length", "period"], msg),
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
if (fname === "ta.parkinson" || fname === "parkinson") {
|
|
4409
|
+
const msg = "ta.parkinson() requires 2 arguments: high, low";
|
|
4410
|
+
const highArg = wave10aArg(0, ["high"], msg);
|
|
4411
|
+
const lowArg = wave10aArg(1, ["low"], msg);
|
|
4412
|
+
if (isSeriesExpr(highArg, env) || isSeriesExpr(lowArg, env)) return NA;
|
|
4413
|
+
return env.ta.parkinson(num(evalExpr(highArg, env)), num(evalExpr(lowArg, env)));
|
|
4414
|
+
}
|
|
4415
|
+
if (fname === "ta.garman_klass" || fname === "garman_klass") {
|
|
4416
|
+
const msg = "ta.garman_klass() requires 4 arguments: high, low, close, open";
|
|
4417
|
+
const argsExpr = [
|
|
4418
|
+
wave10aArg(0, ["high"], msg),
|
|
4419
|
+
wave10aArg(1, ["low"], msg),
|
|
4420
|
+
wave10aArg(2, ["close"], msg),
|
|
4421
|
+
wave10aArg(3, ["open"], msg),
|
|
4422
|
+
];
|
|
4423
|
+
if (argsExpr.some((a) => isSeriesExpr(a, env))) return NA;
|
|
4424
|
+
return env.ta.garmanKlass(
|
|
4425
|
+
num(evalExpr(argsExpr[0]!, env)),
|
|
4426
|
+
num(evalExpr(argsExpr[1]!, env)),
|
|
4427
|
+
num(evalExpr(argsExpr[2]!, env)),
|
|
4428
|
+
num(evalExpr(argsExpr[3]!, env)),
|
|
4429
|
+
);
|
|
4430
|
+
}
|
|
4431
|
+
if (fname === "ta.mean_reversion_score" || fname === "mean_reversion_score") {
|
|
4432
|
+
const msg = "ta.mean_reversion_score() requires 4 arguments";
|
|
4433
|
+
return env.ta.meanReversionScore(
|
|
4434
|
+
site,
|
|
4435
|
+
wave11n(0, ["close", "source"], msg),
|
|
4436
|
+
wave11n(1, ["sma"], msg),
|
|
4437
|
+
wave11n(2, ["stdev", "std_dev"], msg),
|
|
4438
|
+
wave11n(3, ["period", "length"], msg),
|
|
4439
|
+
);
|
|
4440
|
+
}
|
|
4441
|
+
if (fname === "ta.market_condition" || fname === "market_condition") {
|
|
4442
|
+
const msg = "ta.market_condition() requires 4 arguments";
|
|
4443
|
+
return env.ta.marketCondition(
|
|
4444
|
+
site,
|
|
4445
|
+
wave11n(0, ["close", "source"], msg),
|
|
4446
|
+
wave11n(1, ["atr"], msg),
|
|
4447
|
+
wave11n(2, ["sma_period", "sma_length"], msg),
|
|
4448
|
+
wave11n(3, ["stdev_period", "stdev_length"], msg),
|
|
4449
|
+
);
|
|
4450
|
+
}
|
|
4451
|
+
if (fname === "ta.order_flow_imbalance" || fname === "order_flow_imbalance") {
|
|
4452
|
+
const msg = "ta.order_flow_imbalance() requires 5 arguments";
|
|
4453
|
+
return env.ta.orderFlowImbalance(
|
|
4454
|
+
site,
|
|
4455
|
+
wave11n(0, ["high"], msg),
|
|
4456
|
+
wave11n(1, ["low"], msg),
|
|
4457
|
+
wave11n(2, ["close"], msg),
|
|
4458
|
+
wave11n(3, ["volume"], msg),
|
|
4459
|
+
wave11n(4, ["period", "length"], msg),
|
|
4460
|
+
);
|
|
4461
|
+
}
|
|
4462
|
+
if (fname === "ta.liquidity_score" || fname === "liquidity_score") {
|
|
4463
|
+
const msg = "ta.liquidity_score() requires 4 arguments";
|
|
4464
|
+
return env.ta.liquidityScore(
|
|
4465
|
+
site,
|
|
4466
|
+
wave11n(0, ["volume"], msg),
|
|
4467
|
+
wave11n(1, ["volatility"], msg),
|
|
4468
|
+
wave11n(2, ["spread"], msg),
|
|
4469
|
+
wave11n(3, ["period", "length"], msg),
|
|
4470
|
+
);
|
|
4471
|
+
}
|
|
4472
|
+
if (fname === "ta.fear_greed_index" || fname === "fear_greed_index") {
|
|
4473
|
+
const msg = "ta.fear_greed_index() requires 4 arguments";
|
|
4474
|
+
return env.ta.fearGreedIndex(
|
|
4475
|
+
wave11n(0, ["rsi"], msg),
|
|
4476
|
+
wave11n(1, ["vix"], msg),
|
|
4477
|
+
wave11n(2, ["put_call", "put_call_ratio"], msg),
|
|
4478
|
+
wave11n(3, ["breadth"], msg),
|
|
4479
|
+
);
|
|
4480
|
+
}
|
|
4481
|
+
if (fname === "ta.crowd_sentiment" || fname === "crowd_sentiment") {
|
|
4482
|
+
const msg = "ta.crowd_sentiment() requires 3 arguments";
|
|
4483
|
+
return env.ta.crowdSentiment(
|
|
4484
|
+
wave11n(0, ["price_agreement", "price"], msg),
|
|
4485
|
+
wave11n(1, ["volume_agreement", "volume"], msg),
|
|
4486
|
+
wave11n(2, ["time_agreement", "time"], msg),
|
|
4487
|
+
);
|
|
4488
|
+
}
|
|
4489
|
+
if (fname === "ta.smart_money_flow" || fname === "smart_money_flow") {
|
|
4490
|
+
const msg = "ta.smart_money_flow() requires 4 arguments";
|
|
4491
|
+
return env.ta.smartMoneyFlow(
|
|
4492
|
+
wave11n(0, ["price_change"], msg),
|
|
4493
|
+
wave11n(1, ["volume"], msg),
|
|
4494
|
+
wave11n(2, ["time_since_high", "time_high"], msg),
|
|
4495
|
+
wave11n(3, ["time_since_low", "time_low"], msg),
|
|
4496
|
+
);
|
|
4497
|
+
}
|
|
4498
|
+
if (fname === "ta.trend_confirmation_score" || fname === "trend_confirmation_score") {
|
|
4499
|
+
const msg = "ta.trend_confirmation_score() requires 6 arguments";
|
|
4500
|
+
wave11n(5, ["support_distance"], msg); // required, unused (Python parity)
|
|
4501
|
+
return env.ta.trendConfirmationScore(
|
|
4502
|
+
wave11n(0, ["momentum"], msg),
|
|
4503
|
+
wave11n(1, ["trend_alignment"], msg),
|
|
4504
|
+
wave11n(2, ["strength"], msg),
|
|
4505
|
+
wave11n(3, ["rsi"], msg),
|
|
4506
|
+
wave11n(4, ["rsi_alignment"], msg),
|
|
4507
|
+
);
|
|
4508
|
+
}
|
|
4509
|
+
if (fname === "ta.max_loss_level" || fname === "max_loss_level") {
|
|
4510
|
+
const msg = "ta.max_loss_level() requires 3 arguments";
|
|
4511
|
+
return env.ta.maxLossLevel(
|
|
4512
|
+
wave11n(0, ["entry", "entry_price"], msg),
|
|
4513
|
+
wave11n(1, ["account", "account_size"], msg),
|
|
4514
|
+
wave11n(2, ["max_loss_pct", "max_loss_percent"], msg),
|
|
4515
|
+
);
|
|
4516
|
+
}
|
|
4517
|
+
if (fname === "ta.profit_lock_level" || fname === "profit_lock_level") {
|
|
4518
|
+
const msg = "ta.profit_lock_level() requires 4 arguments";
|
|
4519
|
+
return env.ta.profitLockLevel(
|
|
4520
|
+
wave11n(0, ["entry", "entry_price"], msg),
|
|
4521
|
+
wave11n(1, ["current", "current_price"], msg),
|
|
4522
|
+
wave11n(2, ["trail_pct", "trail_percent"], msg),
|
|
4523
|
+
wave11n(3, ["direction"], msg),
|
|
4524
|
+
);
|
|
4525
|
+
}
|
|
4526
|
+
// wave11-end
|
|
4527
|
+
// wave12-start — risk / sizing / timing analytics
|
|
4528
|
+
const wave12s = (index: number, names: string[], message: string): Value =>
|
|
4529
|
+
evalExpr(wave10aArg(index, names, message), env);
|
|
4530
|
+
if (fname === "ta.position_sizing" || fname === "position_sizing") {
|
|
4531
|
+
const msg = "ta.position_sizing() requires 4 arguments";
|
|
4532
|
+
return env.ta.positionSizing(
|
|
4533
|
+
wave11n(0, ["account", "account_size"], msg),
|
|
4534
|
+
wave11n(1, ["risk_pct", "risk_percent"], msg),
|
|
4535
|
+
wave11n(2, ["entry", "entry_price"], msg),
|
|
4536
|
+
wave11n(3, ["stop", "stop_price"], msg),
|
|
4537
|
+
);
|
|
4538
|
+
}
|
|
4539
|
+
if (fname === "ta.kelly_criterion" || fname === "kelly_criterion") {
|
|
4540
|
+
const msg = "ta.kelly_criterion() requires 3 arguments";
|
|
4541
|
+
return env.ta.kellyCriterion(
|
|
4542
|
+
wave11n(0, ["win_rate"], msg),
|
|
4543
|
+
wave11n(1, ["avg_win"], msg),
|
|
4544
|
+
wave11n(2, ["avg_loss"], msg),
|
|
4545
|
+
);
|
|
4546
|
+
}
|
|
4547
|
+
if (fname === "ta.risk_reward_ratio" || fname === "risk_reward_ratio") {
|
|
4548
|
+
const msg = "ta.risk_reward_ratio() requires 3 arguments";
|
|
4549
|
+
return env.ta.riskRewardRatio(
|
|
4550
|
+
wave11n(0, ["entry", "entry_price"], msg),
|
|
4551
|
+
wave11n(1, ["stop", "stop_price"], msg),
|
|
4552
|
+
wave11n(2, ["target", "target_price"], msg),
|
|
4553
|
+
);
|
|
4554
|
+
}
|
|
4555
|
+
if (fname === "ta.risk_reward_asymmetry" || fname === "risk_reward_asymmetry") {
|
|
4556
|
+
const msg = "ta.risk_reward_asymmetry() requires 4 arguments";
|
|
4557
|
+
return env.ta.riskRewardAsymmetry(
|
|
4558
|
+
wave11n(0, ["entry", "entry_price"], msg),
|
|
4559
|
+
wave11n(1, ["stop", "stop_price"], msg),
|
|
4560
|
+
wave11n(2, ["target", "target_price"], msg),
|
|
4561
|
+
wave11n(3, ["win_rate"], msg),
|
|
4562
|
+
);
|
|
4563
|
+
}
|
|
4564
|
+
if (fname === "ta.probability_of_movement" || fname === "probability_of_movement") {
|
|
4565
|
+
const msg = "ta.probability_of_movement() requires 4 arguments";
|
|
4566
|
+
return env.ta.probabilityOfMovement(
|
|
4567
|
+
wave11n(0, ["current", "current_price"], msg),
|
|
4568
|
+
wave11n(1, ["target", "target_price"], msg),
|
|
4569
|
+
wave11n(2, ["atr"], msg),
|
|
4570
|
+
wave11n(3, ["period", "length"], msg),
|
|
4571
|
+
);
|
|
4572
|
+
}
|
|
4573
|
+
if (fname === "ta.trend_strength" || fname === "trend_strength") {
|
|
4574
|
+
const msg = "ta.trend_strength() requires 3 arguments";
|
|
4575
|
+
return env.ta.trendStrength(
|
|
4576
|
+
wave11n(0, ["price", "source"], msg),
|
|
4577
|
+
wave11n(1, ["adx"], msg),
|
|
4578
|
+
wave11n(2, ["rsi"], msg),
|
|
4579
|
+
);
|
|
4580
|
+
}
|
|
4581
|
+
if (fname === "ta.breakeven_level" || fname === "breakeven_level") {
|
|
4582
|
+
const msg = "ta.breakeven_level() requires 5 arguments";
|
|
4583
|
+
return env.ta.breakevenLevel(
|
|
4584
|
+
wave11n(0, ["entry", "entry_price"], msg),
|
|
4585
|
+
wave11n(1, ["quantity", "qty"], msg),
|
|
4586
|
+
wave11n(2, ["commission"], msg),
|
|
4587
|
+
wave11n(3, ["slippage"], msg),
|
|
4588
|
+
wave11n(4, ["direction"], msg),
|
|
4589
|
+
);
|
|
4590
|
+
}
|
|
4591
|
+
if (fname === "ta.market_structure_pivot" || fname === "market_structure_pivot") {
|
|
4592
|
+
const msg = "ta.market_structure_pivot() requires 5 arguments";
|
|
4593
|
+
const high = wave13v(0, ["high_list", "high"], msg);
|
|
4594
|
+
const low = wave13v(1, ["low_list", "low"], msg);
|
|
4595
|
+
wave13v(2, ["close_list", "close"], msg);
|
|
4596
|
+
const period = wave11n(3, ["period", "left", "left_bars"], msg);
|
|
4597
|
+
const mode = wave11n(4, ["mode", "right", "right_bars"], msg);
|
|
4598
|
+
return env.ta.marketStructurePivot(site, high, low, period, mode);
|
|
4599
|
+
}
|
|
4600
|
+
if (fname === "ta.position_sizing_score" || fname === "position_sizing_score") {
|
|
4601
|
+
const msg = "ta.position_sizing_score() requires 4 arguments";
|
|
4602
|
+
return env.ta.positionSizingScore(
|
|
4603
|
+
wave11n(0, ["account_risk", "risk"], msg),
|
|
4604
|
+
wave11n(1, ["volatility"], msg),
|
|
4605
|
+
wave11n(2, ["conviction"], msg),
|
|
4606
|
+
wave11n(3, ["correlation"], msg),
|
|
4607
|
+
);
|
|
4608
|
+
}
|
|
4609
|
+
if (fname === "ta.trailing_exit_level" || fname === "trailing_exit_level") {
|
|
4610
|
+
const msg = "ta.trailing_exit_level() requires 5 arguments";
|
|
4611
|
+
return env.ta.trailingExitLevel(
|
|
4612
|
+
wave11n(0, ["entry", "entry_price"], msg),
|
|
4613
|
+
wave11n(1, ["current", "current_price"], msg),
|
|
4614
|
+
wave11n(2, ["atr"], msg),
|
|
4615
|
+
wave11n(3, ["multiplier", "mult"], msg),
|
|
4616
|
+
wave11n(4, ["step"], msg),
|
|
4617
|
+
);
|
|
4618
|
+
}
|
|
4619
|
+
if (fname === "ta.optimal_entry_zone" || fname === "optimal_entry_zone") {
|
|
4620
|
+
const msg = "ta.optimal_entry_zone() requires 4 arguments";
|
|
4621
|
+
wave11n(0, ["high"], msg);
|
|
4622
|
+
wave11n(1, ["low"], msg);
|
|
4623
|
+
const close = wave13v(2, ["close"], msg);
|
|
4624
|
+
wave11n(3, ["period", "length"], msg);
|
|
4625
|
+
return env.ta.optimalEntryZone(site, close);
|
|
4626
|
+
}
|
|
4627
|
+
if (fname === "ta.mean_reversion_entry" || fname === "mean_reversion_entry") {
|
|
4628
|
+
const msg = "ta.mean_reversion_entry() requires 5 arguments";
|
|
4629
|
+
return env.ta.meanReversionEntry(
|
|
4630
|
+
site,
|
|
4631
|
+
wave13v(0, ["price", "source"], msg),
|
|
4632
|
+
wave13v(1, ["sma"], msg),
|
|
4633
|
+
wave13v(2, ["stdev", "std_dev"], msg),
|
|
4634
|
+
wave11n(4, ["threshold"], msg),
|
|
4635
|
+
);
|
|
4636
|
+
}
|
|
4637
|
+
if (fname === "ta.multi_timeframe_signal" || fname === "multi_timeframe_signal") {
|
|
4638
|
+
const msg = "ta.multi_timeframe_signal() requires 6 arguments";
|
|
4639
|
+
return env.ta.multiTimeframeSignal(
|
|
4640
|
+
wave11n(0, ["s1", "signal1"], msg),
|
|
4641
|
+
wave11n(1, ["s2", "signal2"], msg),
|
|
4642
|
+
wave11n(2, ["s3", "signal3"], msg),
|
|
4643
|
+
wave11n(3, ["w1", "weight1"], msg),
|
|
4644
|
+
wave11n(4, ["w2", "weight2"], msg),
|
|
4645
|
+
wave11n(5, ["w3", "weight3"], msg),
|
|
4646
|
+
);
|
|
4647
|
+
}
|
|
4648
|
+
if (fname === "ta.market_timing_index" || fname === "market_timing_index") {
|
|
4649
|
+
const msg = "ta.market_timing_index() requires 4 arguments";
|
|
4650
|
+
return env.ta.marketTimingIndex(
|
|
4651
|
+
wave11n(0, ["trend"], msg),
|
|
4652
|
+
wave11n(1, ["momentum"], msg),
|
|
4653
|
+
wave11n(2, ["volatility"], msg),
|
|
4654
|
+
wave11n(3, ["sentiment"], msg),
|
|
4655
|
+
);
|
|
4656
|
+
}
|
|
4657
|
+
if (fname === "ta.regime_adaptive_signal" || fname === "regime_adaptive_signal") {
|
|
4658
|
+
const msg = "ta.regime_adaptive_signal() requires 4 arguments";
|
|
4659
|
+
const strength = wave11n(0, ["signal_strength", "strength"], msg);
|
|
4660
|
+
const volRegime = wave12s(1, ["volatility_regime", "vol_regime"], msg);
|
|
4661
|
+
const trendRegime = wave12s(2, ["trend_regime"], msg);
|
|
4662
|
+
wave11n(3, ["period", "length"], msg);
|
|
4663
|
+
return env.ta.regimeAdaptiveSignal(strength, volRegime, trendRegime);
|
|
4664
|
+
}
|
|
4665
|
+
if (fname === "ta.strategy_score" || fname === "strategy_score") {
|
|
4666
|
+
const msg = "ta.strategy_score() requires 4 arguments";
|
|
4667
|
+
return env.ta.strategyScore(
|
|
4668
|
+
wave11n(0, ["rsi"], msg),
|
|
4669
|
+
wave11n(1, ["macd"], msg),
|
|
4670
|
+
wave11n(2, ["ema_cross"], msg),
|
|
4671
|
+
wave11n(3, ["trend"], msg),
|
|
4672
|
+
);
|
|
4673
|
+
}
|
|
4674
|
+
if (fname === "ta.breakout_detection" || fname === "breakout_detection") {
|
|
4675
|
+
const msg = "ta.breakout_detection() requires 3 arguments";
|
|
4676
|
+
return env.ta.breakoutDetection(
|
|
4677
|
+
wave11n(0, ["close", "source"], msg),
|
|
4678
|
+
wave11n(1, ["resistance"], msg),
|
|
4679
|
+
wave11n(2, ["support"], msg),
|
|
4680
|
+
);
|
|
4681
|
+
}
|
|
4682
|
+
// wave12-end
|
|
4683
|
+
// wave13-start — history / sentiment / macro analytics
|
|
4684
|
+
if (fname === "ta.ema_cross_signal" || fname === "ema_cross_signal") {
|
|
4685
|
+
const msg = "ta.ema_cross_signal() requires 3 arguments";
|
|
4686
|
+
return env.ta.emaCrossSignal(
|
|
4687
|
+
site,
|
|
4688
|
+
wave11n(0, ["fast_ema", "fast"], msg),
|
|
4689
|
+
wave11n(1, ["slow_ema", "slow"], msg),
|
|
4690
|
+
wave11n(2, ["threshold"], msg),
|
|
4691
|
+
);
|
|
4692
|
+
}
|
|
4693
|
+
if (fname === "ta.momentum_filter" || fname === "momentum_filter") {
|
|
4694
|
+
const msg = "ta.momentum_filter() requires 3 arguments";
|
|
4695
|
+
return env.ta.momentumFilter(
|
|
4696
|
+
site,
|
|
4697
|
+
wave11n(0, ["momentum_raw", "momentum"], msg),
|
|
4698
|
+
wave11n(1, ["volume"], msg),
|
|
4699
|
+
wave11n(2, ["period", "length"], msg),
|
|
4700
|
+
);
|
|
4701
|
+
}
|
|
4702
|
+
if (fname === "ta.volatility_regime" || fname === "volatility_regime") {
|
|
4703
|
+
const msg = "ta.volatility_regime() requires 2 arguments";
|
|
4704
|
+
return env.ta.volatilityRegime(
|
|
4705
|
+
site,
|
|
4706
|
+
wave11n(0, ["atr_series", "atr", "source"], msg),
|
|
4707
|
+
wave11n(1, ["period", "length"], msg),
|
|
4708
|
+
);
|
|
4709
|
+
}
|
|
4710
|
+
if (fname === "ta.economic_impact_score" || fname === "economic_impact_score") {
|
|
4711
|
+
const msg = "ta.economic_impact_score() requires 3 arguments";
|
|
4712
|
+
return env.ta.economicImpactScore(
|
|
4713
|
+
wave11n(0, ["price_change"], msg),
|
|
4714
|
+
wave11n(1, ["volatility"], msg),
|
|
4715
|
+
wave11n(2, ["volume_change"], msg),
|
|
4716
|
+
);
|
|
4717
|
+
}
|
|
4718
|
+
if (fname === "ta.contrarian_signal" || fname === "contrarian_signal") {
|
|
4719
|
+
const msg = "ta.contrarian_signal() requires 3 arguments";
|
|
4720
|
+
return env.ta.contrarianSignal(
|
|
4721
|
+
wave11n(0, ["sentiment"], msg),
|
|
4722
|
+
wave11n(1, ["volatility"], msg),
|
|
4723
|
+
wave11n(2, ["time_since_extreme", "time_extreme"], msg),
|
|
4724
|
+
);
|
|
4725
|
+
}
|
|
4726
|
+
if (fname === "ta.drawdown_recovery_level" || fname === "drawdown_recovery_level") {
|
|
4727
|
+
const msg = "ta.drawdown_recovery_level() requires 4 arguments";
|
|
4728
|
+
return env.ta.drawdownRecoveryLevel(
|
|
4729
|
+
wave11n(0, ["peak"], msg),
|
|
4730
|
+
wave11n(1, ["current", "current_price"], msg),
|
|
4731
|
+
wave11n(2, ["recovery_factor", "factor"], msg),
|
|
4732
|
+
wave11n(3, ["period", "length"], msg),
|
|
4733
|
+
);
|
|
4734
|
+
}
|
|
4735
|
+
if (fname === "ta.pivot_point_levels" || fname === "pivot_point_levels") {
|
|
4736
|
+
const msg = "ta.pivot_point_levels() requires type+anchor or high, low, close";
|
|
4737
|
+
const first = wave11(0, ["type", "high"], msg);
|
|
4738
|
+
if (typeof first === "string") {
|
|
4739
|
+
// Reference Pine type+anchor form → context pivot-point tuple.
|
|
4740
|
+
if (typeof env.ta.pivotPoints !== "function") return NA;
|
|
4741
|
+
const r = (env.ta.pivotPoints as TaEngine["pivotPoints"]).call(
|
|
4742
|
+
env.ta,
|
|
4743
|
+
site,
|
|
4744
|
+
num(env.ctx.high),
|
|
4745
|
+
num(env.ctx.low),
|
|
4746
|
+
num(env.ctx.close),
|
|
4747
|
+
);
|
|
4748
|
+
return tupleOf([r.pp, r.r1, r.s1, r.r2, r.s2]);
|
|
4749
|
+
}
|
|
4750
|
+
const tradArg = callArg(node.args, 3, ["traditional"]);
|
|
4751
|
+
const traditional = tradArg == null ? true : unwrap(evalExpr(tradArg, env)) !== 0;
|
|
4752
|
+
return env.ta.pivotPointLevels(
|
|
4753
|
+
num(first),
|
|
4754
|
+
wave11n(1, ["low"], msg),
|
|
4755
|
+
wave11n(2, ["close"], msg),
|
|
4756
|
+
traditional,
|
|
4757
|
+
);
|
|
4758
|
+
}
|
|
4759
|
+
if (fname === "ta.volatility_regime_score" || fname === "volatility_regime_score") {
|
|
4760
|
+
const msg = "ta.volatility_regime_score() requires 4 arguments";
|
|
4761
|
+
return env.ta.volatilityRegimeScore(
|
|
4762
|
+
site,
|
|
4763
|
+
wave13v(0, ["atr_list", "atr"], msg),
|
|
4764
|
+
wave13v(1, ["volatility_list", "volatility"], msg),
|
|
4765
|
+
wave13v(2, ["vix_list", "vix"], msg),
|
|
4766
|
+
wave11n(3, ["threshold"], msg),
|
|
4767
|
+
);
|
|
4768
|
+
}
|
|
4769
|
+
if (fname === "ta.correlation_filter" || fname === "correlation_filter") {
|
|
4770
|
+
const msg = "ta.correlation_filter() requires 5 arguments";
|
|
4771
|
+
const s1 = wave13v(0, ["signal1_list", "signal1", "s1"], msg);
|
|
4772
|
+
const s2 = wave13v(1, ["signal2_list", "signal2", "s2"], msg);
|
|
4773
|
+
const s3 = wave13v(2, ["signal3_list", "signal3", "s3"], msg);
|
|
4774
|
+
wave11n(3, ["num_signals"], msg);
|
|
4775
|
+
return env.ta.correlationFilter(site, s1, s2, s3, wave11n(4, ["threshold"], msg));
|
|
4776
|
+
}
|
|
4777
|
+
if (fname === "ta.advanced_breakout_detector" || fname === "advanced_breakout_detector") {
|
|
4778
|
+
const msg = "ta.advanced_breakout_detector() requires 5 arguments";
|
|
4779
|
+
return env.ta.advancedBreakoutDetector(
|
|
4780
|
+
site,
|
|
4781
|
+
wave13v(0, ["price_list", "price"], msg),
|
|
4782
|
+
wave13v(1, ["volume_list", "volume"], msg),
|
|
4783
|
+
wave11n(2, ["level"], msg),
|
|
4784
|
+
wave11n(3, ["lookback"], msg),
|
|
4785
|
+
wave11n(4, ["volume_multiplier", "vol_mult"], msg),
|
|
4786
|
+
);
|
|
4787
|
+
}
|
|
4788
|
+
if (fname === "ta.pullback_bounce_level" || fname === "pullback_bounce_level") {
|
|
4789
|
+
const msg = "ta.pullback_bounce_level() requires 5 arguments";
|
|
4790
|
+
const high = wave13v(0, ["high_list", "high"], msg);
|
|
4791
|
+
const low = wave13v(1, ["low_list", "low"], msg);
|
|
4792
|
+
wave13v(2, ["close_list", "close"], msg);
|
|
4793
|
+
return env.ta.pullbackBounceLevel(
|
|
4794
|
+
site,
|
|
4795
|
+
high,
|
|
4796
|
+
low,
|
|
4797
|
+
wave11n(3, ["trend_direction", "trend_dir"], msg),
|
|
4798
|
+
wave11n(4, ["lookback"], msg),
|
|
4799
|
+
);
|
|
4800
|
+
}
|
|
4801
|
+
if (fname === "ta.divergence_detector" || fname === "divergence_detector") {
|
|
4802
|
+
const msg = "ta.divergence_detector() requires 3 arguments";
|
|
4803
|
+
return env.ta.divergenceDetector(
|
|
4804
|
+
site,
|
|
4805
|
+
wave13v(0, ["price_list", "price"], msg),
|
|
4806
|
+
wave13v(1, ["indicator_list", "indicator"], msg),
|
|
4807
|
+
wave11n(2, ["lookback"], msg),
|
|
4808
|
+
);
|
|
4809
|
+
}
|
|
4810
|
+
if (fname === "ta.momentum_divergence" || fname === "momentum_divergence") {
|
|
4811
|
+
const msg = "ta.momentum_divergence() requires 3 arguments";
|
|
4812
|
+
return env.ta.momentumDivergence(
|
|
4813
|
+
site,
|
|
4814
|
+
wave13v(0, ["price"], msg),
|
|
4815
|
+
wave13v(1, ["momentum_fast", "mom_fast"], msg),
|
|
4816
|
+
wave13v(2, ["momentum_slow", "mom_slow"], msg),
|
|
4817
|
+
);
|
|
4818
|
+
}
|
|
4819
|
+
if (fname === "ta.spread_analysis" || fname === "spread_analysis") {
|
|
4820
|
+
const msg = "ta.spread_analysis() requires 3 arguments";
|
|
4821
|
+
return env.ta.spreadAnalysis(
|
|
4822
|
+
site,
|
|
4823
|
+
wave13v(0, ["bid"], msg),
|
|
4824
|
+
wave13v(1, ["ask"], msg),
|
|
4825
|
+
wave11n(2, ["period", "length"], msg),
|
|
4826
|
+
);
|
|
4827
|
+
}
|
|
4828
|
+
if (fname === "ta.employment_cycle_indicator" || fname === "employment_cycle_indicator") {
|
|
4829
|
+
const msg = "ta.employment_cycle_indicator() requires 3 arguments";
|
|
4830
|
+
return env.ta.employmentCycleIndicator(
|
|
4831
|
+
site,
|
|
4832
|
+
wave13v(0, ["cyclical_stocks", "cyclical"], msg),
|
|
4833
|
+
wave13v(1, ["defensive_stocks", "defensive"], msg),
|
|
4834
|
+
wave13v(2, ["unemployment_proxy", "unemployment"], msg),
|
|
4835
|
+
);
|
|
4836
|
+
}
|
|
4837
|
+
if (fname === "ta.gdp_growth_proxy" || fname === "gdp_growth_proxy") {
|
|
4838
|
+
const msg = "ta.gdp_growth_proxy() requires 3 arguments";
|
|
4839
|
+
return env.ta.gdpGrowthProxy(
|
|
4840
|
+
site,
|
|
4841
|
+
wave13v(0, ["market_breadth", "breadth"], msg),
|
|
4842
|
+
wave13v(1, ["market_volume", "volume"], msg),
|
|
4843
|
+
wave13v(2, ["price_momentum", "momentum"], msg),
|
|
4844
|
+
);
|
|
4845
|
+
}
|
|
4846
|
+
if (fname === "ta.inflation_proxy_indicator" || fname === "inflation_proxy_indicator") {
|
|
4847
|
+
const msg = "ta.inflation_proxy_indicator() requires 3 arguments";
|
|
4848
|
+
return env.ta.inflationProxyIndicator(
|
|
4849
|
+
site,
|
|
4850
|
+
wave13v(0, ["usd_index", "usd"], msg),
|
|
4851
|
+
wave13v(1, ["commodity_prices", "commodities"], msg),
|
|
4852
|
+
wave13v(2, ["bond_yields", "yields"], msg),
|
|
4853
|
+
);
|
|
4854
|
+
}
|
|
4855
|
+
if (fname === "ta.expected_value" || fname === "expected_value") {
|
|
4856
|
+
const msg = "ta.expected_value() requires 2 arguments: returns, probabilities";
|
|
4857
|
+
const returns = numericList(evalExpr(wave10aArg(0, ["returns"], msg), env));
|
|
4858
|
+
const probs = numericList(evalExpr(wave10aArg(1, ["probabilities", "probs"], msg), env));
|
|
4859
|
+
if (returns.length !== probs.length) runtimeError("Returns and probabilities must have same length");
|
|
4860
|
+
return env.ta.expectedValue(returns, probs);
|
|
4861
|
+
}
|
|
4862
|
+
// wave13-end
|
|
4263
4863
|
return undefined;
|
|
4264
4864
|
}
|
|
4265
4865
|
|
|
@@ -4271,6 +4871,31 @@ function hlc3(env: Env): Cell {
|
|
|
4271
4871
|
return (h + l + c) / 3;
|
|
4272
4872
|
}
|
|
4273
4873
|
|
|
4874
|
+
/** Python `_series_last`: array → last cell; scalar → the value itself. */
|
|
4875
|
+
function seriesLastCell(value: Value): number | null {
|
|
4876
|
+
if (value instanceof PineArray) {
|
|
4877
|
+
const n = value.size();
|
|
4878
|
+
return n > 0 ? value.get(n - 1) : null;
|
|
4879
|
+
}
|
|
4880
|
+
const v = unwrap(value);
|
|
4881
|
+
return typeof v === "number" && Number.isFinite(v) ? v : null;
|
|
4882
|
+
}
|
|
4883
|
+
|
|
4884
|
+
/** Python `_expect_list` for array-valued args: numeric cells only. */
|
|
4885
|
+
function numericList(value: Value): number[] {
|
|
4886
|
+
const arr = asArray(value);
|
|
4887
|
+
if (arr == null) {
|
|
4888
|
+
const v = unwrap(value);
|
|
4889
|
+
return typeof v === "number" && Number.isFinite(v) ? [v] : [];
|
|
4890
|
+
}
|
|
4891
|
+
const out: number[] = [];
|
|
4892
|
+
for (let i = 0; i < arr.size(); i++) {
|
|
4893
|
+
const v = arr.get(i);
|
|
4894
|
+
if (typeof v === "number" && Number.isFinite(v)) out.push(v);
|
|
4895
|
+
}
|
|
4896
|
+
return out;
|
|
4897
|
+
}
|
|
4898
|
+
|
|
4274
4899
|
function asArray(value: Value): PineArray | null {
|
|
4275
4900
|
if (value instanceof PineArray) return value;
|
|
4276
4901
|
if (value && typeof value === "object" && "__array" in value) {
|
|
@@ -5149,11 +5774,24 @@ function evalDrawingCall(fname: string | null, node: Call, env: Env): Value | un
|
|
|
5149
5774
|
return NA;
|
|
5150
5775
|
}
|
|
5151
5776
|
if (fname === "table.new") {
|
|
5777
|
+
// Pine positional order is (position, columns, rows, bgcolor,
|
|
5778
|
+
// frame_color, frame_width, border_color, border_width, force_overlay).
|
|
5779
|
+
// Python `_handle_table_new` SoT (columns then rows, not rows then columns).
|
|
5152
5780
|
const extra: Record<string, unknown> = {};
|
|
5153
5781
|
const position = drawRaw(node, env, 0, ["position"]);
|
|
5154
5782
|
if (position !== undefined) extra.position = position;
|
|
5155
|
-
putFinite(extra, "
|
|
5156
|
-
putFinite(extra, "
|
|
5783
|
+
putFinite(extra, "columns", drawNum(node, env, 1, ["columns"]));
|
|
5784
|
+
putFinite(extra, "rows", drawNum(node, env, 2, ["rows"]));
|
|
5785
|
+
const bgcolor = drawRaw(node, env, 3, ["bgcolor"]);
|
|
5786
|
+
if (bgcolor !== undefined) extra.bgcolor = bgcolor;
|
|
5787
|
+
const frameColor = drawRaw(node, env, 4, ["frame_color"]);
|
|
5788
|
+
if (frameColor !== undefined) extra.frame_color = frameColor;
|
|
5789
|
+
putFinite(extra, "frame_width", drawNum(node, env, 5, ["frame_width"]));
|
|
5790
|
+
const borderColor = drawRaw(node, env, 6, ["border_color"]);
|
|
5791
|
+
if (borderColor !== undefined) extra.border_color = borderColor;
|
|
5792
|
+
putFinite(extra, "border_width", drawNum(node, env, 7, ["border_width"]));
|
|
5793
|
+
const forceOverlay = drawRaw(node, env, 8, ["force_overlay"]);
|
|
5794
|
+
if (forceOverlay !== undefined) extra.force_overlay = forceOverlay;
|
|
5157
5795
|
return book.tableNew(env.barIndex, extra);
|
|
5158
5796
|
}
|
|
5159
5797
|
if (fname === "polyline.new") {
|
|
@@ -6194,9 +6832,10 @@ function syminfoPrefix(node: Call, env: Env): string {
|
|
|
6194
6832
|
}
|
|
6195
6833
|
|
|
6196
6834
|
function calendarMs(node: Call, env: Env): number | null {
|
|
6835
|
+
// Python `_timestamp_to_ms`: chart hosts store opens in seconds.
|
|
6197
6836
|
const arg = callArg(node.args, 0, ["time", "timestamp"]);
|
|
6198
|
-
if (arg == null) return num(env.ctx.time);
|
|
6199
|
-
return unwrap(evalExpr(arg, env));
|
|
6837
|
+
if (arg == null) return timestampToMs(num(env.ctx.time));
|
|
6838
|
+
return timestampToMs(unwrap(evalExpr(arg, env)));
|
|
6200
6839
|
}
|
|
6201
6840
|
|
|
6202
6841
|
function evalLibraryMemberCall(mod: LibraryModule, attr: string, node: Call, env: Env): Value {
|