Skip to content

Xlang — 公式语言 ​

Xlang 是 Pangura 的小型时间序列脚本语言。它住在一个 Canvas 节点里(脚本 Script——同一节点承载两条车道),把接进来的上游节点输出——价格序列、因子读数、模型结果——变成你自己定义的因子、指标或信号。

它刻意做得很小:一个下午就能学完全部。本页是完整参考——这门语言没有任何内容在本页之外。

为什么是一门有界语言 ​

Xlang 由构造保证有界:每个脚本必然终止,每次运行结果确定,脚本能消耗的资源有硬上限。这不是事后加的限制,而是设计本身。因为语法上写不出无界循环、递归、IO、随机数和时钟读取,所以每个 Xlang 脚本读一遍就能审计、重跑一遍就能复现,放进夜间监控无人值守也安全。

Xlang 刻意不是三样东西:

  • **不是通用编程语言。**能力留在平台的算子和图拓扑里;语言保持小到一下午学会、一眼看完。
  • **不是 notebook。**任意用户代码会摧毁审计边界——没人能为黑箱作证。在 Xlang 里,危险的东西不是被政策禁止,而是语法上写不出来。
  • **不是裸 JSON 配置。**语法树是机器的存储格式;语言才是人书写和审读的界面。

快速上手 ​

把两个 Price Factor 节点(比如铜和金)接进一个脚本节点(Xlang 车道),粘贴:

input copper: series    # ← 第一个接线的上游
input gold: series      # ← 第二个接线的上游

let ratio = copper / gold
let z = zscore(ratio, 60)

# 路径依赖状态:数连续拉伸天数
let regime_days = 0
for i in 1..len(z) {
  if z[i] > 2 { regime_days = regime_days + 1 } else { regime_days = 0 }
}

output value  = z[len(z)-1]
output series = z
output signal = if regime_days > 5 { "risk_off" } else { "neutral" }

在节点卡上点 ▶ 试跑,立即用上一轮图结果求值;或直接运行整张图。节点卡会显示 value、signal、派生序列的走势线,展开后还能看到公式原文和它的 SHA-256 指纹。

执行模型:整序列、显式循环 ​

脚本每次求值跑一遍,面对的是完整对齐的序列。路径依赖逻辑(移动止损、政权计数、自定义回撤)用显式 for 循环写出来——没有隐藏的逐 bar 重执行。你读到什么,就运行什么。

类型 ​

类型说明
number64 位浮点。NaN 是合法值并会传播(见 NaN 纪律)。
booltrue / false。条件必须是布尔——if price { … } 报错;要写 if price > 0 { … }。
series数值数组,上限 20,000 点。由输入、切片和内建函数产生。
字符串仅限字面量标签(供 output signal 用);没有字符串运算。

语句 ​

input NAME: series      # 声明序列输入(绑定到接线的上游节点)
input NAME: number      # 声明标量输入

let NAME = expr         # 声明变量——赋值前必须先声明
NAME = expr             # 重新赋值(仅限已 let 的名字;给未声明的名字
                        #   赋值是解析错误——打字错当场拦住)

if cond { … } else { … }        # 语句形;else 可省
for i in a..b { … }             # 范围循环:a 含、b 不含;a >= b 时零次
for v in s { … }                # 逐点遍历序列 s

output value  = expr    # 标量读数(number)——Monitor 探针盯的就是它
output series = expr    # 派生序列(画在节点卡上)
output signal = expr    # 字符串标签,如 "risk_off"

脚本必须至少声明一个 output;每个输出槽(value/series/signal)至多出现一次。没有 while、没有递归、没有用户自定义函数(v1)、没有 import——这些在解析时就被拒绝,并附解释,不会被静默忽略。

表达式 ​

优先级从松到紧:or → and → 比较(> >= < <= == !=)→ + - → * / → 一元(-x、not x)→ 后缀(调用 f(x)、索引 s[i]、切片 s[a:b])。

  • 序列间算术逐点进行,要求等长;序列与数字混合时,数字作用到每个点。
  • if 表达式:if c { a } else { b }——表达式形里 else 必须有。
  • 索引从 0 起;最后一个点是 s[len(s)-1]。负索引非法(错误信息会提醒你 len(s)-1 的写法)。
  • 切片 s[a:b] 不含 b,越界自动收拢。
  • 布尔运算用单词 and / or / not——&&、||、! 会被拒绝并给出提示。

内建函数——完整清单 ​

二十二个函数。此外不存在任何函数;调用清单之外的名字会报错并列出可用的近似项。

