使用手册
功能在哪 · 是什么 · 怎么调出来一、thinkScript 是什么
thinkScript 是 thinkorswim(现属 Schwab 嘉信)平台内置的专有脚本语言,语法风格类似 Pascal。官方给它的定位是:在平台内自建分析工具的语言,能写四样东西:
- 指标(Study):在图表上画线、画箭头、画柱状图
- 策略(Strategy):在图上标注买卖信号并做回测(Backtest)
- 扫描器(Scan):从全市场筛出符合条件的标的
- 报警 / 条件单:条件满足时提醒你,或挂一笔手动预设的条件订单
官方明确说明:thinkScript 可以创建"自定义分析工具——指标、观察列表列、报警、扫描查询,甚至条件订单"(Schwab 官方视频原文)。
白话注解:thinkScript 就是 thinkorswim 里的"Excel 公式"——你写条件,它帮你画线、选股、响铃提醒。但它不是自动下单语言。策略回测很强,实盘自动交易不在它的能力范围内。
1、与其他策略语言的差别
| 对比项 | thinkScript | EasyLanguage (TradeStation) | Pine Script (TradingView) |
|---|---|---|---|
| 运行位置 | thinkorswim 桌面/Web 端 | TradeStation | TradingView |
| 下单函数 | AddOrder()(仅回测) | Buy()/Sell() 可接实盘 | strategy.entry() 仅回测 |
| 内置止损 | 无,需用 EntryPrice() 手写 | 有 SetStopLoss() | 有 strategy.exit() |
| 期权支持 | 极强(平台强项) | 一般 | 弱 |
警告:网上大量"thinkScript 教程"照抄 EasyLanguage 写法,出现Buy("LE") if ...、SetStopLoss(50)这类代码——这些在 thinkScript 里全部编译报错。本文所有代码均已对照官方 Learning Center 核实。
操作教程
一步一步 · 跟着做完一件事二、开发环境与第一个脚本
1、打开编辑器
- 打开 thinkorswim 桌面版(首次加载较慢,耐心等)
- 图表页 → 点 Studies(研究)图标 → Edit Studies and Strategies
- 右侧列表里点 Create(新建),或选一个内置指标点旁边的滚动图标进入源码
- 写完点 OK,自动编译,报错会在编辑器底部提示
2、读内置源码是最好的学习方式
平台自带几百个指标,全部是 thinkScript 写的,源码公开可读。官方推荐的学习路径就是:打开内置 ATR 指标 → 看它的 declare lower;、input、plot 结构 → 点 Duplicate(复制)→ 改成自己的。
白话注解:别凭空硬写。想写什么指标,先看平台有没有类似的,复制一份源码慢慢改,比自己从零写快十倍。
3、第一个脚本
# 双均线指标(最小可运行示例)
input fastLength = 10;
input slowLength = 30;
plot fastMA = Average(close, fastLength);
plot slowMA = Average(close, slowLength);
fastMA.SetDefaultColor(GetColor(1));
slowMA.SetDefaultColor(GetColor(2));
每条语句以分号结尾;# 开头是单行注释;{ } 包起来的是块注释。
白话注解:三行核心代码就能画两条均线。input= 用户可调的参数,plot= 画到图上的线。
三、脚本基本结构与三种声明
1、三种变量声明
input length = 14; { 可调参数,加载后在设置面板里改 }
def price = close; { 内部变量,不显示在图上 }
plot out = Average(close, length); { 画到图上的线 }
| 关键字 | 作用 | 显示在哪 |
|---|---|---|
input | 对外参数 | 指标设置面板 |
def | 中间变量(值会被缓存) | 不显示 |
plot | 输出序列 | 图上(或报告里) |
rec | 递归变量(可引用自己上一根K线的值) | 不显示 |
白话注解:def和plot都是"每根K线算一个值"的序列。区别只有一个:plot会画出来,def是幕后算的。多用def存中间结果能提速,因为值被缓存了。
2、declare 声明行
写在脚本开头,告诉平台这个脚本的基础形态:
declare lower; { 指标画在副图(价格下方的子图),如 MACD/RSI }
declare upper; { 画在主图,与K线同图 }
declare hide; { 整个脚本默认隐藏 }
declare once_per_bar; { 每根K线只触发一次(报警类脚本常用) }
declare real_size; { 回测使用真实成交量口径 }
3、数据类型
thinkScript 只有三种基础数据:
| 类型 | 示例 |
|---|---|
| 数值(double) | def x = close * 1.05; |
| 布尔(是/否) | def up = close > open; |
| 字符串 | AddLabel(yes, "现价" + close, Color.BLACK); |
布尔值用 yes / no 或 true / false 表示。字符串拼接用 +。
4、常量与特殊值
def na = Double.NaN; { 空值 }
def maxv = Double.POSITIVE_INFINITY;
def minv = Double.NEGATIVE_INFINITY;
def bn = BarNumber(); { 当前是第几根K线 }
def bad = IsNaN(close); { 判断是否为空值 }
警告:空值不能直接比较大小——NaN > 100结果是"否"而不是报错,这类隐藏逻辑漏洞很常见。判断空值一律用IsNaN()。
四、价格数据与历史偏移
1、基础价格序列
| 名称 | 含义 |
|---|---|
open / high / low / close | 开高低收 |
volume | 成交量 |
hlc3 | (最高+最低+收盘)/3 |
ohlc4 | (开+高+低+收)/4 |
imp_volatility | 隐含波动率 |
bid / ask | 当前买卖价( 仅当前值,不能取历史) |
2、历史偏移 [n]:方括号取过去,负数取未来
def prevClose = close[1]; { 上一根K线的收盘价 }
def close3 = close[3]; { 三根K线前 }
def nextOpen = open[-1]; { 下一根K线的开盘价 }
白话注解:正数往回看(过去),负数往前看(未来)。open[-1] 是合法的,因为信号确认后成交在"下一根K线开盘"——这正是回测的成交假设。
警告:未来函数。除了open[-1]这类明确用于"下一根K线成交"的写法,任何close[-1]、high[-2]都是在偷看未来数据。回测会好看得离谱,实盘必死。写完策略必须检查所有负数偏移。
3、最高最低
def hh = Highest(high, 10); { 最近10根K线的最高价 }
def ll = Lowest(low, 10); { 最近10根K线的最低价 }
五、input 参数与枚举选择
1、普通 input
input length = 14;
input price = close; { 价格类型也是 input }
input showLabel = yes; { 布尔开关 }
input text = "我的指标"; { 字符串 }
2、枚举 input:让用户下拉选择
input smoothingType = {Default SMA, EMA, WMA};
花括号列出候选值,Default 标记默认选中项。用 switch 读取选择(见第十九章)。
3、均线类型枚举 AverageType
内置均线类指标统一用 AverageType 枚举选平滑方式:
input averageType = AverageType.WILDERS;
常用值:SIMPLE(简单)、EXPONENTIAL(指数)、WILDERS(怀尔德,RSI/ATR 用)、HULL(赫尔)、WEIGHTED(加权)。
4、#Hint 提示
input length = 14; #Hint length: RSI 计算周期
#Hint 写在 input 行后面,鼠标悬停参数时会显示这段说明。写给别人用的脚本建议都加上。
白话注解:枚举 input 就像软件里的"下拉菜单"——不用让用户填数字,直接选"SMA 还是 EMA",防呆。
六、条件、运算符与交叉判断
1、比较与逻辑
def up = close > open;
def rsi = reference RSI();
def buy = rsi < 30 and close > open; { and / or / not }
2、if 表达式
def color = if close > open then Color.GREEN else Color.RED;
完整形式为 if 条件 then 值1 else 值2,必须有 else 分支(作为表达式时)。
3、交叉:crosses above / crosses below
plot signal = close crosses above Average(close, 20);
plot signal2 = close crosses below Average(close, 20);
等价的函数写法(官方 Alert 示例使用):
Crosses(close, Average(close, 15), CrossingDirection.ABOVE)
Crosses(fast, slow, CrossingDirection.BELOW)
Crosses(a, b, CrossingDirection.ANY) { 双向交叉 }
4、within N bars:N 根K线内
def buySignal = (macd crosses above 0) and rsi crosses below 30 within 5 bars;
白话注解:within 5 bars = "最近 5 根K线内发生过"。两个条件不用同一根K线出现,只要在窗口期内先后出现就算。写"MACD 金叉后 RSI 很快回落"这类组合条件非常方便。
七、画线与外观控制
1、SetPaintingStrategy:20 种画法
plot 画出来是什么形状,由 SetPaintingStrategy 决定(官方 Chapter 8):
plot avg = Average(close, 15);
avg.SetStyle(Curve.SHORT_DASH); { 线型:短虚线 }
avg.SetLineWeight(3); { 线宽:1~5 像素 }
常用 PaintingStrategy 常量(完整 20 种见官方手册):
| 常量 | 效果 |
|---|---|
LINE | 默认折线 |
POINTS / SQUARES / TRIANGLES | 点 / 方块 / 三角 |
HISTOGRAM / SQUARED_HISTOGRAM | 柱状图(副图) |
BOOLEAN_ARROW_UP / BOOLEAN_ARROW_DOWN | 条件为真时在 K 线下方/上方画箭头 |
BOOLEAN_POINTS / BOOLEAN_WEDGE_UP | 布尔点 / 布尔楔形 |
ARROW_UP / ARROW_DOWN | 在 plot 数值位置画箭头(可自定义高度) |
HORIZONTAL | 水平线 |
VALUES_ABOVE / VALUES_BELOW | 只显示数值标签 |
官方经典示例——金叉死叉自动画箭头:
plot isAbove = Average(close, 5) crosses above Average(close, 20);
plot isBelow = Average(close, 5) crosses below Average(close, 20);
isAbove.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP);
isBelow.SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_DOWN);
白话注解:BOOLEAN_ARROW_UP= 条件为真的那根K线,在 K 线下方自动画一个向上箭头。金叉画绿箭头、死叉画红箭头,买卖点一眼看清。注意:BOOLEAN_ARROW_UP画的位置固定在最低价下方;想让箭头出现在指定价位,用普通ARROW_UP——它画在 plot 的数值处。
警告:原文的SetShape(ShapeUpArrow)不是 thinkScript 语法,正确写法是SetPaintingStrategy(PaintingStrategy.BOOLEAN_ARROW_UP)。原文的SetColor也应为SetDefaultColor。
2、颜色
plot ma = Average(close, 20);
ma.SetDefaultColor(Color.GREEN); { 内置颜色 }
ma.SetDefaultColor(GetColor(1)); { 调色板第1号颜色 }
ma.SetDefaultColor(CreateColor(255, 0, 0)); { RGB 自定义 }
3、动态隐藏:Hide 与 SetHiding
plot SMA5 = Average(close, 5);
plot SMA10 = Average(close, 10);
SMA10.hide(); { 默认隐藏,设置里可再打开 }
SMA15.setHiding(getAggregationPeriod() < AggregationPeriod.DAY); { 日内图隐藏 }
4、数值动态着色
plot rsi = reference RSI();
rsi.AssignValueColor(if rsi > 70 then Color.RED else if rsi < 30 then Color.GREEN else Color.GRAY);
八、标签、气泡与价格着色
1、AddLabel:图上角标
AddLabel(yes, "RSI = " + Round(reference RSI(), 2),
if reference RSI() > 70 then Color.RED else Color.GREEN);
第一个参数是开关,第二个是文字,第三个是颜色。数字拼进字符串时建议先 Round() 保留两位小数。
2、AddChartBubble:K 线旁气泡
def buyCond = close crosses above Average(close, 20);
AddChartBubble(buyCond, low, "买", Color.GREEN, no); { no = 气泡在K线下方 }
AddChartBubble(close crosses below Average(close, 20), high, "卖", Color.RED, yes);
3、AddCloud:两线之间填色云
plot fast = Average(close, 10);
plot slow = Average(close, 30);
AddCloud(fast, slow, Color.GREEN, Color.RED); { fast在上显绿,在下显红 }
4、AssignPriceColor:整根K线染色
AssignPriceColor(if close > Average(close, 20) then Color.GREEN else Color.RED);
5、全局颜色
DefineGlobalColor("上涨", Color.GREEN);
AssignPriceColor(if close > open then GlobalColor("上涨") else Color.RED);
白话注解:标签=挂在图角落的小牌子;气泡=贴在某根K线旁边的便签;云=两条线之间涂色。这三个是做"可视化面板"三件套。
九、报警 Alert
1、官方语法
官方函数参考:
Alert(condition, text, alertType, sound);
| 参数 | 默认值 | 说明 |
|---|---|---|
| condition | — | 触发条件 |
| text | — | 报警弹窗文字 |
| alertType | Alert.ONCE | 触发频率 |
| sound | Sound.NoSound | 提示音 |
alertType 三种: Alert.ONCE(加到图后只触发一次)、Alert.BAR(每根K线最多一次)、Alert.TICK(每个 tick 都可能触发)。
sound 五种: Sound.Bell / Sound.Chimes / Sound.Ding / Sound.Ring / Sound.NoSound。
2、官方示例
# 示例1:收盘价上穿15周期SMA(用 Crosses 函数写法)
Alert(Crosses(close, Average(close, 15), CrossingDirection.ABOVE),
"收盘价上穿 SMA!");
# 示例2:快线上穿慢线,每根K线最多响一次,带提示音
def condition = Crosses(Average(close, 15), Average(close, 30), CrossingDirection.ABOVE);
Alert(condition, "Bullish!", Alert.BAR, Sound.Ring);
# 示例3:简单写法
Alert(open > 400, "开盘价大于400", Alert.ONCE, Sound.Chimes);
3、使用方式与限制
- 写好含 Alert 的指标 → 图表上点铃铛图标选 Study Alert → 选中该指标激活
- 条件满足时弹窗+响铃(手机端可收推送)
官方注记:当 Alert() 内调用其他函数时(如 Alert(HasEarnings())),只取最后一根真实K线上的函数值来判断。历史K线上的值不参与触发。
警告:原文的Alert(condition, "金叉出现", AlertSound.Bell)写法有误——没有AlertSound这个常量集,声音参数是Sound.Bell,且条件参数不能省略。
白话注解:报警是 thinkScript 派上实盘用的第一途径——不用盯盘,条件到了响铃提醒你,人再决定下不下单。日内写法建议用 Alert.BAR,避免一根K线里反复响。
十、AddOrder 全参数精讲
1、完整签名(官方原文)
AddOrder(int type, IDataHolder condition, IDataHolder price,
IDataHolder tradeSize, CustomColor tickColor,
CustomColor arrowColor, String name);
| 参数 | 默认值 | 说明 |
|---|---|---|
| type | OrderType.BUY_AUTO | 方向+开平,见第十二章 |
| condition | — | 为真时下一根K线加单 |
| price | open[-1] | 成交价(默认下一根K线开盘价) |
| tradeSize | 策略全局设置 | 手数;写了就覆盖全局设置 |
| tickColor | Color.MAGENTA | 成交价小点的颜色 |
| arrowColor | Color.MAGENTA | 信号箭头颜色 |
| name | 策略名 | 显示在箭头标注、订单页和报告里 |
2、官方完整示例
AddOrder(OrderType.BUY_AUTO, close > close, open[-1], 50,
Color.ORANGE, Color.ORANGE, "Sample buy @ " + open[-1]);
官方解释:当前收盘高于前一根收盘时,在下一根K线开盘价开多(或平空)50 股,橙色信号,箭头旁显示买入价。
白话注解:open[-1]是灵魂参数——信号在当前K线确认,成交在下一根K线开盘。这模拟了"收盘看到信号 → 下单 → 次日开盘成交"的真实流程。千万别改成close当成交价,那是用还没走完的价格成交,属于作弊回测。
3、三个使用规则
- 信号是条件驱动的:条件为真的每根K线都会尝试加单,没有"只下一单"的状态记忆,防重复要自己写(第十四章)
name参数写清楚含义,报告里按 name 分组看统计更直观- tradeSize 建议留给全局设置统一管理,只在特殊单上单独指定
十一、OrderType 六常量与配对逻辑
官方只有 6 个 OrderType 常量,分两套配对使用:
| 常量 | 作用 |
|---|---|
OrderType.BUY_AUTO | 开新多或平掉空单(自动) |
OrderType.SELL_AUTO | 开新空或平掉多单(自动) |
OrderType.BUY_TO_OPEN | 只开多(期权=买入开仓) |
OrderType.SELL_TO_CLOSE | 只平多 |
OrderType.SELL_TO_OPEN | 只开空 |
OrderType.BUY_TO_CLOSE | 只平空 |
1、两套写法的区别
# 写法一:AUTO 对 永远在场(多空反手)
AddOrder(OrderType.BUY_AUTO, 金叉条件);
AddOrder(OrderType.SELL_AUTO, 死叉条件);
{ 死叉出现时:平掉多仓 + 反手开空,一步完成 }
# 写法二:TO_OPEN / TO_CLOSE 对 只做多,空仓期观望
AddOrder(OrderType.BUY_TO_OPEN, 金叉条件);
AddOrder(OrderType.SELL_TO_CLOSE, 死叉或止损条件);
白话注解:AUTO套件是"双向永远持仓"——多翻空、空翻多一步到位。TO_OPEN/TO_CLOSE套件是"单向操作"——只做多不做空,空仓时金叉再进场。大多数趋势策略用第二套更贴近实际操作。
新手最常见错误:开仓用BUY_TO_OPEN,平仓却写成SELL_TO_OPEN——那又开了个新空单,仓位越搞越大。开平必须成对:OPEN 配 CLOSE。
十二、EntryPrice() 与止损止盈
1、官方定义
EntryPrice():返回入场订单的价格;同方向多次加仓时返回平均成本。
官方示例(原文)——入场价上方 3 点止盈、下方 9 点止损:
AddOrder(OrderType.SELL_TO_CLOSE,
close > EntryPrice() + 3 or close < EntryPrice() - 9);
2、ATR 止损实战版
input stopMult = 2.0;
input targetMult = 4.0;
def atr = reference ATR(length = 14);
def entry = EntryPrice();
AddOrder(OrderType.BUY_TO_OPEN, 金叉条件, open[-1], name = "LE");
AddOrder(OrderType.SELL_TO_CLOSE,
close > entry + targetMult * atr
or close < entry - stopMult * atr,
name = "LX");
3、三条官方/实测限制(必须背下来)
- 只在策略(Strategy)里有效:同样的代码放进普通指标(Study),输出恒为空值 NA。不能用它给指标画"成本线"。
- 空仓时返回 NaN:判断是否持仓可以用
isNaN(EntryPrice())(第十四章实战用)。 - 条件单(Conditional Order)里不可用:条件单无法读到回测策略的入场价,要手动填固定值。
4、SetStopLoss 不存在
警告:thinkScript 没有SetStopLoss()函数——那是 EasyLanguage 的内置止损。thinkScript 的止损必须像上面那样,用EntryPrice()算出止损价并写进平仓条件。原文教程的SetStopLoss(stopLoss);编译会报错。
白话注解:把这个模式背下来——"开仓记下成本(EntryPrice 自动帮你记),平仓条件里写:价格碰到成本±N 就出"。这就是 thinkScript 版的止损止盈,没有捷径。
十三、防重复下单与状态机
1、问题:信号成立期间每根K线都触发
如果金叉后均线保持多头排列,fastMA > slowMA 这种"状态型"条件会每根K线都加一次仓。需要"状态机":只在状态切换的那一瞬间下单。
2、解法一:用 crosses 只抓切换点
AddOrder(OrderType.BUY_TO_OPEN, fastMA crosses above slowMA); { 只在穿越瞬间 }
3、解法二:rec 状态标记
def buySig = fastMA crosses above slowMA;
rec holding = if buySig then yes
else if 平仓条件 then no
else holding[1]; { 其他时间维持原状态 }
AddOrder(OrderType.BUY_TO_OPEN, buySig and !holding[1], name = "LE");
AddOrder(OrderType.SELL_TO_CLOSE, 平仓条件 and holding, name = "LX");
4、解法三:isNaN(EntryPrice()) 判空仓
实战常用套路——只有空仓时才允许更新挂单条件(社区验证写法,配合官方 EntryPrice 语义):
def inTrade = !isNaN(EntryPrice());
rec buyLevel = if 金叉 and isNaN(EntryPrice()) then open[-1] + 0.5 * ATR
else buyLevel[1];
白话注解:rec变量像"记事本"——这根K线写进去的值,下一根K线还能读到(用变量名[1])。金叉那一刻写"yes",之后每根K线沿用上一次的记录,直到平仓条件把它擦成"no"。这就是把"事件"变成"状态"的全部秘密。
5、计数器:统计开仓次数
rec tradeCount = if 新开仓信号 then tradeCount[1] + 1
else if isNaN(tradeCount[1]) then 0
else tradeCount[1];
AddLabel(yes, "开仓次数: " + tradeCount, Color.BLUE);
十四、多周期数据(Secondary Aggregation)
1、period 参数
plot dailyClose = close(period = AggregationPeriod.DAY);
plot weeklyClose = close("IBM", period = AggregationPeriod.WEEK);
plot yesterdayHigh = high(period = AggregationPeriod.DAY)[1];
AggregationPeriod 常用值:MIN, TWO_MIN ... HOUR, TWO_HOURS, FOUR_HOURS, DAY, TWO_DAYS, WEEK, MONTH,也支持字符串形式 "15 min"、"Day"。
2、官方三条规则(违反必出错)
- 副周期不能小于主周期:日线上不能取
close(period = AggregationPeriod.HOUR),会出错。 - 一个变量里不能混两种副周期:官方原文示例
close(period = MONTH) + close(period = WEEK)写在同一个表达式里是不允许的。正确做法是分存两个def再相加。 - 必须先存进 def 再做运算:
Average(close(period = DAY), 10)计算的是"日线数据画在当前图"——想对日线序列算 10 日均线,要先def dc = close(period = DAY);再Average(dc, 10)。
# 官方正确示例:周线随机指标
declare lower;
input KPeriod = 10;
input slowing_period = 3;
def wkClose = close(period = AggregationPeriod.WEEK);
def wkHigh = high(period = AggregationPeriod.WEEK);
def wkLow = low(period = AggregationPeriod.WEEK);
def lowest_k = Lowest(wkLow, KPeriod);
def fastK = if wkHigh - lowest_k != 0
then (wkClose - lowest_k) / (wkHigh - lowest_k) * 100 else 0;
plot FullK = Average(fastK, slowing_period);
注意:多周期语法不能用在条件单(Conditional Order)里,条件单只支持主周期数据。想要"日线条件触发日内下单",得拆成两个报警/条件单配合。
白话注解:多周期就是"5分钟图上看日线数据"。记住三不:不能往小了取、不能一个变量混两个周期、要先存 def 再算。
十五、跨品种数据引用
1、close("代码") 语法
plot spy = close("SPY"); { 取 SPY 的收盘价 }
plot xlf = close("XLF", period = AggregationPeriod.WEEK); { 周线 XLF }
plot vix = close("VIX"); { VIX 恐慌指数 }
2、实用场景:大盘过滤
# 个股金叉 + 标普在 200 日线上方才允许开多
def spyBull = close("SPY") > Average(close("SPY"), 200);
AddOrder(OrderType.BUY_TO_OPEN,
金叉条件 and spyBull, name = "LE");
3、Fundamental 函数
官方教程里多周期与跨品种的高级写法是 Fundamental():
def wkClose = Fundamental(close, period = AggregationPeriod.WEEK);
它把"取什么价格 + 什么周期"合成一个函数调用,写通用模板时用得多。
白话注解:跨品种引用是 thinkScript 的强项——"大盘涨我才买"这种联动逻辑一行代码。注意引用的代码必须打引号,且数据要能加载到(写错代码名会一直出空值)。
十六、fold 循环与 GetValue
1、官方语法
def 变量 = fold 索引 = 起始 to 终止 [with 累加器 = 初值] [while 条件] do 表达式;
索引从起始(含)遍历到终止(不含),每次把 do 后面的结果存进累加器。官方两个经典例子:
# 阶乘:p 从 1 乘到 n
input n = 10;
plot factorial = fold index = 1 to n + 1 with p = 1 do p * index;
# 手写 SMA:用 getValue 逐根取价累加
input length = 9;
plot SMA = (fold i = 0 to length with s do s + getValue(close, i, length - 1)) / length;
2、GetValue
GetValue(序列, 偏移, 最大回看):取序列往前第 offset 根的值。fold 循环里想取"第 i 根之前的数据"就得用它,因为方括号里通常只能放常量。
3、实战:统计最近 N 根内满足条件的次数
input n = 20;
def cnt = fold i = 0 to n with s = 0 do s + (if close[i] > open[i] then 1 else 0);
plot 阳线占比 = cnt / n;
性能警告:fold 是真循环,嵌套 fold 或大区间 fold 会明显拖慢图表加载。能不用就不用,优先用Sum()、Average()这类内置聚合函数(上例其实可写Sum(close > open, n) / n)。
白话注解:fold 是 thinkScript 里唯一的循环,类似 Python 的 for。90% 的场景被内置函数覆盖,只有"逐根回看做复杂统计"才需要它。
十七、switch 语句与枚举输入
1、官方标准示例
input smoothingType = {Default SMA, EMA};
def base;
switch (smoothingType) {
case SMA:
base = Average(close, 12);
case EMA:
base = ExpAverage(close, 12);
}
plot UpperBand = base * 1.1;
plot LowerBand = base * 0.9;
2、语法要点
def 变量;先声明不赋值,在 switch 各分支里分别赋值- case 值必须与枚举 input 的选项一一对应,漏一个分支报错
- 官方 Chapter 11 还有更高级的用法:case 里直接给多个 plot 赋值
3、枚举值的下标判断
枚举 input 参与比较时按下标(从 0 开始):
input AlertType = {default "BAR", "ONCE", "TICK"};
def at = AlertType;
Alert(条件, "提示",
if at == 1 then Alert.ONCE else if at == 2 then Alert.TICK else Alert.BAR);
白话注解:switch 就是"多选一开关"。想让别人用你的脚本时选"用SMA还是EMA",用枚举 input + switch 是标准解,别让人填数字。
十八、rec 递归与持仓期统计
1、rec 基本用法
rec(recursive)变量可以引用自己的历史值,这是实现"累加、计数、状态保持"的唯一正路:
rec cnt = if 条件 then cnt[1] + 1 else cnt[1]; { 累计计数 }
rec sum = rec + close; { 滚动累加 }
注意:第一根K线上 rec[1] 没有历史值(是 NaN),累加前要处理初始值,否则整个序列污染成 NaN。标准写法:
2、CompoundValue 处理初始值
CompoundValue(1, 递归表达式, 初值):第 1 根K线用初值,之后用递归表达式。社区验证写法:
rec runningMax = CompoundValue(1, if high > runningMax[1] then high else runningMax[1], high);
plot drawdownFromPeak = high / runningMax - 1; { 距峰回撤 }
3、实战:浮动盈亏曲线 FPL
平台内置了策略浮动盈亏函数 FPL()(Floating P/L),官方内置脚本直接可用:
declare lower;
plot FPL = FPL();
FPL.SetPaintingStrategy(PaintingStrategy.SQUARED_HISTOGRAM);
FPL.DefineColor("Positive", Color.GREEN);
FPL.DefineColor("Negative", Color.RED);
FPL.AssignValueColor(if FPL >= 0 then FPL.Color("Positive") else FPL.Color("Negative"));
plot ZeroLine = 0;
把它和策略挂同一张图,下方出现浮盈柱状图——每一笔持仓期间的盈亏起伏一目了然,是检验"能不能拿住单"的利器。
白话注解:rec 是序列版的"静态变量"。回撤统计的套路永远一样:rec 记住历史最高点 → 当前价除以它减 1 = 距峰回撤。给策略挂个 FPL 副图,你就知道这笔单最惨的时候浮亏过多少。
十九、扫描器 Stock Hacker
1、扫描脚本
扫描器里 thinkScript 的输出约定:一个布尔 plot,为真的标的入选:
# 放量突破 20 日均线
plot scan = close crosses above Average(close, 20)
and volume > Average(volume, 20) * 1.5;
2、使用步骤
- Scan 标签 → Stock Hacker → 公共过滤器里选 Custom Study(自定义研究)
- 载入上面的脚本(或直接在 Scan 标签页写)
- 选市场范围 → Run Scan → 结果可存为观察列表(Watchlist)
3、扫描的三条限制
- 扫描在固定周期上跑(扫描设置里选),脚本里的
AggregationPeriod只能往大取 - 引用其他自定义脚本时,被引用脚本里若有
AddLabel/AddChartBubble等绘图语句不影响扫描,但 plot 必须输出布尔值 - 复杂扫描配
within N bars很好用:def scan = (macd crosses above 0) and rsi crosses below 30 within 5 bars;
4、扫描 报警 观察列表 全自动盯盘
扫描结果存成观察列表后,可以给观察列表挂列报警:某一列(比如自定义的 thinkScript 列)值变化时通知你。组合拳:扫描选股 → 观察列表 → thinkScript 报警盯盘 → 人再下单。这就是 thinkorswim 生态里最接近"自动化"的合法路径。
白话注解:扫描器=机器替你看几千只股票。写个 plot scan = 条件; 扔进 Stock Hacker,它每天帮你把符合条件的目标抓出来。注意扫描脚本的 plot 要输出"是/否",不要输出价格。
💬 用户评价 (0)
立即登录