バイオスとJavaScriptで遊ぶ - 買い物をするパートナーを作ること (3) 周りに新しいことを好きで活気のある男の子

作者: リン・ハーン小さな夢, 作成日: 2017-03-07 12:50:50, 更新日: 2017-10-11 10:37:06

古い白人とJavaScriptで遊んで,買い物をするパートナーを作ります.

活気のある小さな男の子は,新しいものを愛しています.

前回の記事では,サンドボックスのシステムで簡単な商品フューチャー取引プログラムをJavaScriptで作成し,サンドボックスの楽しい遊びをしました. 自動取引プログラムはデータ駆動に依存しています. つまり,プログラムはリアルタイムに周囲の最新のデータを入手し,取引,意思決定,分析計算のために大量の情報を提供します.

  • この機能は,先行で使用したいくつかの関数,GetTicker,GetRecords,GetAccount を参照します.

    • GetTicker: リアルタイムで市場データを入手する.

    • GetRecords: 過去のK行データを取得し,K行データを返す周期サイズを制御するパラメータを指定できます. パラメータ:PERIOD_M1 は 1 分,PERIOD_M5 は 5 分,PERIOD_M15 は 15 分,PERIOD_M30 は 30 分,PERIOD_H1 は 1 時間,PERIOD_D1 は 1 日

    • GetAccount: 設定で設定されている先物会社の口座情報を取得します. 将来の取引経験のある友人は,GetAccount関数の返し値を確認したことがあるでしょう. 詳細は,このコードで確認できますが,まだ MainLoop関数では,小さな人に少し機能を追加して,アカウントの詳細を要求するようにしました.

      function MainLoop(){
          var account = exchange.GetAccount();            // 框架的接口函数,获取配置的账户信息。
          var obj = JSON.parse(exchange.GetRawJSON());    // 框架的接口函数,获取上一次调用的原始调用信息。这里就是GetAccount 调用时 接收到的原始信息。
          var nowTime = new Date();
          var table = {
              type : "table",
              title : "商品期货账户详细信息",
              cols : ["key", "value"],
              rows : [],
          }
          for(var key in obj){
              table.rows.push([key, obj[key]]);
          }
          LogStatus("time:" + nowTime + JSON.stringify(account) + '\n`' + JSON.stringify(table) + '`');
      }
      