函数返回语义
len(s)number点数;NaN 点照常计入。
sum(s)number求和。含 NaN → NaN;空 → NaN。
mean(s)number算术平均。含 NaN → NaN;空 → NaN。
std(s)number样本标准差(ddof = 1)。len < 2 → NaN;含 NaN → NaN。
min(s) / max(s)number极值。含 NaN → NaN;空 → NaN。
abs(x) / log(x) / sqrt(x)同型number 或 series,逐点。log(x ≤ 0) → NaN、sqrt(x < 0) → NaN——传播,不抛错。
lag(s, k)seriesout[i] = s[i−k];前 k 点 NaN;k = 0 即拷贝。
diff(s, k=1)seriesout[i] = s[i] − s[i−k];前 k 点 NaN。
pct_change(s, k=1)seriesout[i] = s[i]/s[i−k] − 1;前 k 点 NaN。
sma(s, w)series滚动均值;前 w−1 点 NaN;窗口含 NaN → 该点 NaN。
ema(s, w)seriesα = 2/(w+1);种子 = 前 w 点的 SMA(经典技术分析约定);之后 out[i] = α·s[i] + (1−α)·out[i−1];前 w−1 点 NaN。
zscore(s, w)series(s[i] − mean_w) / std_w,样本 std;前 w−1 点 NaN;std_w = 0 或窗口含 NaN → 该点 NaN。
corr(a, b)number整序列皮尔逊相关(样本口径)。长度必须相等;len < 2、任一侧零方差或含 NaN → NaN。
rank(s, w)series滚动百分位名次 = count(窗口 ≤ s[i]) / w,取值 (0, 1];前 w−1 点 NaN。
percentile(s, p)numberp ∈ [0, 100],线性插值(numpy linear 同法);含 NaN → NaN。
crossover(a, b)series0/1 序列:a 上穿 b(b 可为序列或数字阈值)。out[0] = NaN;参与判定的点含 NaN → 该点 NaN。
crossunder(a, b)seriescrossover 的镜像:a 下穿 b。
highest(s, w) / lowest(s, w)series滚动最大 / 最小;前 w−1 点 NaN。

EMA 种子约定

Xlang 的 ema 用经典技术分析约定:第一个有效点是前 w 点的 SMA。pandas 的 ewm(span=w, adjust=False) 则以首值为种子,两者要等种子影响衰减后才一致(约七个窗口长度后差距缩到 10⁻⁶ 以内)。和 pandas 对数时,请对尾部。

NaN 纪律 ​

NaN 是数据,不是错误。它来自过短的历史和滚动窗口的暖机期,并且严格传播:任何触到 NaN 的算术或聚合都得 NaN;滚动函数在窗口不完整或被污染的位置输出 NaN。序列永远保持输入长度——暖机期用 NaN 填充,绝不裁短。如果最新读数是 NaN,诚实的解读就是"干净历史不够长",夜间监控记录的也正是这个(nodata,绝不沿用昨值)。

输入:绑定与对齐 ​

**绑定住在边上(方案 A,2026-08)。**点选脚本节点的入边,设定这条线喂哪个输入(+可选字段路径)。绑定是边自身的属性:上游换标的绑定不断,断线即断绑,结果卡上此类输入标注 (edge)。优先级:边绑定 > 输入映射 > 自动绑定——输入映射保留为高级兜底。两条车道对边绑定的消费完全一致;Python 车道上边绑定即事实上的输入声明,未绑定的边保留 u0、u1……(序号=原始接线序)。

序列输入按声明顺序自动绑定接线的上游节点:第一个 input …: series 拿第一个能给出可用序列的上游,第二个拿下一个,依此类推。数字输入按自己的名字在各上游的数值字段里查找。输入名是给读者起的——copper、gold、spread——接线靠的是位置。

自动绑定不合意时,节点的 Input map 字段一行管一个输入。Input map 是脚本框下方单独的小框——映射行永远不写进脚本(脚本以 input 声明开头):

# 输入名 = 数据来源
copper = upstream[0].price_series   # 第一个接线节点(0 起,与 s[0] 同规)
gold   = upstream[1].price_series   # 第二个接线节点的显式字段
spread = basis.mean_bps             # 路径,在所有上游里依次查找
k      = 2.5                        # 数字字面量(仅限 number 输入)

upstream[N] 按接线顺序数节点,从 0 起——和序列索引同一条规则(s[0] 是第一个点),也和结果卡上绑定行显示的完全一致。裸写 0.price_series 会被拒绝并提示正确写法(它看起来像小数)。

