Skip to content

Commit dde96c6

Browse files
committed
Add Pine v6 footprint metadata support
1 parent d6431b8 commit dde96c6

2 files changed

Lines changed: 183 additions & 0 deletions

File tree

src/pinescript_validator/data_loader.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
"table",
6868
"polyline",
6969
"linefill",
70+
"footprint",
71+
"volume_row",
7072
"series",
7173
"simple",
7274
"const",
@@ -105,10 +107,37 @@
105107
"strategy",
106108
"table",
107109
"text",
110+
"footprint",
111+
"volume_row",
108112
"xloc",
109113
"yloc",
110114
}
111115

116+
SUPPLEMENTAL_NAMESPACE_MEMBERS = {
117+
"request": {"footprint"},
118+
"footprint": {
119+
"buy_volume",
120+
"sell_volume",
121+
"total_volume",
122+
"delta",
123+
"poc",
124+
"vah",
125+
"val",
126+
"get_row_by_price",
127+
"rows",
128+
},
129+
"volume_row": {
130+
"up_price",
131+
"down_price",
132+
"buy_volume",
133+
"sell_volume",
134+
"total_volume",
135+
"delta",
136+
"has_buy_imbalance",
137+
"has_sell_imbalance",
138+
},
139+
}
140+
112141

113142
@dataclass(slots=True, frozen=True)
114143
class FunctionOverloadSpec:
@@ -301,6 +330,140 @@ class BuiltinData:
301330
}
302331
],
303332
},
333+
"request.footprint": {
334+
"name": "request.footprint",
335+
"syntax": "request.footprint(ticks_per_row, va_percent?, imbalance_percent?)",
336+
"overloads": [
337+
{
338+
"requiredParams": ["ticks_per_row"],
339+
"optionalParams": ["va_percent", "imbalance_percent"],
340+
"signature": "request.footprint(ticks_per_row, va_percent?, imbalance_percent?)",
341+
}
342+
],
343+
},
344+
"footprint.buy_volume": {
345+
"name": "footprint.buy_volume",
346+
"syntax": "footprint.buy_volume(id)",
347+
"overloads": [
348+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.buy_volume(id)"}
349+
],
350+
},
351+
"footprint.sell_volume": {
352+
"name": "footprint.sell_volume",
353+
"syntax": "footprint.sell_volume(id)",
354+
"overloads": [
355+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.sell_volume(id)"}
356+
],
357+
},
358+
"footprint.total_volume": {
359+
"name": "footprint.total_volume",
360+
"syntax": "footprint.total_volume(id)",
361+
"overloads": [
362+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.total_volume(id)"}
363+
],
364+
},
365+
"footprint.delta": {
366+
"name": "footprint.delta",
367+
"syntax": "footprint.delta(id)",
368+
"overloads": [
369+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.delta(id)"}
370+
],
371+
},
372+
"footprint.poc": {
373+
"name": "footprint.poc",
374+
"syntax": "footprint.poc(id)",
375+
"overloads": [
376+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.poc(id)"}
377+
],
378+
},
379+
"footprint.vah": {
380+
"name": "footprint.vah",
381+
"syntax": "footprint.vah(id)",
382+
"overloads": [
383+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.vah(id)"}
384+
],
385+
},
386+
"footprint.val": {
387+
"name": "footprint.val",
388+
"syntax": "footprint.val(id)",
389+
"overloads": [
390+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.val(id)"}
391+
],
392+
},
393+
"footprint.get_row_by_price": {
394+
"name": "footprint.get_row_by_price",
395+
"syntax": "footprint.get_row_by_price(id, price)",
396+
"overloads": [
397+
{
398+
"requiredParams": ["id", "price"],
399+
"optionalParams": [],
400+
"signature": "footprint.get_row_by_price(id, price)",
401+
}
402+
],
403+
},
404+
"footprint.rows": {
405+
"name": "footprint.rows",
406+
"syntax": "footprint.rows(id)",
407+
"overloads": [
408+
{"requiredParams": ["id"], "optionalParams": [], "signature": "footprint.rows(id)"}
409+
],
410+
},
411+
"volume_row.up_price": {
412+
"name": "volume_row.up_price",
413+
"syntax": "volume_row.up_price(id)",
414+
"overloads": [
415+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.up_price(id)"}
416+
],
417+
},
418+
"volume_row.down_price": {
419+
"name": "volume_row.down_price",
420+
"syntax": "volume_row.down_price(id)",
421+
"overloads": [
422+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.down_price(id)"}
423+
],
424+
},
425+
"volume_row.buy_volume": {
426+
"name": "volume_row.buy_volume",
427+
"syntax": "volume_row.buy_volume(id)",
428+
"overloads": [
429+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.buy_volume(id)"}
430+
],
431+
},
432+
"volume_row.sell_volume": {
433+
"name": "volume_row.sell_volume",
434+
"syntax": "volume_row.sell_volume(id)",
435+
"overloads": [
436+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.sell_volume(id)"}
437+
],
438+
},
439+
"volume_row.total_volume": {
440+
"name": "volume_row.total_volume",
441+
"syntax": "volume_row.total_volume(id)",
442+
"overloads": [
443+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.total_volume(id)"}
444+
],
445+
},
446+
"volume_row.delta": {
447+
"name": "volume_row.delta",
448+
"syntax": "volume_row.delta(id)",
449+
"overloads": [
450+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.delta(id)"}
451+
],
452+
},
453+
"volume_row.has_buy_imbalance": {
454+
"name": "volume_row.has_buy_imbalance",
455+
"syntax": "volume_row.has_buy_imbalance(id)",
456+
"overloads": [
457+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.has_buy_imbalance(id)"}
458+
],
459+
},
460+
"volume_row.has_sell_imbalance": {
461+
"name": "volume_row.has_sell_imbalance",
462+
"syntax": "volume_row.has_sell_imbalance(id)",
463+
"overloads": [
464+
{"requiredParams": ["id"], "optionalParams": [], "signature": "volume_row.has_sell_imbalance(id)"}
465+
],
466+
},
304467
"box.new": {
305468
"name": "box.new",
306469
"syntax": "box.new(top_left, bottom_right, ...) or box.new(left, top, right, bottom, ...)",
@@ -437,6 +600,7 @@ def load_builtin_data() -> BuiltinData:
437600
for name in raw.get("funcs", {}).keys()
438601
if not OVERLOAD_SUFFIX_RE.search(name)
439602
}
603+
raw_funcs.update(PYTHON_OVERRIDE_SPECS.keys())
440604

