发明者量化学习日记(二)(已完结)

Author: 小小梦, Created: 2016-05-22 20:05:36, Updated: 2019-08-01 10:31:58

前几天,搞定了几个问题后,在给客户写代码的时候,感觉很有必要写一个图表的模板,显示指标(特别是自己写的指标)、交易位置等。因为自己也是初学,对于highcharts 不熟悉,但是看了Z大 的一个商品期货的例子,感觉照猫画虎应该能搞定。于是动手开搞。遇到一些问题,随手记下:

问题1:

 var ChartObj = {//画图
    tooltip: {xDateFormat: '%Y-%m-%d %H:%M:%S, %A'},
    chart: { zoomType:'x',panning:true },//图表类型  
    title: { text: ''}, //标题
    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: " "},//副标题
    xAxis:{type: 'datetime'},
    yAxis: [{
            title: {text: 'K线'},//标题
            style: {color: '#4572A7'},//样式 
            opposite: false  //生成右边Y轴
        },
       {
            title:{text: 'WT'},
            opposite: true  //生成右边Y轴  ceshi
       }
    ],
    series: [//系列
        {name:'wt1',type:'spline',yAxis:1,data:[]},
        {type:'candlestick',yAxis:0,name:'K',id:'wt',data:[]},
        {name:'wt2',type:'spline',yAxis:1,data:[]},
        {type:'flags',onSeries:'wt',data:[]}
        ]                  
};
var chart = Chart(ChartObj);
var isFirst = true;
var preRecordTime = 0;
function Draw(){
    var strState = "";
    var fcolor = "";
    var msg = "";
    getRecords();
    if(isFirst === true){
        chart.reset();
        isFirst = false;
        preRecordTime = globalRecords[globalRecords.length - 1].Time;
    }
    if(preRecordTime === globalRecords[globalRecords.length - 1].Time){
        chart.add([1,[globalRecords[globalRecords.length - 1].Time,globalRecords[globalRecords.length - 1].Open,globalRecords[globalRecords.length - 1].High,globalRecords[globalRecords.length - 1].Low,globalRecords[globalRecords.length - 1].Close ],-1]);
    }else{
        //更新前一柱
        chart.add([1,[globalRecords[globalRecords.length - 2].Time,globalRecords[globalRecords.length - 2].Open,globalRecords[globalRecords.length - 2].High,globalRecords[globalRecords.length - 2].Low,globalRecords[globalRecords.length - 2].Close ],-1]);

        chart.add([1,[globalRecords[globalRecords.length - 1].Time,globalRecords[globalRecords.length - 1].Open,globalRecords[globalRecords.length - 1].High,globalRecords[globalRecords.length - 1].Low,globalRecords[globalRecords.length - 1].Close ]]);
       
        preRecordTime = globalRecords[globalRecords.length - 1].Time;
    }
    chart.update(ChartObj);
    //chart.reset(500);
}

代码贴的不好~可能看着比较费眼,可以粘贴下来弄到 sublime text 里面看,先简单说下问题,就是代码写完在运行的时候,遇到诡异的一幕,看图:

很奇怪,进度条走到100% 一直卡着,一直不动,不是假死,应该卡死了。我也用了各种办法去测试,比如:限制循环次数,显示循环次数什么的,显示的都是运行到循环的最后一次卡死了。 看了看程序代码 感觉应该没什么问题。 百思不得其解 。整来整去,整了好长时间,终于发现问题了,原因是这个。 在图表初始化的时候 数据序列我是这样写的:

 series: [//系列
        {name:'wt1',type:'spline',yAxis:1,data:[]},        //    索引为0的 数据项,   
        {type:'candlestick',yAxis:0,name:'K',id:'wt',data:[]},  // 索引为1
        {name:'wt2',type:'spline',yAxis:1,data:[]},    //索引为2
        {type:'flags',onSeries:'wt',data:[]}   //......
        ] 

