Trò chơi JavaScript với ông già - tạo ra một đối tác bán hàng và bán hàng (để dạy cho anh ấy một số kiến thức đơn giản)

Tác giả:Giấc mơ nhỏ, Tạo: 2017-03-08 11:52:02, Cập nhật: 2017-10-11 10:37:23

Bạn có thể chơi với một người đàn ông và một người đàn bà JavaScript để tạo ra một đối tác bán hàng và mua hàng.

Dạy cho bé những kiến thức đơn giản (tạm dịch: áp dụng đường thẳng)

Trong chương trước, chúng ta đã thực hành rất nhiều mã đính kèm thú vị trong hệ thống hộp cát, và hôm nay chúng ta cần phải đưa chương trình của chúng ta ra khỏi hộp cát để nhìn thế giới bên ngoài, tất nhiên chúng ta phải dạy nó một chút trước!

  • Logic mua bán đồng tuyến

    Lý thuyết mua bán này có lẽ là chiến lược đơn giản nhất và cơ bản nhất trong thế giới giao dịch lập trình, giao dịch định lượng. Nguyên tắc rất đơn giản, sử dụng là sự tương phản giữa giá trị trung bình của giá lịch sử để xác định hướng đi tương lai của giá hàng hóa. Đó là: một vật thể tiêu chuẩn như MA705 hợp đồng (kế hoạch methanol) chu kỳ trung bình 10 Bar (k-cột) và chu kỳ trung bình 8 Bar, sự khác biệt của sự so sánh. Sau đây chỉ đơn giản là 10 chu kỳ dài là đường chậm, 8 chu kỳ tương đối ngắn là đường nhanh, có thể có người đọc không hiểu, không quan trọng, chúng tôi để bạn nhỏ vẽ ra trong hệ thống hộp cát ((nhìn thấy mã hình ảnh trong chương trước và vẽ lại hình ảnh đơn giản so easy!), Để dễ dàng sử dụng, mã của một đống hình ảnh trong chương trước đã được đóng gói thành một tập tin.

    Địa chỉ mẫu hình ảnh:https://www.fmz.com/strategy/27293

    img

    Sau khi sao chép, nó sẽ xuất hiện trong bảng tham khảo của mẫu, sử dụng trực tiếp, hãy nhớ chọn.

    Có rất nhiều cách để làm điều này.

    var PreRecordTime = 0;
    var isFirst = true;
    function MainLoop(){
        var info = exchange.SetContractType("MA705");
        if(!info){
            return;
        }
        var records = exchange.GetRecords();
        if(!records || records.length < 10){    // 因为长周期 为10 所以要计算10个Bar的均值, 必须要有10个K线才能计算出来。
            return;                             // 不满足的情况,返回,重新来。
        }
        if(isFirst){
            PreRecordTime = records[records.length - 1].Time;
            isFirst = false;
        }
      
        var fastLine = TA.MA(records, 8);       // 均线指标,计算出给定 的K线数据 8个 Bar 的均值, 按顺序压入数组(随时间序列,和K线Bar时间同步形成一条线,所以叫快线) 返回这个数组给变量fastLine
        var slowLine = TA.MA(records, 10);      // 同上 区别是10个Bar ,所以叫慢线(周期大,均值变化的慢)。
      
        var current_fastLine = fastLine[fastLine.length - 1];  // 这个数组的 倒数第一个索引 fastLine.length - 1 ,也就是表示的 快线的最后一个值 ,就是当前K线 对应的 快线均值。
        var current_slowLine = slowLine[slowLine.length - 1];  // 同上
      
        if(PreRecordTime !== records[records.length - 1].Time){ // K线更新了才确定当前最新的前一根Bar
            $.PlotLine("fastLine", fastLine[fastLine.length - 2], PreRecordTime);  // 引用了模板,可以直接使用这个模块导出函数 画线。(其实就是封装成 模块了)
            $.PlotLine("slowLine", slowLine[slowLine.length - 2], PreRecordTime);  // 同上  画指标线
            PreRecordTime = records[records.length - 1].Time;
            $.PlotLine("fastLine", current_fastLine, records[records.length - 1].Time);
            $.PlotLine("slowLine", current_slowLine, records[records.length - 1].Time); 
        }else{
            $.PlotLine("fastLine", current_fastLine, records[records.length - 1].Time);  // 当前的不停在变动。
            $.PlotLine("slowLine", current_slowLine, records[records.length - 1].Time); 
        }
        $.PlotRecords(records, "MA705");           // 画K线
    }
    
    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次数据。
        }
    }
    

    Trong khi đó, một số người khác đang sử dụng các hệ thống sandbox.img

    Bạn có thể thấy một đường dây nhanh xuất hiện từ phía dưới và đi qua đường dây chậm khi tăng một phần.

    // 判断指标形态
    if(fastLine.length > 3 && slowLine.length > 3 && fastLine[fastLine.length - 3] < slowLine[slowLine.length - 3] && fastLine[fastLine.length - 2] > slowLine[slowLine.length - 2]){
          $.PlotFlag(records[records.length - 2].Time, '已经交叉', 'X'); // 在图表上打个标记
    }
    

    img

    Các tuyến đường nhanh đi từ trên xuống qua đường chậm cũng tương tự. Chúng ta sẽ thêm một đoạn mã vào chương trình theo logic này.

  • Các giáo viên sẽ học cách giao dịch một cách đơn giản

    Đây là một mô-đun khác được viết bằng JavaScript, nhưng tôi sẽ không trích dẫn nó lần này, nó được nhúng trực tiếp vào chương trình của chúng tôi. MA705 vẫn sử dụng hợp đồng (methanol) như một dấu hiệu giao dịch.

// --------交易模块-------------
var Interval = 500;
var SlideTick = 1;
var RiskControl = false;

var __orderCount = 0
var __orderDay = 0

function CanTrade(tradeAmount) {
    if (!RiskControl) {
        return true
    }
    if (typeof(tradeAmount) == 'number' && tradeAmount > MaxTradeAmount) {
        Log("风控模块限制, 超过最大下单量", MaxTradeAmount, "#ff0000 @");
        throw "中断执行"
        return false;
    }
    var nowDay = new Date().getDate();
    if (nowDay != __orderDay) {
        __orderDay = nowDay;
        __orderCount = 0;
    }
    __orderCount++;
    if (__orderCount > MaxTrade) {
        Log("风控模块限制, 不可交易, 超过最大下单次数", MaxTrade, "#ff0000 @");
        throw "中断执行"
        return false;
    }
    return true;
}

function init() {
    if (typeof(SlideTick) === 'undefined') {
        SlideTick = 1;
    } else {
        SlideTick = parseInt(SlideTick);
    }
    Log("商品交易类库加载成功");
}

function GetPosition(e, contractType, direction, positions) {
    var allCost = 0;
    var allAmount = 0;
    var allProfit = 0;
    var allFrozen = 0;
    var posMargin = 0;
    if (typeof(positions) === 'undefined' || !positions) {
        positions = _C(e.GetPosition);
    }
    for (var i = 0; i < positions.length; i++) {
        if (positions[i].ContractType == contractType &&
            (((positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) && direction == PD_LONG) || ((positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) && direction == PD_SHORT))
        ) {
            posMargin = positions[i].MarginLevel;
            allCost += (positions[i].Price * positions[i].Amount);
            allAmount += positions[i].Amount;
            allProfit += positions[i].Profit;
            allFrozen += positions[i].FrozenAmount;
        }
    }
    if (allAmount === 0) {
        return null;
    }
    return {
        MarginLevel: posMargin,
        FrozenAmount: allFrozen,
        Price: _N(allCost / allAmount),
        Amount: allAmount,
        Profit: allProfit,
        Type: direction,
        ContractType: contractType
    };
}


function Open(e, contractType, direction, opAmount) {
    var initPosition = GetPosition(e, contractType, direction);
    var isFirst = true;
    var initAmount = initPosition ? initPosition.Amount : 0;
    var positionNow = initPosition;
    while (true) {
        var needOpen = opAmount;
        if (isFirst) {
            isFirst = false;
        } else {
            positionNow = GetPosition(e, contractType, direction);
            if (positionNow) {
                needOpen = opAmount - (positionNow.Amount - initAmount);
            }
        }
        var insDetail = _C(e.SetContractType, contractType);
        //Log("初始持仓", initAmount, "当前持仓", positionNow, "需要加仓", needOpen);
        if (needOpen < insDetail.MinLimitOrderVolume) {
            break;
        }
        if (!CanTrade(opAmount)) {
            break;
        }
        var depth = _C(e.GetDepth);
        var amount = Math.min(insDetail.MaxLimitOrderVolume, needOpen);
        e.SetDirection(direction == PD_LONG ? "buy" : "sell");
        var orderId;
        if (direction == PD_LONG) {
            orderId = e.Buy(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), Math.min(amount, depth.Asks[0].Amount), contractType, 'Ask', depth.Asks[0]);
        } else {
            orderId = e.Sell(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), Math.min(amount, depth.Bids[0].Amount), contractType, 'Bid', depth.Bids[0]);
        }
        // CancelPendingOrders
        while (true) {
            Sleep(Interval);
            var orders = _C(e.GetOrders);
            if (orders.length === 0) {
                break;
            }
            for (var j = 0; j < orders.length; j++) {
                e.CancelOrder(orders[j].Id);
                if (j < (orders.length - 1)) {
                    Sleep(Interval);
                }
            }
        }
    }
    var ret = {
        price: 0,
        amount: 0,
        position: positionNow
    };
    if (!positionNow) {
        return ret;
    }
    if (!initPosition) {
        ret.price = positionNow.Price;
        ret.amount = positionNow.Amount;
    } else {
        ret.amount = positionNow.Amount - initPosition.Amount;
        ret.price = _N(((positionNow.Price * positionNow.Amount) - (initPosition.Price * initPosition.Amount)) / ret.amount);
    }
    return ret;
}