多条序列长度不一时,按尾部对齐到最短一条——最近的点互相配对,更早的多余段被截除,截了多少记进节点结果。节点卡会明示("tail-trimmed to N pts"),对齐永远不是静默的。

资源上限与失败语义 ​

上限数值
执行步数5,000,000(序列运算按元素计费)
序列长度20,000 点
累计分配元素200,000
墙钟2 秒

超限即停,错误信息用白话点名是哪条上限。失败的脚本绝不产出残缺数字:Canvas 上节点卡显示行列错误和脚本原文;夜间监控里该读数记 nodata 并附原因——绝不沿用昨值。

错误信息说人话 ​

错误带行列,并直接告诉你怎么改:

line 1, col 1: 'while' is not in this language — loops are bounded:
  write 'for i in a..b { … }' or 'for v in series { … }'

line 2, col 1: 'momentom' is not declared — write 'let momentom = …'
  first (this catches typos early)

溯源 ​

每次执行结果都带语言版本、脚本源码和脚本的 SHA-256 指纹。浏览器里跑试跑的求值器和服务器上跑夜批的求值器是逐字节相同的孪生体,由校验和测试锁定;求值器本身钉在 golden 测试套件上(30 个规范用例对照独立 NumPy 参考实现,另与平台自身的指标算子交叉对账)。同脚本、同输入、同答案——这是一条可测试的承诺,不是口号。

歧伯,语言台 ​

在节点配置对话框里让歧伯起草——一句话就行:"铜金比拉伸到两个 sigma 时提醒我";也可以让它用白话解释一段现成脚本。草稿先过服务端验证再给你看:必须解析通过,并在合成输入上完成一次试跑。没过验证的草稿永远不会出现;草稿要你明确采用,才会写进节点。

三个完整示例 ​

**1. 跨资产比值政权(上文快速上手脚本)。**比值 → 滚动 z 分数 → 显式循环数连续拉伸天数 → value + series + signal 三路输出。

2. 最大回撤,路径依赖:

input s: series
let peak = s[0]
let max_dd = 0
for v in s {
  if v > peak { peak = v }
  let dd = 0
  dd = v / peak - 1
  if dd < max_dd { max_dd = dd }
}
output value = max_dd

运行峰值循环,没有哪个内建函数直接给——有界 for 存在的意义正是这类逻辑。

3. 带标量输入的阈值闸:

input s: series
input k: number
output signal = if mean(s) > k { "above" } else { "below" }

k 可以在 input map 里绑定字面量(k = 2.5),也可以绑定任何接线节点的数值字段——同一个脚本因此成了可复用、可参数化的闸门。

三个示例全部锁在求值器的 golden 套件里——你刚读完的文档是被测试覆盖的。

Python 车道(2026-08) ​

同一个脚本节点提供第二条车道:Python 3.13,沙箱执行。在节点配置顶部选择语言。

Xlang 车道Python 车道
契约input 声明 → 逻辑 → outputdef run(inputs),返回 {"value"/"series"/"signal": …};inputs 是具名 pandas.Series 字典
词汇表22 个内建函数numpy 2.2.6 · pandas 3.0.1 · scipy 1.17.1 · statsmodels 0.14.6 · scikit-learn 1.8.0 · openpyxl 3.1.5(精确锁版白名单,与主服务同版——沙箱镜像里只装这六个包;BLAS 钉单线程保确定性)
执行位置进程内求值器(浏览器试跑=夜批,逐字节孪生)一次性服务端容器:断网、只读文件系统、降权用户、CPU/内存/进程数上限
确定性构造保证——语言写不出随机与 IO探针验证:试跑连跑两次,输出哈希一致发**「双跑一致」徽章**;此后任一次同代码同输入产出不同结果,自动摘徽章并告警
溯源脚本 SHA-256 + 求值器版本脚本 SHA-256 + 沙箱镜像 digest + 输入/输出哈希 + 容器内实测的解释器与包版本
签署与入卷签署链默认车道读数携带 python-sandbox 车道标签;哈希链溯源等强,人签可鉴证性如实弱一档

绑定层两条车道共用:输入映射语法完全一致;不写映射时,Python 车道自动把每个接线上游的主序列绑为 u0、u1……(序号与 upstream[N] 同规)。长度不一的序列同样尾截齐最短。

沙箱 harness 本身——序列注入、原生类型转换、规范输出哈希规则、双跑探针——由独立 golden(python_sandbox_harness,Tier 2)按纯 stdlib 参考锁定。

Verifiable intelligence for the decisions that demand scrutiny.