在程序中往图表里写数据是这样的:

  chart.add([1,[globalR.........    (太长了 没写完)      //   可以看到这里 我写入索引为1 的数据序列,就是add后面[ 符号后的 1,代表写入到{type:'candlestick',yAxis:0,name:'K',id:'wt',data:[]},  这个序列中。问题就在这,我越过了索引0,直接写入索引1的数据序列里,就会导致卡死。

于是 我修改成:

  series: [//系列
        {type:'candlestick',yAxis:0,name:'K',id:'wt',data:[]},
        {name:'wt2',type:'spline',yAxis:1,data:[]},
        {type:'flags',onSeries:'wt',data:[]}
        ]  

所有往图表写数据的代码改成 (主要是把 1 改成 0)

 chart.add([0,[globalRecords[globalRecords.length - 1].Time,globalRecords[globalRe..........(太长了。。。)

很神奇,不卡了,运行没问题了。。。虽然不知道具体是什么原因,感觉应该是图表库里的问题吧。


问题2

今天把帖子更新下,是因为在群里有朋友提出问题,通过研究,问题解决,特此记录。如果有新用户有类似的疑惑,方便讲解。OK 问题如下:

getticker 获取期货价格 为什么和实际价格不一样? img

我自己测试了一下,确实不一样 发明者量化 上机器人 交易所是OK期货,用 GetTicker 获取的 最新行情 价格 确实 和 OK期货官网上的有差别,确实奇怪,肯定是哪没弄明白。 于是写了段代码具体测试:

function main(){
    exchange.SetContractType("this_week");
    var ticker = exchange.GetTicker();
    var huilv = exchange.GetRate();
    var OKhuilv = exchange.GetUSDCNY();
    while(true){
        huilv = exchange.GetRate();
        ticker = exchange.GetTicker();
        OKhuilv = exchange.GetUSDCNY();
        LogStatus("ticker:",ticker,"\n","huilv",huilv,"OKhuilv",OKhuilv);
        Sleep(1000);
    }
}

运行情况: img img 发现汇率不一样,我这里获取汇率 用了2个不同的API函数。(OK期货官网上,可以设置成人民币计价的) img 看了API的介绍,莫非 OK期货 用了自家汇率?

动手换算 并且验证一下吧!结果直接上图: img 因为OK 官网上的 价格只有小数位后1位(剩下的没显示全),估计会有一点误差。 看到结果基本上相等,可以不完全确定,GetTicker 取得的行情价 和 OK期货官网上的 有差别,应该就是 汇率不一致引起的。


问题3: 比特币 OKcoin 平台 市价单问题(此问题 已经调整,现已统一了回测时的市价单 和 实盘时的市价单 统一为: 买入传入的第二个参数为 RMB数额, 卖出时 第二个参数为 标的物 的数量。 详见教程: https://www.fmz.com/bbs-topic/474


问题4: 昨天深夜,一位群友问我哪里有 关于策略 交互按钮的 使用范例,我想了一下,干脆自己写写,练练手,顺便方便大家一起学习,老套路,先上码!

/* 交互按钮 测试
while (true) {
  var cmd = GetCommand();
  if (cmd) {
    Log(cmd);
  }
  Sleep(1000);
}
*/
function main(){
    var cmd = null; //初始化一些用到的变量,这个变量是直接接受 GetCommand 函数 返回值的。
    var jsonObjStr = null; //接收JSON对象字符串 的变量
    var jsonObj = null; // JSON 对象
    var keyValue = null; // JSON 对象中的  KEY 的值
    var arrStr = null; // 字符串数组
    var ticker = exchange.GetTicker(); 
    while(true){
        $.Draw(); // 画图函数 , 图表模板的 导出函数
        while(!ticker){
            ticker = exchange.GetTicker();
            Sleep(500);
        }
        cmd = GetCommand(); //获取  交互命令
        if (cmd) {
            Log("按下了按钮:",cmd);
            arrStr = cmd.split(":"); // GetCommand 函数返回的 是一个字符串,这里我处理的麻烦了,因为想熟悉一下JSON ,所以先对字符串做出处理,把函数返回的字符串以 : 号分割成2个字符串。储存在字符串数组中。
            
            
            jsonObjStr = '{' + '"' + arrStr[0] + '"' + ':' + arrStr[1] + '}'; // 把 字符串数组中的元素重新 拼接 ,拼接成 JSON 字符串  用于转换为JSON 对象。
            //Log(jsonObjStr);//ceshi
            //Log(typeof(cmd));//ceshi
            /*ceshi
            for(var obj1 in cmd){ //  测试用  注释掉了
                Log(cmd[obj1]);
            }
            */
            
            jsonObj = JSON.parse(jsonObjStr); // 转换为JSON 对象
            //Log("ceshi"); //ceshi
            for(var key in jsonObj){ // 遍历对象中的  成员名
                keyValue = jsonObj[key]; //取出成员名对应的 值 , 就是交互按钮的值
            }
            Log(keyValue); //ceshi 
            switch(keyValue){ // 分支选择 操作
                case 1:
                    $.SignOP((new Date()).getTime(),ticker.Last,1,keyValue ); //开多仓
                    break;
                case 2:
                    $.SignOP((new Date()).getTime(),ticker.Last,1,keyValue ); // 开空仓
                    break;
                case 0:
                    $.SignOP((new Date()).getTime(),ticker.Last,1,keyValue );//平仓
                    break;
                default: break;
            }
        }
        Sleep(2000);
    }
}

以上代码我测试过了,没有在广场公开,所以 朋友们想要测试,需要手动添加上 交互按钮。还要添加上 一个 图表模板。我都有截图。 img 按照上图设置就行。

看看运行的情况:

img img

当然策略回测是无法测试的,需要自己创建机器人,用模拟盘测试,点一下交互按钮,在图表上就会标记相应的操作。这里抛砖引玉,希望大家都写出NB的策略。(注意:机器人跑的时候 会默认隐藏交互按钮界面。需要自己点开。)


问题 5: 有用户问,怎么获取 当前操作的电子货币的名称,还有怎么获取当前K线的周期。 获取当前交易所的货币是有API函数可以用的,我用了比较笨的办法解决了获取K线周期的问题。

function main() {
    var records = exchange.GetRecords();
    while(!records || records.length < 2){
        records = exchange.GetRecords();
        Sleep(500);
    }
    var currency = exchange.GetCurrency();
    var diffTime = records[records.length - 1].Time - records[records.length - 2].Time;
    if(diffTime/1000 >= 1 && diffTime/1000 < 60){
        Log("周期:",diffTime/1000,"秒");
    }else if(diffTime/1000/60 >= 1 && diffTime/1000/60 < 60 ){
        Log("周期:",diffTime/1000/60,"分钟");
    }else if(diffTime/1000/60/60 >= 1 && diffTime/1000/60/60 < 24 ){
        Log("周期:",diffTime/1000/60/60,"小时");
    }else if(diffTime/1000/60/60/24 >= 1){
        Log("周期:",diffTime/1000/60/60/24,"天");
    }
    Log("货币品种:",currency);
}

有兴趣的朋友可以测试一下。有更好的方法的欢迎跟帖。


问题6: 如果对于新版的 商品期货交易类库 增加的函数功能不理解可以到QQ群共享下载新的注释版代码。

  • 策略模板在 策略广场可以复制。 img

问题7: x = [1 for i in range(n)] 代表什么意思?

  • 这两天有空时在看python ,整体学习起来没什么难度,但是还是遇到不少问题,我都先记下。时间长了自己也可以复习复习,python的新手遇到同样的问题不用头大了 =_=!

    如题: x = [1 for i in range(n)] 代表什么意思? 当初看到这里我也已经头大了,这样的语法似乎以前从没见过。猜来猜去,猜不出是什么意思。果断百度一下,百度也发现讲这个的不多。所幸还是找到了一些。

这是python的列表解析,含义就是形成一个从1到n的列表。关于列表解析可以看下面的描述: 1.定义和说明>Python 的强大特性之一是其对 list 的解析,它提供一种紧凑的方法,可以通过对 list 中的每个元素应用一个函数,从而将一个 list 映射为另一个 list。>列表解析,又叫列表推导式( list comprehension)>列表解析比 for 更精简,运行更快,特别是对于较大的数据,列表解析可以替代绝大多数需要用到 map和 filter的场合 列表推导式提供了一个创建链表的简单途径,无需使用 map() , filter() 以及 lambda 。以定义方式得到列表通常要比使用构造函数创建这些列表更清晰。每一个列表推导式包括在一个 for 语句之后的表达式,零或多个 for 或 if 语句。返回值是由 for 或 if 子句之后的表达式得到的元素组成的列表。如果想要得到一个元组,必须要加上括号。 2.基本列表解析基本 [x for x in range(5)] # [0, 1, 2, 3, 4]l1 = [1,2,3,4][ x*2 for x in l1] #[2,4,6,8] 多个值的[ ‘%s = %s’ for (k, v) in a_map.items()] 两次循环 l1 = [1,2,3,4] l2 = [1,2,3,4] [x+y for x in l1 for y in l2] [2, 3, 4, 5, 3, 4, 5, 6, 4, 5, 6, 7, 5, 6, 7, 8] 可以调用函数[ func(x) for x in l1] #等价于map 注意,列表解析不会改变原有列表的值,会创建新的list 3.条件列表解析[ x for x in range(100) if x%2 ==0 ] 4.、嵌套列表解析mat = [ [1, 2, 3],[4, 5, 6], [7, 8, 9]]交换行列[ [row[i] for row in mat] for i in (0,1,2)] #[[1, 4, 7], [2, 5, 8], [3, 6, 9]] 以上转自凌岳的CSDN博客文章。

说实话,上面这个我没怎么看明白,但是概念上似乎清楚了一点,这个语句应该是有一些迭代的作用。
再看看其他的热心帮助:

i 在 range(n)的范围内循环,即 i=0,1,2,…,n-1,分别计算for之前的表达式的值,作为列表的项 表达式是1,对应的值分别为 1,1,1…,供n个1,生成了包含n个1的列表[1,1…]

这个看起来就明白多了,再看。

x = [i for i in list] 将一个 list 映射为另一个 list,每个元素设为变量i x = [1 for i in range(n)] 将列表range(n)映射到列表x,每个元素设为常量1 欢迎访问forpython。com进行交流

问题8: python基础不扎实, 被return 忽悠了半天,下面来看看问题

昨天写了一个 python CTP商品期货均线的测试策略,老大让测试一下 python 的CTP商品期货回测系统。 今天继续测试发现策略完全没按照均线金叉死叉去开仓平仓操作,感觉一定有问题,逐个函数检查、测试,发现 交叉函数没有按照设计返回正确的值代码如下:

def Cross(records,fast,slow): # 交叉函数 ,参数是  records K线数据   、  fast 快线周期   、slow 慢线周期
    global array_S_MA,array_F_MA   # 使用外部的 全局变量
    array_F_MA = TA.MA(records,fast)  # 调用指标函数
    array_S_MA = TA.MA(records,slow)
    n = 0   # 返回的信号值   0:不操作   , 1 : 金叉     -1: 死叉 
    if array_F_MA[-2] > array_S_MA[-2] and array_F_MA[-3] < array_S_MA[-3] and array_F_MA[-4] < array_S_MA[-4]:
        n = 1
        return n
    elif array_F_MA[-2] < array_S_MA[-2] and array_F_MA[-3] > array_S_MA[-3] and array_F_MA[-4] > array_S_MA[-4]:
        n = -1 
        return n

怎么样?看到这看出来了吧,一个非常新手的错误! 我把 return n 写到了条件分支里面了,这个看似没什么问题。测试问题就来了,当不触发金叉、死叉的时候 Cross函数 会返回什么值呢?

我们做个小实验:

img

定义一个测试函数,只输出一条信息

img

我们来调用一下,这样写,顺便看看None 是不是等于 -1

img

看看结果就明白了,函数中没有执行return时 取函数的返回值会得到 None ,None 也不等于 -1 , 但是判断None 小于 0 , 这个是 真值 。

问题9: JS 循环引用的问题。

比如这段代码,会报错:

function main(){
    obj = {
        exchange : null,
        initAccount : null,
        state : 0,
        exchangeName : "",
    }
    obj.name = "OKCoin";
    obj.exchange = exchange;
    obj.initAccount = obj.exchange.GetAccount();
    obj.state = 2;
    var table = {
        type : 'table',
        title : '测试',
        cols : ['obj属性名', '值'],
        rows : [],
    };
    for(var k in obj){
        table.rows.push([k, obj[k]]);
    }
    LogStatus('`' + JSON.stringify(table) + '`');
}

img

TypeError: Converting circular structure to JSON (这个错误虐我半个小时啊!),这个报错,在百度查了一下。是说代码里面有循环引用,JSON 无法解析。

想了半天,发现问题了,一定是因为 obj.exchange = exchange; 这句导致的循环引用,果断在for(var k in obj) 循环中遇到k === ‘exchange’ 的时候跳过去,试试。

代码如下:

function main(){
    obj = {
        exchange : null,
        initAccount : null,
        state : 0,
        exchangeName : "",
    }
    obj.name = "OKCoin";
    obj.exchange = exchange;
    obj.initAccount = obj.exchange.GetAccount();
    obj.state = 2;
    var table = {
        type : 'table',
        title : '测试',
        cols : ['obj属性名', '值'],
        rows : [],
    };
    for(var k in obj){
        if(k === 'exchange'){  // 增加的代码
            continue;
        }
        table.rows.push([k, obj[k]]);
    }
    LogStatus('`' + JSON.stringify(table) + '`');
}

img

果然正常了,不过能看出是怎么循环引用的么?(暂时还有点糊涂,高手指点!)

问题10: 商品期货 在星期五的晚上 开仓,持有的仓位,在下个星期一开市后,调用GetPosition 函数 获取的持仓信息,为什么

img

原因是这样的:

img

具体的 positions 持仓信息对象的 type 属性 的各个值参看: https://www.fmz.com/bbs-topic/672

看到这,我是感觉应该动手实践实践了…你呢?

More

botvsing2 直接贴出来啊

小小梦 准备写很多,所以专门申请了个QQ 空间,这样看着也方便,好找。