Type/to search
8
Follow
1386
Followers
2.7 지표의 사용
Tutorials
Created 2016-11-10 16:19:36  Updated 2019-08-01 09:25:06
 22
 6536

지표의 사용


  • TA - 일부 자주 사용되는 지표 함수 라이브러리를 다시 작성하는 최적화

TA 指标库 MACD 指数平滑异同平均线 KDJ 随机指标 RSI 强弱指标 ATR 平均真实波幅 OBV 能量潮 MA 移动平均线 EMA 指数平均数指标 BOLL 布林带 Alligator Alligator Indicator CMF 蔡金货币流量指标 Highest 周期最高价 Lowest 周期最低价

MACD라는 지표를 가지고 테스트를 작성해보자, 그 전에 API 문서에 MACD 태그를 펴서 구체적인 설명을 보도록 하자.

img

DIF, DEA, 지표 알고리즘 등에 관심이 있는 학생이라면 백두대에서 MACD 알고리즘을 검색해 보실 수 있는데, 많은 자료가 있고, 패키지화된 인터페이스를 가지고 있습니다. 테스트 코드는 다음과 같습니다:

function main(){ var records = null; var macd = null; while(true){ records = _C(exchange.GetRecords); // 获取K线数据 ,默认为策略界面设置的K线周期, _C 是一个容错的内置函数。 // _C 详见 https://www.fmz.com/bbs-topic/320 问题7。 macd = TA.MACD(records); // 不加参数的话,使用的是默认参数 12, 26, 9 Log("macd[0]", macd[0]); // DIF Log("macd[1]", macd[1]); // DEA Log("macd[2]", macd[2]); // MACD Log("macd[0]长度", macd[0].length); // DIF 长度 Log("macd[1]长度", macd[1].length); // DEA 长度 Log("macd[2]长度", macd[2].length); // MACD 长度 Log("records 长度:", records.length); // 显示一下 records 的长度。 Sleep(1000 * 60 * 5); } }

디스크의 시뮬레이션 결과:

img

볼 수 있듯이, 계산된 지표가 시작될 때 모두 null이며, 후의 데이터가 시작될 때 구체적인 수치가 있다. 이것은 지표 파라미트가 정한 계산 주기 때문에, 데이터 양이 이 주기 (data quantity) 를 만족하지 않는 때까지 지표를 계산할 수 없다.
따라서, 지표를 사용하기 전에 먼저 지표의 설명을 알아야 한다. 그리고 프로그램에서 지표를 계산하기 위해 사용되는 K선 데이터의 길이를 판단해야 한다. 길이가 부족하면 유효하지 않은 값이 계산되지 않도록, 그리고 유효하지 않은 값을 사용하면 프로그램 오류가 발생할 수 있다.

아래는 그래프 인터페이스를 사용하여 계산된 지표가 그래프에 표시되고 거래소의 실판 그래프와 비교됩니다.

var ChartCfg = { tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, chart: { zoomType:'x',panning:true },//图表类型 title: { text: "K-macd"}, //标题 rangeSelector: { buttons: [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}], selected: 0, inputEnabled: false }, subtitle: {text: "测试macd"},//副标题 xAxis:{type: 'datetime'}, yAxis: [{ title: {text: 'K线'},//标题 style: {color: '#4572A7'},//样式 opposite: false //生成右边Y轴 }, { title:{text: "macd"}, opposite: true //生成右边Y轴 ceshi } ], series: [//系列 {type:'candlestick',yAxis:0,name:'K',id:'KLine',data:[]}, {name:"DIF",type:'spline',yAxis:1,data:[]}, {name:"DEA",type:'spline',yAxis:1,data:[]}, {name:"MACD量柱",type:'spline',yAxis:1,data:[]}, ] }; function main(){ var records = null; var macd = null; var perRecords = _C(exchange.GetRecords); var perRecordTime = perRecords[perRecords.length - 1].Time; var chart_obj = Chart(ChartCfg); // 初始化图表 chart_obj.reset(); while(true){ records = _C(exchange.GetRecords); // 获取K线数据 ,默认为策略界面设置的K线周期, _C 是一个容错的内置函数。 if(!records && records.length < 26 ){ continue; } macd = TA.MACD(records, 12, 26, 9); // 不加参数的话,使用的是默认参数 12, 26, 9 if(records[records.length - 1].Time !== perRecordTime){ // _C 详见 https://www.fmz.com/bbs-topic/320 问题7。 //先更新,再添加K线 chart_obj.add(0, [records[records.length - 2].Time, records[records.length - 2].Open, records[records.length - 2].High, records[records.length - 2].Low, records[records.length - 2].Close], -1); // 跟新刚完成的bar。 chart_obj.add(0, [records[records.length - 1].Time, records[records.length - 1].Open, records[records.length - 1].High, records[records.length - 1].Low, records[records.length - 1].Close]); // 添加新出现的bar //先更新,添加指标线 chart_obj.add(1, [records[records.length - 2].Time, macd[0][records.length - 2]], -1); // 更新 chart_obj.add(1, [records[records.length - 1].Time, macd[0][records.length - 1]]); chart_obj.add(2, [records[records.length - 2].Time, macd[1][records.length - 2]], -1); // 更新 chart_obj.add(2, [records[records.length - 1].Time, macd[1][records.length - 1]]); chart_obj.add(3, [records[records.length - 2].Time, macd[2][records.length - 2]], -1); // 更新 chart_obj.add(3, [records[records.length - 1].Time, macd[2][records.length - 1]]); perRecordTime = records[records.length - 1].Time; }else{ //只更新当前的bar 和 线 chart_obj.add(0, [records[records.length - 1].Time, records[records.length - 1].Open, records[records.length - 1].High, records[records.length - 1].Low, records[records.length - 1].Close], -1); chart_obj.add(1, [records[records.length - 1].Time, macd[0][records.length - 1]], -1); // 更新 chart_obj.add(2, [records[records.length - 1].Time, macd[1][records.length - 1]], -1); // 更新 chart_obj.add(3, [records[records.length - 1].Time, macd[2][records.length - 1]], -1); // 更新 } chart_obj.update(ChartCfg); Sleep(1000); } }

로봇 인터페이스의 K선 주기 파라미터는 1분으로 설정되어 있으며, 효과를 보기 위해 약간의 시간이 걸리기 때문에 조금 더 작은 주기를 선택하십시오. 그림처럼:

img
img

도표에서 볼 수 있다 Inventor 양적 로봇 동작 계산된 DIF는 약 2.729이며, DEA는 약 2.646 MACD 양 기둥 약 0.0831
실제 OKCoin 거래소 거래 도표는 DIF가 2.73, DEA가 2.65, MACD의 기둥은 0.17입니다.
DIF와 DEA의 차이는 매우 작고, OKCoin은 MACD보다 두 배나 더 낮습니다.
(DIF - DEA) * 2는 보통 DIF - DEA = 2.729 - 2.646 = 0.083 입니다. 2를 곱하면 0.166이 약 0.17이 됩니다.

다른 지표의 사용법은 기본적으로 비슷하다.

  • talib - http://ta-lib.org/

    타리브 (talib) 라이브러리에는 TA 라이브러리의 일반적인 지표 외에도 많은 지표가 있습니다.
    예를 들어, talib.STOCHRSI라는 지표를 사용해보자.

STOCHRSI Stochastic Relative Strength Index STOCHRSI(Records[Close],Time Period = 14,Fast-K Period = 5,Fast-D Period = 3,Fast-D MA = 0) = [Array(outFastK),Array(outFastD)]

여기서는 talib.STOCHRSI ((records, 14, 14, 3, 3); 기본적으로 상단 MACD와 비슷한 코드입니다. 약간 수정해서 실행해 보겠습니다.

var ChartCfg = { tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'}, chart: { zoomType:'x',panning:true },//图表类型 title: { text: "stochrsi"}, //标题 rangeSelector: { buttons: [{type: 'hour',count: 1, text: '1h'}, {type: 'hour',count: 3, text: '3h'}, {type: 'hour', count: 8, text: '8h'}, {type: 'all',text: 'All'}], selected: 0, inputEnabled: false }, subtitle: {text: "测试stochrsi"},//副标题 xAxis:{type: 'datetime'}, yAxis: [{ title: {text: 'K线'},//标题 style: {color: '#4572A7'},//样式 opposite: false //生成右边Y轴 }, { title:{text: "K-D"}, opposite: true //生成右边Y轴 ceshi } ], series: [//系列 {type:'candlestick',yAxis:0,name:'K',id:'KLine',data:[]}, {name:"K",type:'line',yAxis:1,data:[]}, {name:"D",type:'line',yAxis:1,data:[]}, ] }; function main(){ var records = null; //var macd = null; var stochrsi = null; var perRecords = _C(exchange.GetRecords); var perRecordTime = perRecords[perRecords.length - 1].Time; var chart_obj = Chart(ChartCfg); // 初始化图表 chart_obj.reset(); while(true){ records = _C(exchange.GetRecords); // 获取K线数据 ,默认为策略界面设置的K线周期, _C 是一个容错的内置函数。 if(!records && records.length < 26 ){ continue; } //macd = TA.MACD(records, 12, 26, 9); // 不加参数的话,使用的是默认参数 12, 26, 9 stochrsi = talib.STOCHRSI(records, 14, 5, 3, 0); if(records[records.length - 1].Time !== perRecordTime){ // _C 详见 https://www.fmz.com/bbs-topic/320 问题7。 //添加K线 chart_obj.add(0, [records[records.length - 2].Time, records[records.length - 2].Open, records[records.length - 2].High, records[records.length - 2].Low, records[records.length - 2].Close], -1); // 跟新刚完成的bar。 chart_obj.add(0, [records[records.length - 1].Time, records[records.length - 1].Open, records[records.length - 1].High, records[records.length - 1].Low, records[records.length - 1].Close]); // 添加新出现的bar //添加指标线 chart_obj.add(1, [records[records.length - 2].Time, stochrsi[0][records.length - 2]], -1); // 更新 chart_obj.add(1, [records[records.length - 1].Time, stochrsi[0][records.length - 1]]); chart_obj.add(2, [records[records.length - 2].Time, stochrsi[1][records.length - 2]], -1); // 更新 chart_obj.add(2, [records[records.length - 1].Time, stochrsi[1][records.length - 1]]); perRecordTime = records[records.length - 1].Time; }else{ //只更新当前的bar 和 线 chart_obj.add(0, [records[records.length - 1].Time, records[records.length - 1].Open, records[records.length - 1].High, records[records.length - 1].Low, records[records.length - 1].Close], -1); chart_obj.add(1, [records[records.length - 1].Time, stochrsi[0][records.length - 1]], -1); // 更新 chart_obj.add(2, [records[records.length - 1].Time, stochrsi[1][records.length - 1]], -1); // 更新 } chart_obj.update(ChartCfg); LogStatus("倒数第一组数据:", stochrsi[0][stochrsi[0].length - 1], stochrsi[1][stochrsi[1].length - 1], " 倒数第二组数据:", stochrsi[0][stochrsi[0].length - 2], stochrsi[1][stochrsi[1].length - 2]); Sleep(1000); } }

그리고 BOSS는 talib의 라이브러리 구현 알고리즘이 OKCoin의 것과 다를 수 있다고 말했습니다. 저는 온라인에서 조금만 찾아보았는데, 다른 게시물이 나타났습니다.
STOCHRSI 지표 이해
이 지표가 어떻게 쓰여졌는지 보실 수 있습니다. 제가 쓴 것은 최적화되지 않고, 효율성이 낮으며, 느리고, 학습할 수 있습니다.

Related Recommendations
Comment
All comments (22)

    您说“最后一根 K 线需要先更新再添加,因为最后一根 K 线是一直在变动的,只有当新的 K 线生成以后,前一根 K 线才是确定的。”

    我不是很理解,通过 GetRecords() 获得的 K 线数据难道不是确定的吗?records[length -1] 代表的是当前时间戳下的 K 线数据,直接画到图表中即可,等有了新的时间戳的 K 线数据,再添加到图表的最后一个元素即可,不是吗?望不吝赐教。

    9 years ago

    GetRecords() 获取的 K线数据 除了 倒数第一根 ,其余是确定的(因为周期已经完成),倒数第一根 不确定是因为 周期没有走完,Close 一直在变动。比如 日K 线, 当前的日K 线柱,在今天没有过完的时候, 谁也不知道 今天最终会多少价格 收盘。

    9 years ago

    嗯,我之前理解的误区在于 K 线的时间戳上。调试下来后发现 BotVS 的机制是这样的,以您说的日 K 线为例:

    如果我要获取 3月1日 这一天的 K 线数据,调用 GetRecords(D1) 会返回很多条记录,但它们的时间戳都是一样的,都是2018-03-01 00:00:00。

    也就是说,即使我在 3月1日 10点 调用了 GetRecords(D1),获取到的 K 线时间戳,依然是 2018-03-01 00:00:00。所以才有了您说的最后一根是会不确定的,直到有了新的时间戳,即 2018-03-02 00:00:00,则 3月1日这一天的时间戳便确定了。

    我此前理解的误区在于,我以为我在 3月1日 1点、2点、3点调用后返回的时间是不同的,即 2018-03-01 01:00:00、2018-03-01 02:00:00、2018-03-01 03:00:00。

    9 years ago

    K线Bar 的时间戳是 这个Bar 的起始时间, 并不是 实时 时间, Bar 的起始时间是不会变动的。

    9 years ago

    请问有没有关于 TA.ATR(records, 14) 更详细的说明,我在BotVS的API文档,视频以及完全使用手册中都没有找到。在我调用 TA.ATR(records, 14) 时(参数为30分钟k线),返回的是一个长度为 177 的数组。请问这个数组的含义是什么?

    9 years ago

    举例 传入的records 为 日K线, 计算出的 ATR 就是 一个 数组, 这个数组的 倒数第一个元素 就是 当天的(日K线 最后一根的日期)ATR 指标。 依次类推 30分钟 K线 , 15分钟K线。 您算出的 177 的数组 就是 177 个K线周期 ,对应每个 K线Bar 的 ATR指标。

    9 years ago

    感谢您的回答,这下清楚了,另外想问一下,这个117的长度是由什么决定的?

    9 years ago

    是由 K线长度 决定的 ^^

    9 years ago

    不好意思,我还是不太明白,我的意思是,如果调用这个函数,那么返回数据的长度是交易所给定的吗?我发现不同时刻调用这个函数,即使参数相同,返回数据的长度也可能不同。

    9 years ago

    这个 指标函数 返回多少 数据 是和 K线数据 也就是 records 对应的, 你传入的 records 数组 长度是多少 , 算出来的 长度 就是多少。

    9 years ago

    这个我明白,TA.ATR()实质就是对传入的records进行了一个数学运算。是我之前没有表达清楚,我想问的是用函数exchange.GetRecords() 获取k线数据时,返回的长度是由交易所决定的吗?

    9 years ago

    是的 具体 返回多少是 看交易所 的,并不能 指定。所以 交易所 一般 提供的都是 有限 数据。

    9 years ago

    好的,非常感谢您的回答。最后再确认一下,var a = TA.ATR(records, 14), a[a.length-1] 这个表示的是最近k线数据的ATR吧?

    9 years ago

    是的 对应 倒数第一根 K线 Bar。

    9 years ago

    明白了,非常感谢!

    9 years ago

    python版本

    img img

    9 years ago

    python版本
    def main():
    while true:
    records = _C(exchange.GetRecords); # 获取K线数据 ,默认为策略界面设置的K线周期, _C 是一个容错的内置函数。
    macd = TA.MACD(records); # 不加参数的话,使用的是默认参数 12, 26, 9
    Log("macd[0]", macd[0]); # DIF
    Log("macd[1]", macd[1]); # DEA
    Log("macd[2]", macd[2]); # MACD
    Log("macd[0]长度", len(macd[0])); # DIF 长度
    Log("macd[1]长度", len(macd[1])); # DEA 长度
    Log("macd[2]长度", len(macd[2])); # MACD 长度
    Log("records 长度:", len(records)); # 显示一下 records 的长度。
    Sleep(1000 * 60 * 5);

    9 years ago

    这个例子挺好, 就是在实际使用中 ,最新的指标数据都是在数组 最后的, 就是 索引 -1 的元素。前边不满足周期要求 计算出的数据都是 None,
    后面才有 有效数据,如图:
    img

    9 years ago

    另外,我用实盘级tick回测,没有数据返回

    9 years ago

    前面的数据为什么会不满足周期要求呢?

    9 years ago

    举个栗子~
    如果 现在K线数据只有10个Bar 的时候,我要求 MA(15) 即 15周期均线, 那么 肯定是求不出来的,因为只有10个周期,无法计算15个周期的平均值。

    9 years ago
  • 1
Forums
PINE Language
Get the app
iPhone Download
© 2015 - ∞ INVENTOR PTE LTD (SG)