function Cover(e, contractType) {
    var insDetail = _C(e.SetContractType, contractType);
    while (true) {
        var n = 0;
        var opAmount = 0;
        var positions = _C(e.GetPosition);
        for (var i = 0; i < positions.length; i++) {
            if (positions[i].ContractType != contractType) {
                continue;
            }
            var amount = Math.min(insDetail.MaxLimitOrderVolume, positions[i].Amount);
            var depth;
            if (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) {
                depth = _C(e.GetDepth);
                opAmount = Math.min(amount, depth.Bids[0].Amount);
                if (!CanTrade(opAmount)) {
                    return;
                }
                e.SetDirection(positions[i].Type == PD_LONG ? "closebuy_today" : "closebuy");
                
                e.Sell(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), opAmount, contractType, positions[i].Type == PD_LONG ? "平今" : "平昨", 'Bid', depth.Bids[0]);
                n++;
            } else if (positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) {
                depth = _C(e.GetDepth);
                opAmount = Math.min(amount, depth.Asks[0].Amount);
                if (!CanTrade(opAmount)) {
                    return;
                }
                e.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
                e.Buy(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), opAmount, contractType, positions[i].Type == PD_SHORT ? "平今" : "平昨", 'Ask', depth.Asks[0]);
                n++;
            }
        }
        if (n === 0) {
            break;
        }
        while (true) {
            Sleep(Interval);
            var orders = _C(e.GetOrders);
            if (orders.length === 0) {
                break;
            }
            for (var j = 0; j < orders.length; j++) {
                e.CancelOrder(orders[j].Id);
                if (j < (orders.length - 1)) {
                    Sleep(Interval);
                }
            }
        }
    }
}

var PositionManager = (function() {
    function PositionManager(e) {
        if (typeof(e) === 'undefined') {
            e = exchange;
        }
        if (e.GetName() !== 'Futures_CTP') {
            throw 'Only support CTP';
        }
        this.e = e;
        this.account = null;
    }
    // Get Cache
    PositionManager.prototype.Account = function() {
        if (!this.account) {
            this.account = _C(this.e.GetAccount);
        }
        return this.account;
    };
    PositionManager.prototype.GetAccount = function(getTable) {
        this.account = _C(this.e.GetAccount);
        if (typeof(getTable) !== 'undefined' && getTable) {
            return AccountToTable(this.e.GetRawJSON())
        }
        return this.account;
    };

    PositionManager.prototype.GetPosition = function(contractType, direction, positions) {
        return GetPosition(this.e, contractType, direction, positions);
    };

    PositionManager.prototype.OpenLong = function(contractType, shares) {
        if (!this.account) {
            this.account = _C(this.e.GetAccount);
        }
        return Open(this.e, contractType, PD_LONG, shares);
    };

    PositionManager.prototype.OpenShort = function(contractType, shares) {
        if (!this.account) {
            this.account = _C(this.e.GetAccount);
        }
        return Open(this.e, contractType, PD_SHORT, shares);
    };

    PositionManager.prototype.Cover = function(contractType) {
        if (!this.account) {
            this.account = _C(this.e.GetAccount);
        }
        return Cover(this.e, contractType);
    };
    PositionManager.prototype.CoverAll = function() {
        if (!this.account) {
            this.account = _C(this.e.GetAccount);
        }
        while (true) {
            var positions = _C(this.e.GetPosition)
            if (positions.length == 0) {
                break
            }
            for (var i = 0; i < positions.length; i++) {
                // Cover Hedge Position First
                if (positions[i].ContractType.indexOf('&') != -1) {
                    Cover(this.e, positions[i].ContractType)
                    Sleep(1000)
                }
            }
            for (var i = 0; i < positions.length; i++) {
                if (positions[i].ContractType.indexOf('&') == -1) {
                    Cover(this.e, positions[i].ContractType)
                    Sleep(1000)
                }
            }
        }
    };
    PositionManager.prototype.Profit = function(contractType) {
        var accountNow = _C(this.e.GetAccount);
        return _N(accountNow.Balance - this.account.Balance);
    };

    return PositionManager;
})();

