js+python websocket實時推數據


js與python 通過websocket通信

## user.html

{% extends "monitor.html" %}
{% load staticfiles %}
<head>

</head>
{% block title %}仿真策略監控{% endblock %}

{% block styles %}
    <style>

        th {
            text-align: center;
        }

        label {
            width: 100px;
        }

        input, select {
            width: 160px;
            height: 24px;
            background: #0F0F0F;
            border: 1px solid #3C3C3C;
            border-radius: 1px;
        }

        button {
            border: none;
        }

        thead {
            background-color: #444;
        }

        .bootstrap-table {
            border-style: solid;
            border-width: 3px;
        }

        .pagination-info {
            display: none
        }

        .page-list {
            display: none
        }

        #sse {
            color: white;
        }

        #startstg .modal-body {
            background-color: #282923;
            padding: 0px;
        }

        {#無數據時鼠標懸浮的背景顏色#}
        .table-hover > tbody > tr:hover {
            background-color: #444444;
            cursor: pointer;
        }

        {#無數據時的奇數背景顏色#}
        {#        .table-striped > tbody > tr:nth-of-type(odd) {#}
        {#            background-color: transparent;#}
        {#        }#}

        #StgFileShow, #ConFileShow {
            background-color: #282923;
            color: #90918b;
            border-width: 0px;
            width: 448px;
            height: 100%;
        }

        #stg_check {
            padding: 10px;
        }

        .row {
            margin-top: 10px;
        }


        .left {
            width: 60%;
            height: 400px;
            float: left;
        }

        .right {
            width: 37%;
            height: 400px;
            float: left;
        {#margin-right: 30px;#}
        }

        .buttom {
            width: 97%;
            height: 400px;
            float: left;
            margin-top: 90px;
        }

        {#tab表頭的選中的樣式#}
        .nav-tabs-custom > .nav-tabs > li.active > a, .nav-tabs-custom > .nav-tabs > li.active:hover > a {
            background-color: red;
            color: white;
        }

        {#tab樣式#}
        .nav-tabs-custom > .nav-tabs > li {
            border-top: transparent;
            margin-bottom: -2px;
            margin-right: 2px;
        }

        .nav-tabs > li {
            float: left;
            margin-bottom: -1px;
        }

        .nav > li > a {
            position: relative;
            display: block;
            padding: 4px 20px;
        }

        {#tab頭的位置及邊框顏色透明#}
        .nav-tabs-custom > .nav-tabs {
            border-bottom-color: rgba(0, 0, 0, 0.2);
            border-top-right-radius: 3px;
            border-top-left-radius: 3px;
            margin-left: 10px;
        }

        {#tab中的a標簽懸浮樣式#}

        .nav-tabs-custom > .nav-tabs > li > a, .nav-tabs-custom > .nav-tabs > li > a:hover {
            background: rgba(255, 255, 255, .15);
            margin: 0;
        }

        .nav-tabs-custom > .nav-tabs > li > a {
            color: #d2d6de;
            border-radius: 0;
        }


        .simcheck {
            float: left;
            margin-top: 15px;
            margin-left: 10px
        }

        .packheadiv {
            float: left;
            margin-top: 15px;
            margin-left: 10px
        }

        .cancelheadiv {
            float: left;
            margin-top: 15px;
            margin-left: 10px
        }

        .simcheckhead, .simHead, .packHead {
            color: white;
            margin-left: 30px;
        }

        .cancelheadiv, .packheadiv, .traheadiv {
            margin-left: 30px;
        }

        .bootstrap-table .fixed-table-container .table {
            width: 100%;
            margin-bottom: 0 !important;
            color: #939393;
        }

        .fixed-table-toolbar {
            background-color: #141414;
        }

        .checksearch, .packsearch, .simsearch {
            width: 87px;
            height: 26px;
            background: #3C3C3C;
            border-radius: 3px;

        }

        .cancelorder {
            width: 119px;
            height: 28px;
            background: inherit;
            background-color: rgba(255, 204, 153, 1);
            box-sizing: border-box;
            border-width: 1px;
            border-style: solid;
            border-color: rgba(121, 121, 121, 1);
            border-radius: 5px;
            -moz-box-shadow: none;
            -webkit-box-shadow: none;
            box-shadow: none;
            color: #2B2B2B;
            margin-left: 15%;
            cursor: pointer;
        }

        tr.activetable, input.activetable {
            background-color: white;
            width: 114px;
            height: 30px;
        }


    </style>
{% endblock %}

{% block content %}
    <section class="content-header" style="background-color: #000000">
        <h1 style="color: #FFFFFF">
            仿真策略監控
            <small>{{ allHtml.title }}</small>
        </h1>
    </section>
    <div class="modal fade" id="upmodal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
        <div class="modal-dialog" style="width: 450px;">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title">上傳策略</h4>
                </div>
                <div class="modal-body" style="padding: 30px;height:auto">
                    <form>
                        <div style="margin-top:3%; display: block">
                            <label>策略名稱</label>
                            <input class="opts form-control" id="confirmpwd" name="confirmpwd" type="text"
                                   placeholder="確認密碼">
                        </div>
                        <div style="margin-top:3%"><label>指定服務器</label>
                            <select class="opts form-control" id="isactive" name="isactive">
                                <option name="isactive" value="1">
                                    服務器1
                                </option>
                                <option name="isactive" value="2">
                                    服務器2
                                </option>
                            </select>
                        </div>
                        <div style="margin-top:3%"><label>策略組</label>
                            <select class="opts form-control" id="stgroup" name="stgroup">
                                <option name="isactive" value="1">
                                    做市策略
                                </option>
                                <option name="isactive" value="2">
                                    CTA策略
                                </option>
                                <option name="isactive" value="3">
                                    算法策略
                                </option>
                            </select>
                        </div>
                        <div style="margin-top:3%"><label>選擇文件</label>
                            <input class="opts form-control" id="maxlogined" name="maxlogined" type="file"
                                   placeholder="選擇文件">
                        </div>
                        <div style="margin-top:3%"><label>策略參數</label>

                            <table style="background-color: white">
                                <thead>
                                <tr class="activetable">
                                    <th>
                                        操作
                                    </th>
                                    <th>
                                        參數名*
                                    </th>
                                    <th>
                                        參數值*
                                    </th>
                                    <th>
                                        參數描述
                                    </th>
                                </tr>
                                </thead>
                                <tbody id="tb1">
                                <tr class="activetable">
                                    <td>
                                        <span style="padding:3px;cursor: pointer" class="btn-success small"
                                              onclick="new_col('tb1')">新增行
                                        </span>
                                    </td>
                                    <td>
                                        <input class="activetable" name="stg_val">
                                    </td>
                                    <td>
                                        <input class="activetable" name="stg_choices">
                                    </td>
                                    <td>
                                        <input class="activetable" name="stg_desc">
                                    </td>
                                </tr>
                                </tbody>
                            </table>
                        </div>
                        <div style="margin-top:3%">
                            <span style="margin-left:10%;cursor:pointer;padding:5px" class="btn-primary"
                                  onclick="sub(gets)">提交</span>
                            <span style="margin-left:60%;cursor:pointer;padding:5px" class="btn-success"
                                  onclick="quit__()">取消</span>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <div class="modal fade" id="editstg" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
        <div class="modal-dialog" style="width: 450px;">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span></button>
                    <h4 class="modal-title">修改策略參數</h4>
                </div>
                <div class="modal-body" style="padding: 30px;height:auto">
                    <form>
                        <div style="margin-top:3%"><label>參數名</label>
                            <input type="text" id="paraname" readonly>
                        </div>
                        <div style="margin-top:3%"><label>參數值</label>
                            <input type="text" id="paravalue">
                        </div>
                        <div style="margin-top:3%"><label>描述</label>
                            <input type="text" id="paradesc">
                        </div>

                        <div style="margin-top:3%">
                            <span style="margin-left:10%;cursor:pointer;padding:5px" class="btn-primary"
                                  onclick="sub(gets)">提交</span>
                            <span style="margin-left:60%;cursor:pointer;padding:5px" class="btn-success"
                                  onclick="quit__()">取消</span>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
    <!-- Main content -->
    <section class="content" style="background-color: #000000">
        <div class="row">
            <div class="col-xs-12">
                <div class="nav-tabs-custom left" style="background-color: #000000">
                    <ul class="nav nav-tabs">
                        <li class="active"><a href="#fa-simapproval" onclick="tabs(1)" data-toggle="tab">策略列表</a></li>
                    </ul>
                    <div class="tab-content" style="background-color: #000000">
                        <!--仿真審批-->
                        <div class="tab-pane active mailbox-messages" id="fa-simapproval">
                            <div class=" mailbox-messages">
                                <div class="cancelheadiv" style="margin-left: 25px"></div>
                                <table id="toolbar"></table>
                                <table class="table table-bordered table-striped table-hover" id="RuleTable">
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="nav-tabs-custom right" style="background-color: #000000">
                    <ul class="nav nav-tabs">
                        <li id="stgpara"><a href="#fa-stgpara" onclick="tabs(1)" data-toggle="tab">策略參數</a></li>
                        <li id="stgindic"><a href="#fa-stgindic" onclick="tabs(2)" data-toggle="tab">策略指標</a></li>
                    </ul>
                    <div class="tab-content" style="background-color: #000000">
                        <!--策略參數-->
                        <div class="tab-pane mailbox-messages" id="fa-stgpara">
                            <div class=" mailbox-messages">
                                <table id="toolbar"></table>
                                <div class="cancelheadiv" style="margin-left: 25px">
                                </div>
                                <table class="table table-bordered table-striped table-hover" id="StgparaTable">
                                </table>
                            </div>
                        </div>
                        <!--策略指標-->
                        <div class="tab-pane mailbox-messages" id="fa-stgindic">
                            <div class=" mailbox-messages">
                                <table id="toolbar"></table>
                                <div class="cancelheadiv" style="margin-left: 25px">
                                </div>
                                <table class="table table-bordered table-striped table-hover" id="StgdicTable">
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
                <div class="nav-tabs-custom buttom" style="background-color: #000000">
                    <ul class="nav nav-tabs">
                        <li><a href="#fa-stgmm" onclick="btabs(1)" data-toggle="tab">做市類策略</a></li>
                        <li><a href="#fa-stgcta" onclick="btabs(2)" data-toggle="tab">CTA類策略</a></li>
                    </ul>
                    <div class="tab-content stginfo" style="background-color: #000000">
                        <!--做市類策略-->
                        <div class="tab-pane mailbox-messages" id="fa-stgmm">
                            <div class=" mailbox-messages">
                                <table id="toolbar"></table>
                                <div class="cancelheadiv" style="margin-left: 25px">
                                </div>
                                <table class="table table-bordered table-striped table-hover" id="StgMMTable">
                                </table>
                            </div>
                        </div>
                        <!--CTA類策略-->
                        <div class="tab-pane mailbox-messages" id="fa-stgcta">
                            <div class=" mailbox-messages">
                                <table id="toolbar"></table>
                                <div class="cancelheadiv" style="margin-left: 25px">
                                </div>
                                <table class="table table-bordered table-striped table-hover" id="StgCtaTable">
                                </table>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </section>

    <div id="sse">
        <form id="userLogin">
            <p>用戶名:<input type="text" id="uname"></p>
            <p>密&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;碼:<input type="password" id="upwd"></p>
            <p><input type="button" value="登 錄" onclick="sendMsg_login()"></p>
        </form>
        <p>
        <p>合約:<input type="text" id="instrumentID" value="T2009"></p>
        <input type="button" onclick="ws.SubscribeMarketData($('#instrumentID').val())" value="訂閱行情"/>
        <h2>用戶登錄查看到的數據</h2>
        <div id="console" style="width: 100%;height:200px;">


        </div>
        </p>

    </div>

{% endblock %}

{% block scripts %}


    <script type="text/javascript">
		
		//頁面加載后就調用
        {#import lo from "../../../commonStatic/bower_components/moment/src/locale/lo";#}

        {#$(function () {#}
        {#    alert("qew")#}
        {# })#}

        var timer, clickFlag, stgtype = false;//外部變量,這三個變量是定時器是否存在的標志
        //初始化策略列表
        $("#RuleTable").bootstrapTable('destroy').bootstrapTable({
            uniqueId: "RuleID",
            // 策略列表table
            columns: [{
                field: 'RuleID',
                title: '策略ID'
            }, {
                field: 'RuleName',
                title: '策略名稱'
            }, {
                field: 'LServer',
                title: '指定服務器',
                {#formatter: function (value, row, index) {#}
                {#    $.post("{% url 'trade:serverfind' %}", {"ruleid": row.RuleID}, function (r) {#}
                {#        if (r){#}
                {#            console.log("123adce", r)#}
                {#            return r#}
                {#        }#}
                {#    })#}
                {# }#}
            }, {
                field: 'RuleRunStatus',
                title: '運行狀態',
                formatter: function (value, row, index) {
                    if (value === "0") {
                        return "停止"
                    } else if (value === "1") {
                        return "啟動"
                    } else {
                        return value
                    }
                }
            }, {
                field: 'LOption',
                title: '操作',
                formatter: function (value, row, index) {
                    if (row.RuleRunStatus === "1") {
                        var start = "<span onclick=\"startstg('" + row.RuleID + "')\" class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' disabled='disabled'> <i class='fa'></i>啟動</span> ";
                        var end = "&nbsp<span onclick=\"endstg('" + row.RuleID + "')\" type='button' class='btn btn-danger btn-xs btn-flat btn_operation'> <i class='fa'></i>停止</span>";
                    } else if (row.RuleRunStatus === "0") {
                        var start = "<span onclick=\"startstg('" + row.RuleID + "')\" class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' > <i class='fa'></i>啟動</span> ";
                        var end = "&nbsp<span onclick=\"endstg('" + row.RuleID + "')\" type='button' class='btn btn-danger btn-xs btn-flat btn_operation'disabled='disabled'> <i class='fa'></i>停止</span>";
                    }
                    return start + end + "&nbsp<span onclick=\"del_('" + row.RuleID + "')\" type='button' class='btn btn-primary btn-xs btn-flat btn_operation'> <i class='fa'></i>人工錄入</span>";
                }
            },
                {
                    field: 'LRisk',
                    title: '風控',
                }],
            onClickRow: function (row, $element, field) {
                {#console.log(rules)#}
                //當前行的父元素tbody下的所有tr移除樣式類
                $element.parent().children().removeClass("onclickrow")
                $element.addClass("onclickrow")
                var ruleid = row.RuleID
                //點擊一行數據,初始化策略參數和策略指標
                RulePropIndic(ruleid)
                //剛開始需要把上次的定時器取消掉,然后再進行定時任務。
                if (clickFlag) {
                    clearInterval(timer)
                }
                clickFlag = true;
                timer = setInterval(function () {
                    //先移除所有的數據,然后再append
                    $('#StgparaTable').bootstrapTable('removeAll');
                    $('#StgdicTable').bootstrapTable('removeAll');
                    for (let key in ruleProps) {
                        //策略參數
                        if (key.startsWith(ruleid + "prop")) {
                            $('#StgparaTable').bootstrapTable('append', ruleProps[key]);
                        }
                        //策略指標
                        else if (key.startsWith(ruleid + "indic")) {
                            $('#StgdicTable').bootstrapTable('append', ruleProps[key]);
                        }
                    }
                    var clickFlag = false
                }, 2000)
                //關閉定時器
                {#clearInterval(timer)#}
            }
        })

        //初始化策略參數
        $("#StgparaTable").bootstrapTable('destroy').bootstrapTable({
            {#uniqueId: "RuleID" + "prop_" + "PropKey",#}
            // 策略列表table
            columns: [{
                field: 'RuleID',
                title: '策略ID',
                visible: false
            }, {
                field: 'PropKey',
                title: '參數名'
            }, {
                field: 'PropValue',
                title: '參數值'
            }, {
                field: 'Description',
                title: '描述',
            }, {
                field: 'POption',
                title: '操作',
                formatter: function (value, row, index) {
                    return "<span onclick=\"modify('" + row.RuleID  + "'" + "," + "'" + row.PropKey + "'"+
                        ","+ "'" +row.PropValue+"'"+"," + "'" + row.Description+"')\" class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' data-target='#startstg'> <i class='fa'></i>編輯</span> "
                }
            }]
        })

        //初始化策略指標
        $("#StgdicTable").bootstrapTable('destroy').bootstrapTable({
            uniqueId: "RuleID",
            // 策略列表table
            columns: [{
                field: 'RuleID',
                title: '策略ID',
                visible: false
            }, {
                field: 'IndicatorKey',
                title: '指標名'
            }, {
                field: 'IndicatorValue',
                title: '指標值'
            }, {
                field: 'Description',
                title: '指標描述',
            }]
        })

        //初始化做市策略
        $("#StgMMTable").bootstrapTable('destroy').bootstrapTable({
            uniqueId: "RuleID",
            // 做市策略table
            columns: [
                //第一行表頭
                [{
                    field: 'RuleID',
                    title: '',
                    {#visible: false#}
                }, {
                    field: 'mm',
                    title: '做市券盤口',
                    colspan: 2
                }, {
                    field: 'hedge',
                    title: '對沖券盤口',
                    colspan: 2
                }, {
                    field: '',
                    title: '',
                    colspan: 2
                }, {
                    field: "my",
                    title: "我方報價",
                    colspan: 2
                }, {
                    field: '',
                    title: '',
                    colspan: 2
                }],
                //第二行表頭
                [{
                    field: "RuleName",
                    title: "策略名稱"
                }, {
                    field: "bid_mm",
                    title: "Bid"
                }, {
                    field: "ofr_mm",
                    title: "Ofr"
                }, {
                    field: "bid_hedge",
                    title: "Bid"
                }, {
                    field: "ofr_hedge",
                    title: "Ofr"
                }, {
                    field: "spread",
                    title: "合理利差"
                }, {
                    field: "offset",
                    title: "偏移"
                }, {
                    field: "bid_my",
                    title: "Bid"
                }, {
                    field: "ofr_my",
                    title: "Ofr"
                }, {
                    field: "position",
                    title: "持倉"
                }, {
                    field: "is_broken",
                    title: "斷腿警告"
                }]
            ]
        })

        //初始化cta策略
        $("#StgCtaTable").bootstrapTable('destroy').bootstrapTable({
            uniqueId: "RuleID",
            // CTA策略table
            columns: [{
                field: "RuleName",
                title: "策略名稱"
            }, {
                field: "indicator1",
                title: "指標1"
            }, {
                field: "indicator2",
                title: "指標2"
            }, {
                field: "trade_time",
                title: "最新成交時間"
            }, {
                field: "yield",
                title: "收益率"
            }, {
                field: "high",
                title: "高"
            }, {
                field: "low",
                title: "低"
            }, {
                field: "decision",
                title: "決策點"
            }, {
                field: "trigger",
                title: "觸發點"
            },
                {
                    field: "direction",
                    title: "方向"
                },
                {
                    field: "position",
                    title: "持倉"
                },
                {
                    field: "volume_left",
                    title: "待執行量"
                },
                {
                    field: "ytm_signal",
                    title: "信號價格"
                },
                {
                    field: "ytm_limit",
                    title: "限價"
                }]
        })

        //rules用於存儲 策略列表和不同類型的策略, ruleprops用於存儲策略參數和策略指標
        var rules = {}, ruleProps = {};
        var idsarry = []; //windows-server, 47.102.219.52:9000
        var ws = $.trade("ws://127.0.0.1:9000/", {//
            OnFrontConnected: function () {
                $("#console").append("<br/>TD連接成功");
            },
            OnFrontDisconnected: function () {
                $("#console").append("<br/>TD已斷開");
            },
            OnRspUserLogin: function (r) {
                //{TID: data:{},RspInfoField:{ErrorID:“”,ErrorMsg:“”}
                if (r.RspInfoField.ErrorID == 0) {
                    $("#console").append("<br/>" + r.data.CustomerID + " 交易登錄成功");
                    user.CustomerID = r.data.CustomerID;
                } else
                    $("#console").append("<br />交易登錄失敗:" + r.RspInfoField.ErrorMsg);
            },
            OnRspError: function (r) {
                $("#console").append("<br/>" + r.RspInfoField.ErrorID + ":" + r.RspInfoField.ErrorMsg);
            },
            OnRtnRule: function (r) {
                if (rules[r.data.RuleID] == undefined) {
                    if (r.data.RuleRunStatus == undefined) {
                        r.data.RuleRunStatus = 0;
                    }
                    $('#RuleTable').bootstrapTable('append', r.data);
                } else {
                    $('#RuleTable').bootstrapTable('updateRow', {
                        index: r.data.RuleID,
                        row: r.data
                    });
                }
                rules[r.data.RuleID] = r.data;
                rules[r.data.RuleID].RuleRunStatus = 0;
                {#ruleType[r.data.RuleID] = r.data#}

                $("#console").append("<br/>策略信息: " + r.data.RuleID + ":" + r.data.RuleName);
            },
            OnRtnRuleStatus: function (r) {
                rules[r.data.RuleID].RuleRunStatus = r.data.RuleRunStatus;
                if (rules[r.data.RuleID].RuleRunStatus == undefined) {
                    $('#RuleTable').bootstrapTable('append', r.data);
                } else {
                    $('#RuleTable').bootstrapTable('updateRow', {
                        index: r.data.RuleID,
                        row: r.data
                    });
                }
                $("#console").append("<br/>策略狀態 " + r.data.RuleID + ":" + r.data.RuleRunStatus);
            },
            OnRtnRuleProp: function (r) {
                if (rules[r.data.RuleID] !== undefined) {
                    rules[r.data.RuleID][r.data.PropKey] = r.data.PropValue
                }
                ruleProps[r.data.RuleID + "prop" + "_" + r.data.PropKey] = r.data
                $("#console").append("<br/>策略參數:" + r.data.RuleID + ":" + r.data.PropKey + ":" + r.data.PropValue + ":" + r.data.Description);

            },
            OnRtnRuleIndicator: function (r) {
                if (rules[r.data.RuleID] !== undefined) {
                    rules[r.data.RuleID][r.data.IndicatorKey] = r.data.IndicatorValue
                }
                ruleProps[r.data.RuleID + "indic" + "_" + r.data.IndicatorKey] = r.data;
                $("#console").append("<br/>策略指標:" + r.data.RuleID + ":" + r.data.IndicatorKey + ":" + r.data.IndicatorValue + ":" + r.data.Description);
            },
            events: {
                say: function (e) {
                    alert(e.data.name); // 'foo'
                    alert(e.data.text); // 'baa'
                }
            }
        });

        function sendMsg_login() {
            var dt = {
                "Tid": 1111, "data": {
                    'CustomerID': '1001',
                    'Password': '123456',
                    'MacAddress': '',
                    'SubInstrumentMethod': '',
                    'IsRule': ''
                }
            };
            ws.ReqUserLogin("1001", "123456");
        }

        function submitit() {
            $('#rf').submit();
        }

        function tabs(n) {
            if (n == 1) {
                $('#fa-stgpara').addClass('active');
                $('#fa-stgindic').removeClass('active');
            } else {
                $('#fa-stgpara').removeClass('active');
                $('#fa-stgindic').addClass('active');
            }
        }

        //點擊做事類策略或者CTA類策略
        function btabs(n) {
            initypestg()
            if (n == 1) {
                $('#fa-stgmm').addClass('active');
                $('#fa-stgcta').removeClass('active');
                clearInterval(stgtype)
                stgtype = setInterval(function () {
                    //先移除所有的數據,然后再append
                    $('#StgMMTable').bootstrapTable('removeAll');
                    $('#StgCtaTable').bootstrapTable('removeAll');
                    //bootstraptable渲染不同類型的策略
                    for (let key in rules) {
                        if (rules[key].hasOwnProperty("bid_mm")) {
                            $('#StgMMTable').bootstrapTable('append', rules[key]);
                        } else if (rules[key].hasOwnProperty("trigger")) {
                            $('#StgCtaTable').bootstrapTable('append', rules[key]);
                        }
                    }
                }, 2000)

            } else {
                $('#fa-stgmm').removeClass('active');
                $('#fa-stgcta').addClass('active');
                clearInterval(stgtype)
                stgtype = setInterval(function () {
                    //先移除所有的數據,然后再append
                    $('#StgMMTable').bootstrapTable('removeAll');
                    $('#StgCtaTable').bootstrapTable('removeAll');
                    //bootstraptable渲染不同類型的策略
                    for (let key in rules) {
                        if (rules[key].hasOwnProperty("bid_mm")) {
                            $('#StgMMTable').bootstrapTable('append', rules[key]);
                        } else if (rules[key].hasOwnProperty("trigger")) {
                            $('#StgCtaTable').bootstrapTable('append', rules[key]);
                        }
                    }
                }, 2000)
            }

        }

        //做事類策略或者cTa類策略的初始化
        function initypestg() {
            //先移除所有的數據,然后再append
            $('#StgMMTable').bootstrapTable('removeAll');
            $('#StgCtaTable').bootstrapTable('removeAll');
            //bootstraptable渲染做事類策略
            for (let key in rules) {
                if (rules[key].hasOwnProperty("bid_mm")) {
                    $('#StgMMTable').bootstrapTable('append', rules[key]);
                } else if (rules[key].hasOwnProperty("trigger")) {
                    $('#StgCtaTable').bootstrapTable('append', rules[key]);
                }
            }
        }


        function status(th) {
            var StgID = $(th).attr('id');
            var S = $(th).attr('status');
            Status = S.split('-')[0];
            EnvID = S.split('-')[1];
            $.post("{{url}}", {
                'StgID': JSON.stringify([StgID]),
                'Status': JSON.stringify([Status]),
                'EnvID': JSON.stringify([EnvID])
            }, function (r) {
                if (r) {
                    window.location.reload();
                }
            })
        }

        function RefreshIt() {
            var opt = {
                url: "{{url}}",
                silent: true,
                query: {
                    env: $('#env').val()
                }
            };
            $("#RuleTable").bootstrapTable('refresh', opt);
        }


        function pack(sid) {
            $.post('{{url}}', {'sid': sid}, function (r) {
                $("#packTable").bootstrapTable('refresh');
            })
        }

        //策略參數和策略指標的定時任務
        function RulePropIndic(ruleid) {
            //先移除所有的數據,然后再append
            $('#StgparaTable').bootstrapTable('removeAll');
            $('#StgdicTable').bootstrapTable('removeAll');
            for (let key in ruleProps) {
                //策略參數
                if (key.startsWith(ruleid + "prop")) {
                    $('#StgparaTable').bootstrapTable('append', ruleProps[key]);
                }
                //策略指標
                else if (key.startsWith(ruleid + "indic")) {
                    $('#StgdicTable').bootstrapTable('append', ruleProps[key]);
                }
            }

        }

        function new_col(idn) {
            var h = "<tr class='activetable'>" + '<td>' +
                '                                        <span style="padding:3px; cursor: pointer" class="btn-danger small"\n' +
                '                                              onclick="$(this).parent().parent().remove()">刪除行' +
                '                                        </span>' +
                '                                    </td>' +
                "<td><input class='activetable' name='stg_val'></td>" +
                "</td>" +
                "<td><input class='activetable' name='stg_choices'></td>" +
                "<td><input class='activetable' name='stg_desc'></td>" +
                "</tr>";
            $('#' + idn).append($(h));
        };

        //上傳列表模態框展示
        function uploadmodal() {
            $("#upmodal").modal("show")
        }

        //啟動策略
        function startstg(id) {
            ws.ReqOptionStg("start", id);
        }

        //停止策略
        function endstg(id) {
            ws.ReqOptionStg("end", id);
        }

        //編輯策略參數
        function modify(id, key, value, desc) {
            ws.ReqStgPara(id, key, value, desc)

        }

    </script>
    <style>
        {#加載數據時的樣式#}
        .bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
            align-items: center;
            background: #141414;
            display: none;
            justify-content: center;
            position: absolute;
            bottom: 0;
            width: 100%;
            z-index: 1000;
        }

        {#bootstraptable表格布局樣式#}
        .bootstrap-table .fixed-table-container .table {
            width: 90%;
            margin-bottom: 0 !important;
            margin: 0 auto;
            border: solid #444 !important
        }

        {#bootstraptable上邊框#}
        .table-bordered {
            border: 1px solid transparent !important;
        }

        {#bootstraptable左側邊框#}
        .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td {
            border: 1px solid rgba(255, 255, 255, .15);
        }


        {#每一個table的樣式#}
        .bootstrap-table .fixed-table-container .table thead th .th-inner {
            padding: .75rem;
            vertical-align: bottom;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            border: 1px solid #444 !important;
        }

        .tab-content {
            height: 380px;
        }

        .stginfo {
            height: 400px;
        }

        .onclickrow {
            background-color: #444444;
        }
    </style>


{% endblock %}
            
            

////// websocket.js
/*
 * jQuery Web Sockets Plugin v0.0.4
 * https://github.com/dchelimsky/jquery-websocket
 * http://code.google.com/p/jquery-websocket/
 *
 * This document is licensed as free software under the terms of the
 * MIT License: http://www.opensource.org/licenses/mit-license.php
 *
 * Copyright (c) 2010 by shootaroo (Shotaro Tsubouchi).
 */
const TSS_DIALOG = 1;		//對話流
const TSS_PRIVATE = 2;		//會員私有流
const TSS_PUBLIC = 3;		//公共流
const TSS_QUERY = 4;		//查詢
const TSS_USER = 5;		//交易員私有流

//用戶啟動策略請求
const FTD_TID_ReqOptionStg = 0x00002101;
//編輯策略參數請求
const FTD_TID_ReqStgPara = 0x00002102
//用戶登錄請求
const FTD_TID_ReqUserLogin = 0x00001001;
//用戶登錄響應
const FTD_TID_RspUserLogin = 0x00001002;
//用戶退出請求
const FTD_TID_ReqUserLogout = 0x00001005;
//用戶退出響應
const FTD_TID_RspUserLogout = 0x00001006;
//報單錄入請求
const FTD_TID_ReqOrderInsert = 0x00003001;
//報單錄入錯誤時響應
const FTD_TID_RspOrderInsert = 0x00003002;
//報單修改/撤單請求
const FTD_TID_ReqOrderAction = 0x00003003;
//報單修改/撤單錯誤時響應
const FTD_TID_RspOrderAction = 0x00003004;
//請求查詢合約
const FTD_TID_ReqQryInstrument = 0x00002001;
//請求查詢合約響應
const FTD_TID_RspQryInstrument = 0x00002002;
//請求查詢債券
const FTD_TID_ReqQryBond = 0x00001109;
//請求查詢債券響應
const FTD_TID_RspQryBond = 0x0000110A;
//查詢債券期貨和可交割券關系表
const FTD_TID_ReqQryBondFutureDeliverable = 0x00001105;
//債券期貨和可交割券關系表回報
const FTD_TID_RspQryBondFutureDeliverable = 0x00001106;
//請求查詢物理賬號資金
const FTD_TID_ReqQryAccount = 0x00002005;
//請求查詢物理賬號資金響應
const FTD_TID_RspQryAccount = 0x00002006;
//請求查詢物理賬號持倉
const FTD_TID_ReqQryPosition = 0x00002007;
//請求查詢物理賬號持倉響應
const FTD_TID_RspQryPosition = 0x00002008;
//請求訂閱行情
const FTD_TID_ReqSubMarketData = 0x00002009;
//請求退訂行情
const FTD_TID_ReqUnSubMarketData = 0x0000200b;
//請求訂閱行情BAR
const FTD_TID_ReqSubMarketDataBar = 0x0000200d;
//請求退訂行情BAR
const FTD_TID_ReqUnSubMarketDataBar = 0x0000200e;
//請求訂閱新本幣平台行情
const FTD_TID_ReqSubCFETSMarketData = 0x00001130;
//請求退訂新本幣平台行情
const FTD_TID_ReqUnsubCFETSMarketData = 0x00001132;
//查詢債券成交信息
const FTD_TID_ReqQryBondExecReport = 0x0000110D;
//查詢債券成交信息響應
const FTD_TID_RspQryBondExecReport = 0x0000110E;
//請求從行情中心訂閱行情
const FTD_TID_ReqSubMDfromMC = 0x00001140;
//請求從行情中心退訂行情
const FTD_TID_ReqUnSubMDfromMC = 0x00001142;
//請求訂閱策略對應RFQ報價
const FTD_TID_ReqSubRFQByClientID = 0x00001150;
//請求退訂策略對應RFQ報價
const FTD_TID_ReqUnSubRFQByClientID = 0x00001152;
//請求增加策略
const FTD_TID_ReqInsertRule = 0x00001010;
//請求增加策略響應
const FTD_TID_RspInsertRule = 0x00001011;
//請求修改策略狀態
const FTD_TID_ReqUpdateRuleStatus = 0x00001030;
//請求修改策略狀態響應
const FTD_TID_RspUpdateRuleStatus = 0x00001031;
//請求更新策略屬性
const FTD_TID_ReqUpdateRuleProp = 0x00001040;
//請求更新策略屬性響應
const FTD_TID_RspUpdateRuleProp = 0x00001041;
//策略回報
const FTD_TID_RtnRule = 0x00001012;
//策略運行狀態變化回報
const FTD_TID_RtnRuleStatus = 0x00001032;
//策略屬性變化回報
const FTD_TID_RtnRuleProp = 0x00001044;
//策略指標回報
const FTD_TID_RtnRuleIndicator = 0x00001045;
//深度行情回報
const FTD_TID_RtnDepthMarketData = 0x00006001;
//行情Bar回報
const FTD_TID_RtnMarketDataBar = 0x00006002;
//報價行情回報
const FTD_TID_RtnQuoteMarket = 0x00001103;
//債券市場成交回報
const FTD_TID_RtnBondExecReport = 0x00001110;
//新本幣平台深度行情回報
const FTD_TID_RtnCFETSDepthMarketData = 0x00001133;
//報價接收回報
const FTD_TID_RtnCfetsRFQReceive = 0x00001134;
//CME/CBOT深度行情回報
const FTD_TID_RtnCMEDepthMarketData = 0x00001144;
//報單回報
const FTD_TID_RtnRuleOrder = 0x00001102;
//成交回報
const FTD_TID_RtnRuleTrade = 0x00001111;
//持倉變動回報
const FTD_TID_RtnRulePosition = 0x00001100;
//資金變動回報
const FTD_TID_RtnRuleAccount = 0x00001101;
//Api建立連接回報
const FTD_TID_RtnApiConnected = 0x0000110B;
//Api斷開連接回報
const FTD_TID_RtnApiDisconnected = 0x0000110C;
//客戶端顯示消息回報
const FTD_TID_RtnUserMessage = 0x0000110F;
//用戶分賬號信息回報
const FTD_TID_RtnCustomerAccountInfo = 0x00001114;
//交易成員基本信息回報
const FTD_TID_RtnCfetsTradeMember = 0x00001135;
//心跳通知
const FTD_TID_NotifyHeartBeat = 0x00004500;
//請求更新合約做市狀態
const FTD_TID_ReqUpdMakeMarketStatus = 0x00001115;
//請求更新合約做市狀態響應
const FTD_TID_RspUpdMakeMarketStatus = 0x00001116;
//合約做市狀態回報
const FTD_TID_RtnMakeMarketStatus = 0x00001117;
//請求更新合約做市參數
const FTD_TID_ReqUpdMakeMarketPara = 0x00001118;
//請求更新合約做市參數響應
const FTD_TID_RspUpdMakeMarketPara = 0x00001119;
//合約做市參數回報
const FTD_TID_RtnMakeMarketPara = 0x0000111A;
//請求查詢策略限倉額度
const FTD_TID_ReqQryRulePositionLimit = 0x00001120;
//請求查詢策略限倉額度響應
const FTD_TID_RspQryRulePositionLimit = 0x00001121;
//請求更改策略限倉額度
const FTD_TID_ReqRulePositionLimitAction = 0x00001122;
//請求更改策略限倉額度響應
const FTD_TID_RspRulePositionLimitAction = 0x00001123;
//請求訂閱價差
const FTD_TID_ReqSubPriceSpread = 0x0000112a;
//請求退訂價差
const FTD_TID_ReqUnsubPriceSpread = 0x0000112c;
//合約價差回報
const FTD_TID_RtnInsPriceSpread = 0x0000112b;

(function ($) {
    $.extend({
        websocket: function (url, s, protocols) {
            var ws, _s = s;
            var settings = {
                message: function () {
                    console.log("message function is undefined");
                },
                options: {}, events: {}, ver: "1.0", company: "上海爾易信息科技有限公司"
            };
            _reconnect = function (url, protocols) {
                if (protocols) {
                    ws = window['MozWebSocket'] ? new MozWebSocket(url, protocols) : window['WebSocket'] ? new WebSocket(url, protocols) : null;
                } else {
                    ws = window['MozWebSocket'] ? new MozWebSocket(url) : window['WebSocket'] ? new WebSocket(url) : null;
                }
                ws._url = url;
                ws._protocols = protocols;
                $.extend(settings, $.websocketSettings, _s);
                ws.nRequestID = 1;
                if (ws) {
                    $(ws)
                        .bind('open', settings.OnFrontConnected)
                        .bind('close', settings.OnFrontDisconnected)
                        .bind('message', settings.message)
                        .bind('message', function (e) {
                            var m = JSON.stringify(e.originalEvent.data);
                            //eval("m=" + e.originalEvent.data);
                            var h = settings.events[m.Tid];//調用指定的方法
                            if (h) h.call(this, m);
                        });
                    ws._send = ws.send;
                    ws.send = function (type, data) {
                        var m = {Tid: type};//類型
                        m = $.extend(true, m, $.extend(true, {}, settings.options, m));
                        if (data) {
                            delete data["_id"];
                            m['data'] = data;
                        }
                        m['RequestID'] = ws.nRequestID++;
                        return this._send(JSON.stringify(m));
                    };
                    ws.reconnect = function () {
                        setTimeout(function () {
                            _reconnect(ws._url, ws._protocols);
                        }, 1000 * 30);
                        //_reconnect(ws._url, ws._protocols);
                    };
                }
            };
            _reconnect(url, protocols);
            // $(window).unload(function () {
            //     ws.close();
            //     ws = null;
            // });
            $(window).on("unload",function () {
                ws.close();
                ws = null;
            });
            return ws;
        }


        , trade: function (url, s, protocols) {
            var tradeWS = {nRequestID: 1};

            var settings = {
                OnFrontConnected: function () {
                    console.log("OnFrontConnected");
                },
                OnFrontDisconnected: function () {
                    console.log("OnFrontDisconnected");
                },
                OnHeartBeatWarning: function (nTimeLapse) {
                    console.log("OnHeartBeatWarning: " + nTimeLapse);
                },
                OnRspError: function (r) {
                },
                OnRspUserLogin: function (r) {
                },
                OnRspUserLogout: function (r) {
                },
                OnRtnDepthMarketData: function (r) {
                },
                OnRspSubMarketData: function (r) {
                },
                OnRspUnSubMarketData: function (r) {
                },
                OnRtnRule: function (r) {
                },
                OnRtnRuleStatus: function (r) {
                },
                OnRtnRuleProp: function (r) {
                },
                OnRtnRuleIndicator: function (r) {
                },

                message: function (msg) {
                    try {
                        if (event.data instanceof Blob) {
                            var reader = new FileReader();
                            reader.onloadend = function () {
                                console.log(reader.result);
                            };
                            reader.readAsText(event.data, "gbk");//utf-8
                        } else {
                            eval("r=" + event.data);
                            switch (r.Tid) {
                                case FTD_TID_RtnDepthMarketData: {
                                    tradeWS.OnRtnDepthMarketData(r);
                                    break;
                                }
                                case FTD_TID_RspUserLogin: {
                                    tradeWS.OnRspUserLogin(r);
                                    break;
                                }
                                // case FTD_TID_RspError: {
                                //     tradeWS.OnRspError(r);
                                //     break;
                                // }
                                case FTD_TID_RtnRule: {//策略回報
                                    tradeWS.OnRtnRule(r);
                                    break;
                                }
                                case FTD_TID_RtnRuleStatus: {//策略運行狀態變化回報
                                    tradeWS.OnRtnRuleStatus(r);
                                    break;
                                }
                                case FTD_TID_RtnRuleProp: {//策略屬性變化回報
                                    tradeWS.OnRtnRuleProp(r);
                                    break;
                                }
                                case FTD_TID_RtnRuleIndicator: {//策略指標回報
                                    tradeWS.OnRtnRuleIndicator(r);
                                    break;
                                }
                                case FTD_TID_RspUserLogout: {
                                    tradeWS.OnRspUserLogout(r);
                                    break;
                                }
                                case FTD_TID_RspSubMarketData: {
                                    tradeWS.OnRspSubMarketData(r);
                                    break;
                                }
                                case FTD_TID_RspUnSubMarketData: {
                                    tradeWS.OnRspUnSubMarketData(r);
                                    break;
                                }
                                case FTD_TID_IntlRtnDepthMarketData: {
                                    tradeWS.OnRspError(r);
                                    break;
                                }
                                default:
                                    OnHeartBeatWarning(r);
                            }

                            console.log("log: " + event.data);
                        }

                    } catch (e) {
                        console.log(e.message + " : " + event.data);
                    }
                },
                options: {},
                events: {}
            };
            $.extend(settings, $.websocketSettings, s, tradeWS);
            tradeWS.ws = $.websocket(url, settings, protocols);

            tradeWS.OnFrontConnected = settings.OnFrontConnected;
            tradeWS.OnFrontDisconnected = settings.OnFrontDisconnected;
            tradeWS.OnHeartBeatWarning = settings.OnHeartBeatWarning;
            tradeWS.OnRspError = settings.OnRspError;
            tradeWS.OnRspUserLogin = settings.OnRspUserLogin;
            tradeWS.OnRspUserLogout = settings.OnRspUserLogout;
            tradeWS.OnRtnDepthMarketData = settings.OnRtnDepthMarketData;
            tradeWS.OnRspSubMarketData = settings.OnRspSubMarketData;
            tradeWS.OnRspUnSubMarketData = settings.OnRspUnSubMarketData;
            tradeWS.OnRtnRule = settings.OnRtnRule;
            tradeWS.OnRtnRuleStatus = settings.OnRtnRuleStatus;
            tradeWS.OnRtnRuleProp = settings.OnRtnRuleProp;
            tradeWS.OnRtnRuleIndicator = settings.OnRtnRuleIndicator;

            //啟動或停止策略發送到后台
            tradeWS.ReqOptionStg = function(optype, ruleid){
              this.ws.send(FTD_TID_ReqOptionStg, {
                  "optype": optype,
                  "stgid": ruleid
              })
            };

            //策略參數發送到websocket
            tradeWS.ReqStgPara = function(ruleid, key, value, desc){
                this.ws.send(FTD_TID_ReqStgPara, {
                    "stgid": ruleid,
                    "stgname": key,
                    "stgvalue": value,
                    "stgdesc": encodeURI(desc) //中文亂碼,用url編碼,python在后台url解碼
                })
            }

            tradeWS.ReqUserLogin = function (u, p) {
                this.ws.send(FTD_TID_ReqUserLogin, {
                    'CustomerID': u,
                    'Password': p,
                    'ProductInfo': '',
                    'InterfaceProductInfo': 'websocket'
                });
            };
            tradeWS.SubscribeMarketData = function (ppInstrumentIDs) {
                if (ppInstrumentIDs.length > 0)
                    this.ws.send(FTD_TID_ReqSubMarketData, {'InstrumentID': ppInstrumentIDs, "ExchangeID": "CFFEX"});
                else
                    alert("請選擇要訂閱行情的合約!");
            };
            tradeWS.UnSubscribeMarketData = function (ppInstrumentIDs) {
                if (ppInstrumentIDs.length > 0)
                    this.ws.send(FTD_TID_ReqUnSubMarketData, {'InstrumentIDs': ppInstrumentIDs});
                else
                    alert("請選擇要取消訂閱行情的合約!");
            };
            return tradeWS;
        }
    });

})(jQuery);


                        
            
## 后端代碼(STCHftData.py)
# encoding: UTF-8

from time import sleep

from hft_api import HftApi

import os
import json
import threading
import copy
import multiprocessing
from websocket_server import WebsocketServer

global signal

from enum import Enum
from process_manager import process_manager
import pandas as pd
from sqlalchemy import create_engine
import urllib.parse


class run_status_enum(Enum):
    運行中 = 1
    已結束 = 2  # 回測模擬
    已分析 = 3  # 回測模擬
    未運行 = 4
    待運行 = 5
    待停止 = 6
    錯誤 = 7  # 回測模擬


# signal = threading.Event()
def print_dict(d):
    """按照鍵值打印一個字典"""
    for key, value in d.items():
        print(key + ':' + str(value))


def simple_log(func):
    """簡單裝飾器用於輸出函數名"""

    def wrapper(*args, **kw):
        print(str(func.__name__))
        return func(*args, **kw)

    return wrapper


class TestMdApi(HftApi):
    """測試用實例"""

    def __init__(self, Queue1):
        """Constructor"""
        super(TestMdApi, self).__init__()
        # 1. 創建1個隊列
        self.tid_Queue = Queue1

    @simple_log
    def onFrontConnected(self):
        """服務器連接"""
        signal = 1
        pass

    @simple_log
    def onFrontDisconnected(self, n):
        """服務器斷開"""
        print(n)
        signal = 0

    @simple_log
    def onHeartBeatWarning(self, n):
        """心跳報警"""
        print(n)

    @simple_log
    def onRspError(self, error, n, last):
        """錯誤"""
        print_dict(error)

    @simple_log
    def onRspUserLogin(self, data, error, data2, n, last):
        """登陸回報"""
        print_dict(data)
        print_dict(error)

    @simple_log
    def onRspUserLogout(self, data, error, n, last):
        """登出回報"""
        print_dict(data)
        print_dict(error)

    def onRspUnSubMarketData(self, data, error, n, last):
        """退訂合約回報"""
        print_dict(data)
        print_dict(error)

    def onRtnDepthMarketData(self, data):
        """行情推送"""
        print_dict(data)
        # print('子進程1進程>>', os.getpid())
        package = {'Tid': 0x00002008, 'data': data}
        self.save_as_json(package, "onRtnDepthMarketData")

    # ----------------------------------------------------------------------
    def onRtnForQuoteRsp(self, data):
        """行情推送"""
        print_dict(data)

    def onRspUpdateRuleStatus(self, data, error, n, last):
        """更改C++ 策略更新狀態響應"""
        # print_dict(error)
        package = {'Tid': 0x00001031, 'data': data}
        self.save_as_json(package)

    def onRspUpdateRuleProp(self, data, error, n, last):
        # print_dict(error)
        """更改C++ 策略更新屬性響應"""
        package = {'Tid': 0x00001041, 'data': data}
        self.save_as_json(package, "onRspUpdateRuleProp")

    def onRtnRuleIndicator(self, data):
        """策略指標回報"""
        package = {'Tid': 0x00001045, 'data': data}
        self.save_as_json(package, "onRtnRuleIndicator")
        # save ruleid+key

    def onRtnRuleProp(self, data):
        """策略屬性變化回報"""
        package = {'Tid': 0x00001044, 'data': data}
        self.save_as_json(package, "onRtnRuleProp")
        # save

    def onRtnRuleStatus(self, data):
        """策略狀態變化回報"""
        package = {'Tid': 0x00001032, 'data': data}
        self.save_as_json(package)

    @simple_log
    def onRtnRule(self, data):
        """策略回報"""
        package = {'Tid': 0x00001012, 'data': data}
        self.save_as_json(package, "onRtnRule")

    """
    主動API接口封裝
    """

    # 在C++環境中創建MdApi對象,傳入參數是希望用來保存.con文件的地址
    # @param pszFlowPath 存貯訂閱信息文件的目錄,默認為當前目錄
    # @return 創建出的UserApi
    # modify for udp marketdata
    def CreateFtdcMdApi(self, path):
        if os.path.exists(path) and path[-1] == '\\':
            type = ''
            self.createUserApi(path, type)
        else:
            print('con file path error')

    # 刪除接口對象本身
    # @remark 不再使用本接口對象時,調用該函數刪除接口對象
    def Release(self):
        self.release()

    # 初始化
    # @remark 初始化運行環境,只有調用后,接口才開始工作
    def Init(self):
        self.init()

    # 等待接口線程結束運行
    # @return 線程退出代碼
    def Join(self):
        self.join()

    # 退出
    def Exit(self):
        self.exit()

    # 獲取當前交易日
    # @retrun 獲取到的交易日
    # @remark 只有登錄成功后,才能得到正確的交易日
    def GetTradingDay(self):
        trade_date = self.getTradingDay()
        return trade_date

    # 注冊前置機網絡地址
    # @param pszFrontAddress:前置機網絡地址。
    # @remark 網絡地址的格式為:“protocol://ipaddress:port”,如:”tcp://127.0.0.1:17001”。
    # @remark “tcp”代表傳輸協議,“127.0.0.1”代表服務器地址。”17001”代表服務器端口號。
    def RegisterFront(self, ip_address):
        self.registerFront(ip_address)

    # 用戶登錄請求
    def ReqUserLogin(self):
        # signal.wait()
        # 登陸
        print("ready to login")
        loginReq = {}  # 創建一個空字典
        loginReq['CustomerID'] = '1001'  # 參數作為字典鍵值的方式傳入
        loginReq['Password'] = '123456'  # 鍵名和C++中的結構體成員名對應
        loginReq['MacAddress'] = '00-50-56-C0-00-01'
        loginReq['SubInstrumentMethod'] = '0'
        loginReq['IsRule'] = 0
        self.reqUserLogin(loginReq, 1)

    # 登出請求
    def ReqUserLogout(self):
        self.reqUserLogout()

        # 訂閱行情。
        # @param ppInstrumentID 合約ID
        # @param nCount 要訂閱/退訂行情的合約個數

    def SubscribeMarketData(self, msg):
        subReq = {}
        subReq['InstrumentID'] = msg['data']['InstrumentID']
        subReq['ExchangeID'] = msg['data']['ExchangeID']
        self.reqSubMarketData(subReq, 2)

        # self.subscribeMarketData(ppInstrumentID)

    # 退訂行情。
    # @param ppInstrumentID 合約ID
    # @param nCount 要訂閱/退訂行情的合約個數
    def UnSubscribeMarketData(self, ppInstrumentID):
        self.unSubscribeMarketData(ppInstrumentID)

    # 請求修改策略狀態
    def ReqUpdateRuleStatus(self, req):
        self.reqUpdateRuleStatus(req, 3)

    # 請求修改策略狀態
    def ReqUpdateRuleProp(self, req):
        self.reqUpdateRuleProp(req, 4)

    # 業務流程處理
    def subs(self):
        self.CreateFtdcMdApi('mdcon\\')
        self.RegisterFront("tcp://127.0.0.1:32201")
        self.Init()
        sleep(1)
        self.ReqUserLogin()
        sleep(1)

        # 請求修改策略狀態_啟動策略
        ruleReq = {}  # 創建一個空字典
        ruleReq['RuleID'] = 1  # 策略代碼
        ruleReq['RuleOperType'] = '0'  # 策略運行狀態類型設定
        self.ReqUpdateRuleStatus(ruleReq)

        # 請求更新策略屬性
        # for i in range(0,1000):
        ruleReq2 = {}  # 創建一個空字典
        ruleReq2['RuleID'] = 1  # 策略代碼
        ruleReq2['PropKey'] = 'OrderPrice'  # 策略屬性鍵
        ruleReq2['PropValue'] = '99.5'  # 策略屬性值
        ruleReq2['PropType'] = '1'  # 策略屬性值類型
        ruleReq2['PropValueItems'] = '1'  # 策略屬性值選項
        ruleReq2['Description'] = '報單價格'.encode('gbk')  # 描述
        self.ReqUpdateRuleProp(ruleReq2)

        # # 請求修改策略狀態_關閉策略
        # ruleReq['RuleID'] = 1  # 策略代碼
        # ruleReq['RuleOperType'] = '1'  # 策略運行狀態類型設定
        # self.ReqUpdateRuleStatus(ruleReq)

    # 數據轉換
    def save_as_json(self, package, func=""):
        json_package = json.dumps(package)
        # self.tid_Queue.put(json_package)
        self.tid_Queue.put(package)
        # if self.tid_Queue.qsize() % 10 == 0:
            # print('打包 現在隊列里面有包數:', self.tid_Queue.qsize())


#####################################
class switch(object):
    def __init__(self, value):
        self.value = value
        self.fall = False

    def __iter__(self):
        """Return the match method once, then stop"""
        yield self.match
        raise StopIteration

    def match(self, *args):
        """Indicate whether or not to enter a case suite"""
        if self.fall or not args:
            return True
        elif self.value in args:  # changed for v1.5, see below
            self.fall = True
            return True
        else:
            return False


# 自定義常量類,類名要大寫,不可修改。python中只規定常量大寫,建議不可修改,若想不可修改,自定義類。
# 策略回報
FTD_Tid_RtnRule = 0x00001012;
# 策略運行狀態變化回報
FTD_Tid_RtnRuleStatus = 0x00001032;
# 策略屬性變化回報
FTD_Tid_RtnRuleProp = 0x00001044;
# 策略指標回報
FTD_Tid_RtnRuleIndicator = 0x00001045;

global client_list
client_list = []
global total_dict


class SocketEnv():
    def __init__(self, queue1):
        """Constructor"""
        super(SocketEnv, self).__init__()
        self.total_dict = {}  # 全量數據
        # tid_Queue          增量數據隊列
        self.tid_Queue = queue1

    r = 'r是什么,r是傳遞的data,case是r.Tid'
    v = 'ten'  # 若按照此寫法,v=r.Tid

    def caseswitch(self, req_msg):
        for case in switch(req_msg["Tid"]):
            print('前端請求HFT數據:', type(req_msg), req_msg)
            # 帶on的是回報,不帶on的是請求,關注請求即可
            if case(FTD_Tid_RtnRule):
                # self.api.onRtnDepthMarketData(req_msg)
                break
            if case(FTD_Tid_RtnRuleStatus):
                # self.api.onRspUserLogin(req_msg)
                break
            if case(FTD_Tid_RtnRuleProp):
                # self.api.OnRspError(req_msg)
                break
            if case(FTD_Tid_RtnRuleIndicator):
                # self.api.OnRspUserLogout(req_msg)
                break
            if case():  # default, could also just omit condition or 'if True'
                print('switch case default!')

    def startWebsocketServer(self):

        def login(msg):
            # switch case
            if msg["Tid"] == 8201:
                api.SubscribeMarketData(msg)
                print("行情定閱成功")
            else:
                data = msg["data"]
                try:
                    if data['CustomerID'] == '1001' and data['Password'] == '123456':
                        return True
                    else:
                        return False
                except Exception as e:
                    print(e)
                    return False

        def onmessage(client, server, msg):
            jsonObject = json.loads(msg)
            print("前端傳來了:", jsonObject)
            if jsonObject["data"].get("stgdesc"):
                stgdesc = jsonObject['data']
                data = urllib.parse.quote(stgdesc)
                print(data)

            if jsonObject["data"].get("optype"):
                try:
                    optype = jsonObject['data']["optype"]
                    stgid = jsonObject['data']['stgid']
                    if optype == "end":
                        self.init_run(6, stgid)
                        print("待停止發送成功!!")
                    elif optype == "start":
                        self.init_run(5, stgid)
                        print("待啟動發送成功!!")
                except Exception as e:
                    print(e)
            islogin = login(jsonObject)
            if client in client_list:
                pass
                # 請求switchcase方法
            else:
                if islogin:
                    client_list.append(client)
                    # 登錄成功,推全量數據給用戶
                    print('登錄成功,推全量數據給用戶')
                    print(self.total_dict)
                    for key in self.total_dict:
                        data = self.total_dict[key]
                        server.send_message_to_all(json.dumps(data))
                    return True
                else:
                    pass
                    # 登錄失敗
                # // server.send_message(client, json.dumps({"Tid": 1111, "data": {"msg": "登陸失敗"}}))

        def sendDataToAllClient():
            while True:
                jsonData = self.tid_Queue.get()
                if jsonData == None:
                    sleep(0.1)
                if jsonData["Tid"] == FTD_Tid_RtnRule:  # 策略
                    self.total_dict[jsonData['data']['RuleID']] = jsonData
                elif jsonData['Tid'] == FTD_Tid_RtnRuleStatus:  # 狀態4146
                    self.total_dict[str(jsonData['data']['RuleID']) + '_' + 'status'] = jsonData
                elif jsonData["Tid"] == FTD_Tid_RtnRuleProp:  # 參數4164
                    self.total_dict[str(jsonData['data']['RuleID']) + '_' + jsonData['data']['PropValue']] = jsonData
                elif jsonData["Tid"] == FTD_Tid_RtnRuleIndicator:  # 指標4165
                    self.total_dict[str(jsonData['data']['RuleID']) + '_' + jsonData['data']['IndicatorKey']] = jsonData

                client_list_copy = copy.copy(client_list)
                for client in client_list_copy:
                    sleep(0.1)
                    server.send_message(client, json.dumps(jsonData))

        # 這是服務端,所以需要監聽的是本機的ip,通過ipconfig拿到,
        # 同時在linux上的客戶端也需要監聽這個ip和端口。
        # server = WebsocketServer(9000, host='172.16.78.228')
        # server = WebsocketServer(9000, host='172.19.190.191')
        server = WebsocketServer(9000, host='127.0.0.1')
        server.set_fn_message_received(onmessage)
        t1 = threading.Thread(target=sendDataToAllClient)
        t1.start()
        server.run_forever()
        server.server_close()

    def init_run(self, run_status, stg_id):
        '''
        1運行中  2已結束  3已分析  4未運行  5待運行   6待停止  7錯誤
        '''

        # 數據庫
        user = 'root'
        password = 'XCL29hao'
        ip_address = '111.231.16.33'
        port = 3306
        db_name = 'strategy'
        # 表名
        self.db_approval = 't_paperapproval'
        self.db_run = 't_paperrun'

        # 配置文件
        self.ini_host = '127.0.0.1'
        self.ini_port = 50000
        self.ini_api_mode = 'HFT_Address_IP'
        self.ini_api_host = '127.0.0.1'
        self.ini_api_port = '32201'

        # python exe文件, 保存路徑
        self.python_path = 'D:\\program\\Anaconda3'
        self.target_path = 'D:'

        # 遍歷周期
        self.sleep_period = 10
        # 數據庫連接
        self.engine_str = f'mysql+mysqlconnector://{user}:{password}@{ip_address}:{port}/{db_name}?charset=utf8'
        self.engine = create_engine(self.engine_str)

        try:
            # 進程管理
            process_main = process_manager()

            print('運行一次進程啟動...')
            # 進程管理器
            process_main.get_current_process()

            # 讀取需要啟停的進程
            content = f'select * from {self.db_run} where StgID = {stg_id}'
            sql_df = pd.read_sql_query(content, self.engine)

            if len(sql_df) == 0:
                return 0

            # 類型轉換
            run_status = int(run_status)
            stg_id = str(stg_id)
            path = sql_df['RunPath'].tolist()[0]

            # 啟動進程
            if run_status == run_status_enum.待運行.value:
                # 運行進程
                flag = process_main.start_process(path)

                if flag:
                    # 更新run表
                    try:
                        content = f'update {self.db_run} set RunStatus = \
                        {run_status_enum.運行中.value}, StartTime = \
                        "{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}" \
                        where StgID = {stg_id}'
                        pd.read_sql_query(content, self.engine)
                    except Exception as e:
                        pass
            # 終止進程
            elif run_status == run_status_enum.待停止.value:
                # 終止進程
                flag = process_main.end_process(stg_id)

                if flag:
                    # 更新run表
                    try:
                        content = f'update {self.db_run} set RunStatus = \
                        {run_status_enum.未運行.value}, EndTime = \
                        "{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}" \
                        where StgID = {stg_id}'
                        pd.read_sql_query(content, self.engine)
                    except Exception as e:
                        pass
        except Exception as e:
            print(e)


"""
消耗多進程隊列的func
"""


def main():
    """
    @ERYI.tcy
    """
    # 初始化
    # print('父進程>>', os.getpid())
    # 創建一個實時更新隊列-存儲實時數據
    tid_Queue = multiprocessing.Queue()

    p1 = multiprocessing.Process(target=TestMdApi, args=(tid_Queue,))
    global api
    api = TestMdApi(tid_Queue)
    api.subs()
    p1.start()

    # p1.daemon = True
    p2 = multiprocessing.Process(target=SocketEnv, args=(tid_Queue,))
    p2.start()

    socket_env = SocketEnv(tid_Queue)
    #     # api的業務操作測試 從hft取數據比如請求訂閱行情、收策略回報等
    socket_env.startWebsocketServer()

    i = 1
    while True:
        if i == 0:
            break
    api.Exit()

    print("父進程結束")


if __name__ == '__main__':
    main()

遇到一些問題

'''
websocket服務端
	
	如果部署在服務器上,則監聽的是內網ip(阿里雲:命令是ifconfig, windowserver:命令是ipconfig), 而客戶端連接的是公網ip(服務器外網訪問的ip) 
	如果部署在本地,則監聽的是本機的ip, 客戶端連接的是本機的ip,通過ipconfig查看本機(ipv4地址),不然的話有可能會抱胸:在其上下文,該請求地址無效,主要就是ip+端口錯誤導致的。
	
	
'''


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM