教程 · 单套解锁 教程 · EA量化教学

EA08 thinkScript/thinkorswim 策略

使用手册 + 操作教程 共 19 章全部免费开放,一步一步把软件用起来;核心知识点 7 章,¥69 一次解锁讲透原理。

作者 枢汇安全课堂 更新 2026-10-13 阅读 81 收录 EA量化教学 · 26 章
本课程 · 单套解锁
¥69¥104
单品永久解锁 · 会员可阅 · 一次购买终身使用
✓使用手册 + 操作教程 共 19 章全文免费 ✓核心知识点 7 章,¥69 一次解锁 ✓支持电脑 / 手机 · 永久免费更新
注册后解锁核心知识点
免费学习区 使用手册 + 操作教程 共 19 章全部免费开放 · 核心知识点 7 章为付费内容 ¥69 解锁核心知识点
PART ONE

使用手册

功能在哪 · 是什么 · 怎么调出来

一、thinkScript 是什么

thinkScript 是 thinkorswim(现属 Schwab 嘉信)平台内置的专有脚本语言,语法风格类似 Pascal。官方给它的定位是:在平台内自建分析工具的语言,能写四样东西:

  • 指标(Study):在图表上画线、画箭头、画柱状图
  • 策略(Strategy):在图上标注买卖信号并做回测(Backtest)
  • 扫描器(Scan):从全市场筛出符合条件的标的
  • 报警 / 条件单:条件满足时提醒你,或挂一笔手动预设的条件订单

官方明确说明:thinkScript 可以创建"自定义分析工具——指标、观察列表列、报警、扫描查询,甚至条件订单"(Schwab 官方视频原文)。

白话注解:thinkScript 就是 thinkorswim 里的"Excel 公式"——你写条件,它帮你画线、选股、响铃提醒。但它不是自动下单语言。策略回测很强,实盘自动交易不在它的能力范围内。

1、与其他策略语言的差别

对比项thinkScriptEasyLanguage (TradeStation)Pine Script (TradingView)
运行位置thinkorswim 桌面/Web 端TradeStationTradingView
下单函数AddOrder()(仅回测)Buy()/Sell() 可接实盘strategy.entry() 仅回测
内置止损无,需用 EntryPrice() 手写有 SetStopLoss()有 strategy.exit()
期权支持极强(平台强项)一般弱
警告:网上大量"thinkScript 教程"照抄 EasyLanguage 写法,出现 Buy("LE") if ...、SetStopLoss(50) 这类代码——这些在 thinkScript 里全部编译报错。本文所有代码均已对照官方 Learning Center 核实。
PART TWO

操作教程

一步一步 · 跟着做完一件事

二、开发环境与第一个脚本

1、打开编辑器

  1. 打开 thinkorswim 桌面版(首次加载较慢,耐心等)
  2. 图表页 → 点 Studies(研究)图标 → Edit Studies and Strategies
  3. 右侧列表里点 Create(新建),或选一个内置指标点旁边的滚动图标进入源码
  4. 写完点 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—报警弹窗文字
alertTypeAlert.ONCE触发频率
soundSound.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、使用方式与限制

  1. 写好含 Alert 的指标 → 图表上点铃铛图标选 Study Alert → 选中该指标激活
  2. 条件满足时弹窗+响铃(手机端可收推送)
官方注记:当 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);
参数默认值说明
typeOrderType.BUY_AUTO方向+开平,见第十二章
condition—为真时下一根K线加单
priceopen[-1]成交价(默认下一根K线开盘价)
tradeSize策略全局设置手数;写了就覆盖全局设置
tickColorColor.MAGENTA成交价小点的颜色
arrowColorColor.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、三条官方/实测限制(必须背下来)

  1. 只在策略(Strategy)里有效:同样的代码放进普通指标(Study),输出恒为空值 NA。不能用它给指标画"成本线"。
  2. 空仓时返回 NaN:判断是否持仓可以用 isNaN(EntryPrice())(第十四章实战用)。
  3. 条件单(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、官方三条规则(违反必出错)

  1. 副周期不能小于主周期:日线上不能取 close(period = AggregationPeriod.HOUR),会出错。
  2. 一个变量里不能混两种副周期:官方原文示例 close(period = MONTH) + close(period = WEEK) 写在同一个表达式里是不允许的。正确做法是分存两个 def 再相加。
  3. 必须先存进 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、使用步骤

  1. Scan 标签 → Stock Hacker → 公共过滤器里选 Custom Study(自定义研究)
  2. 载入上面的脚本(或直接在 Scan 标签页写)
  3. 选市场范围 → Run Scan → 结果可存为观察列表(Watchlist)

3、扫描的三条限制

  1. 扫描在固定周期上跑(扫描设置里选),脚本里的 AggregationPeriod 只能往大取
  2. 引用其他自定义脚本时,被引用脚本里若有 AddLabel/AddChartBubble 等绘图语句不影响扫描,但 plot 必须输出布尔值
  3. 复杂扫描配 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 要输出"是/否",不要输出价格。
付费专享 · 核心知识点 7 章

继续阅读:核心知识点,搞懂原理

EA08 thinkScript/thinkorswim 策略 — 共 26 章,已免费阅读 19 章,剩余 7 章核心知识点讲透「为什么这样、点了会怎样」。

二十、第一个完整策略:双均线回测
二十一、引用内置指标 reference
二十二、从回测到实盘:条件单与 API
二十三、回测报告解读与迁移坑 Top10
二十四、常见问题 FAQ 与一页纸速查
二十五、学习路线
…等共 7 章核心内容
✍️ 注册后扫码购买 🔐 已有账号,去登录 单套 ¥69 永久 · 会员可阅
SERVICE & PRICING
服务收费说明 · 明码标价,无隐藏收费
全部课程统一 ¥39-69,买一套永久使用。以下为购买与使用的完整流程。
01先免费试读

每套教程的「使用手册」部分均可免费阅读,先确认内容是否适合自己再决定,不必盲买。

02扫码付款

选定课程后点「立即解锁」,页面展示收款二维码与客服微信,扫码转账即可,无需注册海外支付。

03获取卡密

付款完成后客服第一时间发送卡密号与卡密密码,站内消息与微信同步通知。

04兑换解锁

在用户中心「兑换卡密」输入卡密号与密码,系统即时解锁全部章节,无需二次付款。

一次付费,终身可用 · 解锁后课程永久留在账号里,不限次数、不限时间阅读 持续免费更新 · 平台改版、规则变化时课程同步修订
RECOMMEND
相关教程推荐 · 同栏目更多上手机会

💬 用户评价 (0)

🔒 登录并成为会员后可发表评价
还没有评价 · 成为第一个评价的人吧 🎯