$.NewPositionManager = function(e) {
    return new PositionManager(e);
};

// Via: http://mt.sohu.com/20160429/n446860150.shtml
$.IsTrading = function(symbol) {
    var now = new Date();
    var day = now.getDay();
    var hour = now.getHours();
    var minute = now.getMinutes();

    if (day === 0 || (day === 6 && (hour > 2 || hour == 2 && minute > 30))) {
        return false;
    }
    symbol = symbol.replace('SPD ', '').replace('SP ', '');
    var p, i, shortName = "";
    for (i = 0; i < symbol.length; i++) {
        var ch = symbol.charCodeAt(i);
        if (ch >= 48 && ch <= 57) {
            break;
        }
        shortName += symbol[i].toUpperCase();
    }

    var period = [
        [9, 0, 10, 15],
        [10, 30, 11, 30],
        [13, 30, 15, 0]
    ];
    if (shortName === "IH" || shortName === "IF" || shortName === "IC") {
        period = [
            [9, 30, 11, 30],
            [13, 0, 15, 0]
        ];
    } else if (shortName === "TF" || shortName === "T") {
        period = [
            [9, 15, 11, 30],
            [13, 0, 15, 15]
        ];
    }


    if (day >= 1 && day <= 5) {
        for (i = 0; i < period.length; i++) {
            p = period[i];
            if ((hour > p[0] || (hour == p[0] && minute >= p[1])) && (hour < p[2] || (hour == p[2] && minute < p[3]))) {
                return true;
            }
        }
    }

    var nperiod = [
        [
            ['AU', 'AG'],
            [21, 0, 02, 30]
        ],
        [
            ['CU', 'AL', 'ZN', 'PB', 'SN', 'NI'],
            [21, 0, 01, 0]
        ],
        [
            ['RU', 'RB', 'HC', 'BU'],
            [21, 0, 23, 0]
        ],
        [
            ['P', 'J', 'M', 'Y', 'A', 'B', 'JM', 'I'],
            [21, 0, 23, 30]
        ],
        [
            ['SR', 'CF', 'RM', 'MA', 'TA', 'ZC', 'FG', 'IO'],
            [21, 0, 23, 30]
        ],
    ];
    for (i = 0; i < nperiod.length; i++) {
        for (var j = 0; j < nperiod[i][0].length; j++) {
            if (nperiod[i][0][j] === shortName) {
                p = nperiod[i][1];
                var condA = hour > p[0] || (hour == p[0] && minute >= p[1]);
                var condB = hour < p[2] || (hour == p[2] && minute < p[3]);
                // in one day
                if (p[2] >= p[0]) {
                    if ((day >= 1 && day <= 5) && condA && condB) {
                        return true;
                    }
                } else {
                    if (((day >= 1 && day <= 5) && condA) || ((day >= 2 && day <= 6) && condB)) {
                        return true;
                    }
                }
                return false;
            }
        }
    }
    return false;
};
// --------交易模块完结----------
var PreRecordTime = 0;
var isFirst = true;
var IDLE = 0;
var OPENLONG = 1;
var COVER = 2;
var STATE = IDLE;
var InitAccount = null;
function MainLoop(){
    var info = exchange.SetContractType("MA705");
    if(!info){
        return;
    }
    var records = exchange.GetRecords();
    if(!records || records.length < 10){    // 因为长周期 为10 所以要计算10个Bar的均值, 必须要有10个K线才能计算出来。
        return;                             // 不满足的情况,返回,重新来。
    }
    if(isFirst){
        PreRecordTime = records[records.length - 1].Time;
        InitAccount = exchange.GetAccount();
        isFirst = false;
    }
    
    var fastLine = TA.MA(records, 5);       // 均线指标,计算出给定 的K线数据 8个 Bar 的均值, 按顺序压入数组(随时间序列,和K线Bar时间同步形成一条线,所以叫快线) 返回这个数组给变量fastLine
    var slowLine = TA.MA(records, 10);      // 同上 区别是10个Bar ,所以叫慢线(周期大,均值变化的慢)。
    
    var current_fastLine = fastLine[fastLine.length - 1];  // 这个数组的 倒数第一个索引 fastLine.length - 1 ,也就是表示的 快线的最后一个值 ,就是当前K线 对应的 快线均值。
    var current_slowLine = slowLine[slowLine.length - 1];  // 同上
    
    if(PreRecordTime !== records[records.length - 1].Time){ // K线更新了才确定当前最新的前一根Bar
        $.PlotLine("fastLine", fastLine[fastLine.length - 2], PreRecordTime);  // 引用了模板,可以直接使用这个模块导出函数 画线。(其实就是封装成 模块了)
        $.PlotLine("slowLine", slowLine[slowLine.length - 2], PreRecordTime);  // 同上  画指标线
        PreRecordTime = records[records.length - 1].Time;
        $.PlotLine("fastLine", current_fastLine, records[records.length - 1].Time);
        $.PlotLine("slowLine", current_slowLine, records[records.length - 1].Time); 
    }else{
        $.PlotLine("fastLine", current_fastLine, records[records.length - 1].Time);  // 当前的不停在变动。
        $.PlotLine("slowLine", current_slowLine, records[records.length - 1].Time); 
    }
    $.PlotRecords(records, "MA705");           // 画K线
    // 判断指标形态
    if(STATE === IDLE && fastLine.length > 3 && slowLine.length > 3 && fastLine[fastLine.length - 3] < slowLine[slowLine.length - 3] && fastLine[fastLine.length - 2] > slowLine[slowLine.length - 2]){
        P.OpenLong("MA705", 1);
        STATE = OPENLONG;
        $.PlotFlag(new Date().getTime(), '快线上传慢线', 'OPENLONG');
    }
    if(STATE === OPENLONG && fastLine[fastLine.length - 3] > slowLine[slowLine.length - 3] && fastLine[fastLine.length - 2] < slowLine[slowLine.length - 2]){
        P.Cover("MA705");
        STATE = COVER;   
        $.PlotFlag(new Date().getTime(), '快线下传慢线', 'COVER');
    }
    if(STATE === COVER){
        var nowAccount = exchange.GetAccount();
        LogProfit(nowAccount.Balance - InitAccount.Balance, nowAccount);
        STATE = IDLE;
    }
}