機能 main ((() { var 状態 = ゼロ 真実である 状態 =exchange.IO("status"); // API を呼び出し,接続状態を決定する if(status === true) { // 判断する状態 // LogStatus (( 接続済み!); // リトートまたは実際の動作中にリアルタイムでデータや情報を表示します. // MainLoopではLogStatusが使われていますので,この場所の記述を削除します. MainLoop ((); // 取引所サーバーに接続された後に,主要な作業機能を実行します. 接続されていない場合は,exchange.IO"status" は false を返します. LogStatus ((未接続状態!); //未接続状態を表示します。 {cH00ffff} Sleep ((1000); // 包装された睡眠関数で,アクセスが頻繁すぎないように,輪廻の間隔が必要である. CTP プロトコルは2秒間に2回のデータをプッシュする. {cH00ffff} {cH00ffff} ` `

##### 如图显示:

![img](/upload/asset/d2be567a7af2fb63c7834703d362136de7e794b6.png) 
![img](/upload/asset/f8383059b540e417161d5ac49e9465dd1178d5db.png) 

第一次打印出详细信息的时候我也一头雾水,查询了CTP相关资料知道了具体各个字段的意思。
可以用以下这个对象去在程序中匹配翻译下。

```
var trans = {
    "AccountID": "投资者帐号",
    "Available": "可用资金",
    "Balance": "期货结算准备金",
    "BrokerID": "经纪公司代码",
    "CashIn": "资金差额",
    "CloseProfit": "平仓盈亏",
    "Commission": "手续费",
    "Credit": "信用额度",
    "CurrMargin": "当前保证金总额",
    "CurrencyID": "币种代码",
    "DeliveryMargin": "投资者交割保证金",
    "Deposit": "入金金额",
    "ExchangeDeliveryMargin": "交易所交割保证金",
    "ExchangeMargin": "交易所保证金",
    "FrozenCash": "冻结的资金",
    "FrozenCommission": "冻结的手续费",
    "FrozenMargin": "冻结的保证金",
    "FundMortgageAvailable": "货币质押余额",
    "FundMortgageIn": "货币质入金额",
    "FundMortgageOut": "货币质出金额",
    "Interest": "利息收入",
    "InterestBase": "利息基数",
    "Mortgage": "质押金额",
    "MortgageableFund": "可质押货币金额",
    "PositionProfit": "持仓盈亏",
    "PreBalance": "上次结算准备金",
    "PreCredit": "上次信用额度",
    "PreDeposit": "上次存款额",
    "PreFundMortgageIn": "上次货币质入金额",
    "PreFundMortgageOut": "上次货币质出金额",
    "PreMargin": "上次占用的保证金",
    "PreMortgage": "上次质押金额",
    "Reserve": "基本准备金",
    "ReserveBalance": "保底期货结算准备金",
    "SettlementID": "结算编号",
    "SpecProductCloseProfit": "特殊产品持仓盈亏",
    "SpecProductCommission": "特殊产品手续费",
    "SpecProductExchangeMargin": "特殊产品交易所保证金",
    "SpecProductFrozenCommission": "特殊产品冻结手续费",
    "SpecProductFrozenMargin": "特殊产品冻结保证金",
    "SpecProductMargin": "特殊产品占用保证金",
    "SpecProductPositionProfit": "特殊产品持仓盈亏",
    "SpecProductPositionProfitByAlg": "根据持仓盈亏算法计算的特殊产品持仓盈亏",
    "TradingDay": "交易日",
    "Withdraw": "出金金额",
    "WithdrawQuota": "可取资金",
};
```
##### 先复制这个用来翻译的对象到代码中。
![img](/upload/asset/4f034b84a65aae2f4f4f0cccf601df857f040825.png) 
##### 修改前 
![img](/upload/asset/0b2a2ba84c6c90ac4cc9dd15021e7dd0bfcd3a76.png) 
##### 修改后

```
var desc = trans[key];
table.rows.push([key, typeof(desc) === "undefined" ? "--" : desc, obj[key]]);
```

![img](/upload/asset/f2e3a47db7556f7a64479877982b26244fdc0b0a.png) 
##### 这下直白多了。
![img](/upload/asset/b8d3053f361a8af3ed11e5e2ad30c104968fe854.png) 
  • プログラムがよく使う情報取得のインターフェースもあります

    • GetDepth: 深い情報,つまり,細かい情報を入手する.
    • GetOrders:未完成の注文情報を入手する.
    • GetPosition: 現在の保有情報を入手する.

    後にサンドボックスシステムで試してみてください: (メインループ関数でコードを追加しただけです)

    function MainLoop(){
        var account = exchange.GetAccount();            // 框架的接口函数,获取配置的账户信息。
        var obj = JSON.parse(exchange.GetRawJSON());    // 框架的接口函数,获取上一次调用的原始调用信息。这里就是GetAccount 调用时 接收到的原始信息。
        var nowTime = new Date();
        var table = {
            type : "table",
            title : "商品期货账户详细信息",
            cols : ["key", "value"],
            rows : [],
        }
        for(var key in obj){
            var desc = trans[key];
            table.rows.push([key, typeof(desc) === "undefined" ? "--" : desc, obj[key]]);
        }
        // 还记得状态栏表格么? 就是刚才我们翻译的账户信息所在的表格。其实可以多表格显示,我们来试下。
        var table2 = {
            type : "table",
            title : "GetDepth、GetOrders、GetPosition",
            cols : ["function Name", "return value"],
            rows : [],
        }
        // 使用 GetDepth 获取深度数据
        exchange.SetContractType("MA705");              // 一定记得 不论是获取数据还是下单,一定要设置或切换操作的合约。
        var depth = exchange.GetDepth();
        table2.rows.push(["GetDepth()", depth]);
      
        // 下个低价买单挂单、然后获取未完成的订单
        exchange.SetDirection("buy");                   // 一定记得 下单前要明确下单方向, 这里是开多仓。
        exchange.Buy(depth.Bids[0].Price - 20, 1);
        var PendingOrders = exchange.GetOrders();
        table2.rows.push(["GetOrders()", PendingOrders]);
      
        // 下个高价买单吃掉盘口的卖单,用获取持仓函数 GetPosition 获取信息来看下是否成交。
        exchange.SetDirection("buy");                   // 一定记得 下单前要明确下单方向, 这里是开多仓。
        exchange.Buy(depth.Asks[0].Price + 1, 1); // 比卖一价 这个价格再多出一块钱,确保吃到单子
        Sleep(1000);
        var positions = exchange.GetPosition();
        table2.rows.push(["GetPosition()", positions]);
      
        var tables = [table, table2];
      
        LogStatus("time:" + nowTime + JSON.stringify(account) + '\n`' + JSON.stringify(tables) + '`');
        throw "仅仅执行一次,抛出个错误停止程序,老白在平时也经常使用这个办法调试代码!因为有时会输出一大堆信息无从下手。";
    }
    
    逃げなさい!

    img img

  • 活気のある小さな男もSHOWが好きだ

    K線,請求書情報,SHOWなど,興味のあるデータを取得します. サンドボックスは図書データベースを統合し,手書きJSを操作し,フューチャー会社のCTPサーバーから聞いた面白いデータ (※データ) を示しました.

var chart = null    // 一个全局 变量 用来接收 Chart 函数返回的  对象。
var series = []     // 数据系列数组,用来保存 图表上的线、标记、K线等数据。
var labelIdx = []   // label 索引,
var preBarTime = 0  // K线前一柱的 时间戳 ( 毫秒数 )
var preFlagTime = 0 // 前一个  标记 的时间戳
var preDotTime = [] //            时间戳

var cfg = {    // 用来初始化设置图表的对象(即图表设置)  具体可见  HighCharts
    tooltip: {
        xDateFormat: '%Y-%m-%d %H:%M:%S, %A'
    },
    legend: {
        enabled: true,
    },
    plotOptions: {
        candlestick: {
            color: '#d75442',  // 颜色值
            upColor: '#6ba583' // 颜色值
        }
    },
    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: 2,
        inputEnabled: true
    },
    series: series,           // 全局变量赋值给该属性, 注意 数组是引用传递,  即: 浅拷贝。(不明白的可以百度:引用传递)
}

$.GetCfg = function() {       // 导出函数 , 用来获取 cfg 对象。
    return cfg
}

$.PlotHLine = function(value, label, color, style) {    // 画 x 轴 水平线。
    if (typeof(cfg.yAxis) === 'undefined') {            // 如果 没有定义 y 轴 属性, 添加y轴属性定义。
        cfg.yAxis = {
            plotLines: []                               // y 轴上的 平行于x 轴的 水平线数组。
        }
    } else if (typeof(cfg.yAxis.plotLines) === 'undefined') {  // 如果定义了 y轴 ,没有定义 水平线数组,则添加水平线数组。
        cfg.yAxis.plotLines = []
    }
    /*
    Solid
    ShortDash
    ShortDot
    ShortDashDot
    ShortDashDotDot
    Dot
    Dash
    LongDash
    DashDot
    LongDashDot
    LongDashDotDot
    */
    var obj = {                                          // 定义一个 对象,保存水平线的 各个属性。
        value: value,                                    // 在y轴上的 位置值。
        color: color || 'red',                           // 没有传入 默认红色。
        width: 2,                                        // 线的宽度是2
        dashStyle: style || 'Solid',                     // 选择线的类型,可以设置为虚线。 'dash'
        label: {                                         // 
            text: label || '',
            align: 'center'                              // 居中显示
        },
    }
    var found = false  
    for (var i = 0; i < cfg.yAxis.plotLines.length; i++) { // 遍历 水平线这个属性的 数组。
        if (cfg.yAxis.plotLines[i].label.text == label) {  // 如果找到对应的 label 的水平线
            cfg.yAxis.plotLines[i] = obj                   // 把 obj对象赋值给这个索引 指向的元素。(替换)
            found = true                                   // 标记为找到 
        }
    }
    if (!found) {                                          // 如果是没找到   (新压入)
        cfg.yAxis.plotLines.push(obj)
    }
    if (!chart) {                                          // 如果 chart 对象是 null 
        chart = Chart(cfg)                                 // 调用API Chart 初始化图表,返回 对象赋值给chart
    } else {                                               // else
        chart.update(cfg)                                  // 更新图表
    }
}

$.PlotRecords = function(records, title) {                 // 画K线  ,参数是 K线数据(数组), 标题
    var seriesIdx = labelIdx["candlestick"];               // 数据序列 的索引
    if (!chart) {                                          // 如果chart 图表对象为 null 
        chart = Chart(cfg)                                 // 初始化图表,并且返回图表对象给 chart 变量
    }
    if (typeof(seriesIdx) == 'undefined') {                // 如果获取到的数据序列索引 是未定义,执行以下if代码
        cfg.__isStock = true                               // 设置 cfg 对象 __isStock 属性为 true 
        seriesIdx = series.length                          // 设置 当前数据序列索引 为 已经存在的索引之后。
        series.push({                                      // 压入series 数据序列数组 ,该数据序列属性如下:
            type: 'candlestick',                               // type 属性  K线图
            name: typeof(title) == 'undefined' ? '' : title,   // 如果 $.PlotRecords 导出函数调用时,没有传入 title参数,则该数据序列 标题为 空字符串。
            id: 'primary',                                     // 数据序列的 id 为 primary (首要的)
            data: []                                           // 数据序列的 数据数组 ,图表上显示的数据值都储存在该数组内。
        });
        chart.update(cfg)                                  // 用设置过的 cfg 对象 更新 chart 对象。
        labelIdx["candlestick"] = seriesIdx                // 给 labelIdx (索引的标签) 添加 K线数据序列的索引,即 当前的 seriesIdx 值。
    }
    if (typeof(records.Time) !== 'undefined') {            // 如果传入的是单根 K线 数据执行以下if 代码
        var Bar = records;                                 // 把records 赋值给 Bar
        if (Bar.Time == preBarTime) {                      // preBarTime 初始是0, 如果参数传入的records 的时间戳 和 全局变量 preBarTime 相等,即认为当前的Bar (即 records) 没有更新(没有出现新的K线柱)
            chart.add(seriesIdx, [Bar.Time, Bar.Open, Bar.High, Bar.Low, Bar.Close], -1) // 根据参数传入的records 更新图表上的 值为 labelIdx["candlestick"] 的数据序列的 倒数第一个数据( 因为 add函数 最后一个参数传入的是 -1)。
        } else if (Bar.Time > preBarTime) {                // 如果 参数传入的 records 也就是 Bar 的时间戳 比 preBarTime 大,即 新出现了Bar 。
            preBarTime = Bar.Time                          // 更新preBarTime 这个全局变量用于 下次的比较。
            chart.add(seriesIdx, [Bar.Time, Bar.Open, Bar.High, Bar.Low, Bar.Close])  // 区别于 Bar.Time == preBarTime 时的操作,没有传参数 -1 ,为在数据序列最后添加一个数据。
        }
    } else {                                               // 如果传入的是一个K线数据数组, 此情况在第一次 preBarTime = 0 时 ,执行比较特殊:会逐个添加全部 records数据
        for (var i = 0; i < records.length; i++) {         // 遍历records 
            if (records[i].Time == preBarTime) {           // 如果 索引i 这个元素的时间戳 等于 上一次记录的 preBarTime 更新图表 该数据序列的最后一个数据 
                chart.add(seriesIdx, [records[i].Time, records[i].Open, records[i].High, records[i].Low, records[i].Close], -1) // 更新
            } else if (records[i].Time > preBarTime) {     // 如果索引i 这个元素的时间戳大于 preBarTime 则添加
                preBarTime = records[i].Time               // 更新preBarTime
                chart.add(seriesIdx, [records[i].Time, records[i].Open, records[i].High, records[i].Low, records[i].Close])  // 添加
            }
        }
    }
    return chart                                           // 返回 chart
}

$.PlotLine = function(label, dot, time) {                  // 画线
    if (!chart) {                                          // 如果 chart 为 null 执行以下
        cfg.xAxis = {                                      // 设置 cfg对象 x轴 的类型为 datatime 时间类型
            type: 'datetime'
        }
        chart = Chart(cfg)                                 // 调用 Chart 这个API函数(cfg为参数) 初始化图表
    }
    var seriesIdx = labelIdx[label]                        // 按照 label参数获取 标签索引 赋值给 数据序列索引。                 
    if (typeof(seriesIdx) === 'undefined') {               // 如果 labelIdx 内没有 参数传入的 label 这个标签的索引 执行以下代码 添加这个数据序列                       
        seriesIdx = series.length                          // 根据已有的数据序列的长度,赋值 新数据序列的索引
        preDotTime[seriesIdx] = 0                          // 在线条的 前一个值(preDotTime中的元素) 使用了一个数组储存(因为可能有多个线条,所以会有多个 “前一个值” ,所以用数组储存,这个“前一个值”作用类似于 preBarTime)
        labelIdx[label] = seriesIdx;                       // 把当前标签 对应的索引 储存在标签索引。
        series.push({                                      // 把数据序列 push 进 数据序列数组
            type: 'line',           // 设置当前的数据序列 类型为: 线
            yAxis: 0,               // 使用的y轴 为索引为 0 的y轴(highcharts 图表 可以有 多个 y 坐标轴,这里指定索引0的y轴)
            showInLegend: true,     // 
            name: label,            // 根据 函数传入的 参数 label 设置
            data: [],               // 数据序列的数据项
            tooltip: {              // 工具提示
                valueDecimals: 5    // 值的小数点 保留5位
            }
        })
        chart.update(cfg)           // 用cfg 对象 更新图表对象 chart
    }
    if (typeof(time) == 'undefined') {                     // 如果参数没有传入 要画线(其实是线上的点)的时间。
        time = new Date().getTime()                        // 给形参 time 赋值当前的时间戳
    }

    if (preDotTime[seriesIdx] != time) {                   // 对比当前时间如果不等于执行if 内的代码
        preDotTime[seriesIdx] = time                       // 更新 上一次时间
        chart.add(seriesIdx, [time, dot])                  // 添加参数传进的点 在x轴值为 time 的时间上
    } else {
        chart.add(seriesIdx, [time, dot], -1)              // 如果时间相等 ,则更新数据序列的最后的点
    }

    return chart                                           // 返回图表对象
}


$.PlotFlag = function(time, text, title, shape, color) {   // 在图表上添加 旗帜标签
    if (!chart) {                                          // 如果 chart 为 null
        chart = Chart(cfg)                                 // 初始化 图表
    }
    label = "flag";                                        // 设置标签类型为 flag  (旗帜)
    var seriesIdx = labelIdx[label]                        // 在数据序列索引数组中获取 旗帜 类型的索引
    if (typeof(seriesIdx) === 'undefined') {               // 如果没有该索引
        seriesIdx = series.length                          // 根据现有的索引数组长度 新设置一个索引
        labelIdx[label] = seriesIdx;                       // 储存该索引 到标签索引数组
        series.push({                                      // 向数据序列数组 压入 当前数据序列
            type: 'flags',             // 设置当前压入的数据序列 类型为 旗帜类型
            onSeries: 'primary',       // 设置 旗帜标记在哪个数据序列上,   这里设置 标记在  id 为 primary的数据序列上(即 K线数据序列)
            data: []                   // 数据序列的 数据项
        })
        chart.update(cfg)              // 更新图表对象
    }

    var obj = {                        // 根据函数参数 初始化一个对象  用来 加载到图表。
        x: time,                       // x轴的值, 即时间戳
        color: color,                  // 颜色
        shape: shape,                  // 形状
        title: title,                  // 标题
        text: text                     // 文本
    }

    if (preFlagTime != time) {         // 上一次的标记时间 如果不等于当前时间 
        preFlagTime = time             // 更新上一次的标记时间
        chart.add(seriesIdx, obj)      // 用设置好的obj对象添加该标记(写入图表)
    } else {
        chart.add(seriesIdx, obj, -1)  // 如果时间相同,则更新最后一个旗帜标记。
    }

    return chart                       // 返回图表对象
}

$.PlotTitle = function(title, chartTitle) {                     // 设置 图表 标题
    cfg.subtitle = {                                            // 根据参数 title设置子标题
        text: title
    };
    if (typeof(chartTitle) !== 'undefined') {                   // 如果 chartTitle 不等于 未定义,即传入了值
        cfg.title = {                                           // 设置 图表标题
            text: chartTitle                                    // 文本为 chartTitle
        };
    }
    if (chart) {                                                // 如果 图表已经初始化,更新图表
        chart.update(cfg)
    }
}

var isFirstPlotLine = true;
var PreBarTime = 0;
function MainLoop(){
    var info = exchange.SetContractType("MA705");
    if(!info){
        return;
    }
    var records = exchange.GetRecords();
    if(!records || records.length < 10){
        return;
    }
    var depth705 = exchange.GetDepth();
    if(!depth705 || depth705.Asks.length == 0 || depth705.Bids.length == 0){
        return;
    }
    info = exchange.SetContractType("MA709");
    if(!info){
        return;
    }
    var depth709 = exchange.GetDepth();
    if(!depth709 || depth709.Asks.length == 0 || depth709.Bids.length == 0){
        return;
    }
    $.PlotRecords(records, "MA705");                // 在图表上画出 MA705 甲醇 合约的 K线数据
    var diffA_B = depth705.Bids[0].Price - depth709.Asks[0].Price;
    var diffB_A = depth705.Asks[0].Price - depth709.Bids[0].Price;
    if(PreBarTime !== records[records.length - 1].Time){
        $.PlotLine("MA705->MA709", diffA_B);
        $.PlotLine("MA705<-MA709", diffB_A);
        PreBarTime = records[records.length - 1].Time;
        if(isFirstPlotLine){
            for(var j = 0;j < cfg.series.length; j++){
                if(cfg.series[j].name == "MA705->MA709" || cfg.series[j].name == "MA705<-MA709"){
                    cfg.series[j].yAxis = 1;
                }
            }
            isFirstPlotLine = false;
        }
    }
}

var cfg = $.GetCfg();
function main() {
    var status = null;
    cfg.yAxis = [{
            title: {text: 'K线'},            //标题
            style: {color: '#4572A7'},       //样式 
            opposite: false                  //生成右边Y轴
        },
       {
            title:{text: "另一个Y轴"},
            opposite: true                   //生成右边Y轴  ceshi
       }
    ];
    while(true){
        status = exchange.IO("status");      //  调用API 确定连接状态
        if(status === true){                 //  判断状态
            // LogStatus("已连接!");         //  在回测或者实际运行中显示一些实时数据、信息。
            // 由于MainLoop 中用到了LogStatus 所以这个地方的要先注释掉, 以免覆盖掉信息
            MainLoop();                      //  连接上 交易所服务器后,执行主要工作函数。
        }else{                               //  如果没有连接上 即 exchange.IO("status") 函数返回 false
            LogStatus("未连接状态!");         //  显示 未连接状态。
        }
        Sleep(1000);                         //  封装的睡眠函数,需要有轮询间隔, 以免访问过于频繁。CTP协议是每秒推送2次数据。
    }
}

走行して見られるように,表の差線が描かれ,MA705とMA709の契約差は,同じ品種に基づいた跨期契約組合せの利息を構築することができる.

img

もちろん,注意深い読者は,この2つの円盤差線がほぼ平行している理由がわかります.これは砂箱が実際のK線に基づいて模擬したデータであるため,一貫した変化であり,実際は少しランダムです.

この記事を書く前に,読者が私に留言をしてください! 提案や意見,もし楽しいと感じているなら,より多くのプログラムを愛する取引を愛する友人に共有することができます

https://www.fmz.com/bbs-topic/726

プログラマー littleDream 原作


もっと

中身料if ((isFirstPlotLine) } は,この文字を表示しています. for ((var j = 0;j < cfg.series.length;j++) { if ((cfg.series[j].name == "MA705->MA709" で cfg.series[j].name == "MA705<-MA709") cfg.series[j].yAxis = 1; ありがとうございました. ありがとうございました. isFirstPlotLine = false; isFirstPlotLine = false; isFirstPlotLine = false; isFirstPlotLine = false; isFirstPlotLine = false; isFirstPlotLine = false; isFirstPlotLine = false; isFirstPlotLine = false; isFirstPlotLine = false この段落は理解できない.

小さな夢この2つの線を設定するだけです.