441605
namespace_members: dict[str, set[str]] = {}
442606
for full_name in raw_vars | raw_funcs:
@@ -449,6 +613,9 @@ def load_builtin_data() -> BuiltinData:
449613
for namespace, members in _load_namespace_constants().items():
450614
namespace_members.setdefault(namespace, set()).update(members)
451615

616+
for namespace, members in SUPPLEMENTAL_NAMESPACE_MEMBERS.items():
617+
namespace_members.setdefault(namespace, set()).update(members)
618+
452619
for namespace in ADDITIONAL_CONSTANT_NAMESPACES:
453620
namespace_members.setdefault(namespace, set())
454621

tests/test_pine_v6_docs_cases.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,22 @@ class PineV6DocsCasesTests(unittest.TestCase):
1010
def setUp(self) -> None:
1111
self.validator = PineScriptValidator()
1212

13+
def test_request_footprint_and_volume_row_types_are_accepted(self) -> None:
14+
code = """
15+
indicator("Footprint support", overlay = true)
16+
ticksPerRow = input.int(4, "Ticks per row", minval = 1)
17+
valueAreaPercent = input.int(70, "Value area percent", minval = 1, maxval = 100)
18+
imbalancePercent = input.int(300, "Imbalance percent", minval = 1)
19+
20+
footprint reqFootprint = request.footprint(ticksPerRow, valueAreaPercent, imbalancePercent)
21+
array<volume_row> rows = reqFootprint.rows()
22+
if array.size(rows) > 0
23+
volume_row row = array.get(rows, 0)
24+
x = row.total_volume()
25+
"""
26+
diagnostics = self.validator.validate_text(code)
27+
self.assertFalse(any(d.severity == Severity.ERROR for d in diagnostics))
28+
1329
def test_strategy_when_parameter_is_rejected(self) -> None:
1430
code = """
1531
strategy("Conditional strategy", overlay = true)

0 commit comments

Comments
 (0)