var cfg = $.GetCfg();
var P = null;
function main() {
    var status = null;
    P = $.NewPositionManager(exchange);      // 构造一个 用于控制交易细节的对象。
    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次数据。
    }
}

Các nhà nghiên cứu cho biết:

img img img

Trong phản hồi, thị trường sốc đã rút lui rất nhiều, có thể thấy rằng logic giao dịch này là không hoàn hảo, lợi nhuận trong xu hướng tăng lên, không có nghĩa là không có vấn đề ((thị trường sốc có thể bị sốc = _=)). Vì vậy, đây chỉ là nghiên cứu học tập, không sử dụng thực tế.
  • Hãy thử sử dụng công nghệ Simnow để tạo ra một tài khoản tương lai giả mạo.

    Thông tin chi tiết về việc đăng ký simnow Commodity Futures có thể được tìm thấy tại bài viết sau:https://www.fmz.com/bbs-topic/325Các giao dịch tương tự có thể được lập trình trực tiếp bằng giao thức CTP.

    Chương trình chạy trong một thời gian, chính sách là bản chất trình diễn, tất cả mọi người chỉ để tham khảo, nếu cần cải thiện, bạn có thể thêm kiểm soát vị trí, ngăn chặn stop loss, báo động trước khi bắt đầu, vv.

    img

    Bạn có thể dùng tài khoản giả để kiểm tra.

    Hãy nhìn vào một chương trình khác của tôi chạy trên đĩa mô phỏng, và điểm phức tạp này cho thấy một chút:

    img

    Có một số người thích chơi JS, nghiên cứu về JS, lấy mã nguồn:https://www.fmz.com/strategy/17289

    img

    img

Trước khi viết điều này, chào đón độc giả để lại cho tôi một lời nhắn! để đưa ra các đề xuất và ý kiến, nếu bạn cảm thấy thú vị, bạn có thể chia sẻ với nhiều bạn bè yêu thích chương trình yêu thích giao dịch

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

Các lập trình viên LittleDream


Thêm nữa