django+websocket
websocket知识点
安装:
pip install websocket-server
注意事项:
当运行了 run_forever 方法后,当前线程就阻塞了。
如果在之后要调用 send_message_to_all方法,利用多线程。
html页面
base.html
{% load staticfiles %}
<!-- <!DOCTYPE html> -->
<html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
{% block head_link %}{% endblock %}
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
<link rel="stylesheet" href="{% static '/bower_components/bootstrap/dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static '/bower_components/font-awesome/css/font-awesome.min.css' %}">
<link rel="stylesheet" href="{% static '/bower_components/Ionicons/css/ionicons.min.css' %}">
<!-- <link rel="stylesheet" href="{% static '/plugins/bootstrap-datatable/bootstrap-table.css' %}"> -->
<link rel="stylesheet" href="{% static '/bootstrap_table/bootstrap-table/dist/bootstrap-table.min.css' %}">
<link rel="stylesheet" href="{% static '/bower_components/datatables.net-bs/css/dataTables.bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static '/plugins/iCheck/all.css' %}">
<link rel="stylesheet" href="{% static '/plugins/bootstrapValidator/bootstrapValidator.min.css' %}">
<link rel="stylesheet" href="{% static '/plugins/bootstrap-dialog/bootstrap-dialog.min.css' %}">
<!-- <link rel="stylesheet" href="{% static '/dist/css/AdminLTE.min.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static '/plugins/timepicker/bootstrap-timepicker.css' %}"> -->
<!-- <link rel="stylesheet" href="{% static '/dist/css/skins/skin-blue.min.css' %}"> -->
<link rel="stylesheet" href="{% static '/public.css' %}">
<link rel="stylesheet" href="{% static '/datetimepicker/bootstrap-datetimepicker.min.css' %}">
<!-- 子模板的样式表应放在父模板的样式表之后,只有这样才可以在子模板中重定义父模板中的某些样式 -->
{% block styles %}{% endblock %}
<style>
.btn-group-xs > .btn, .btn-xs {
padding: 0px 6px;
font-size: 11px;
line-height: 1.5;
border-radius: 3px;
}
.table > tbody > tr > td, .table > tbody > tr > th, .table > tfoot > tr > td, .table > tfoot > tr > th, .table > thead > tr > td, .table > thead > tr > th {
padding: 4px;
line-height: 1.2;
vertical-align: top;
border-top: 1px solid #ddd;
}
.skin-blue .main-header .logo {
background-color: #2b2b2b;
color: #fff;
border-bottom: 0 solid transparent;
}
.skin-blue .wrapper, .skin-blue .main-sidebar, .skin-blue .left-side {
background-color: #141414;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
.main-footer {
background: #000;
padding: 15px;
color: #f39c12;
border-top: 1px solid #000;
}
th {
text-align: center;
}
td {
text-align: center;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
label {
width: 100px;
margin-left: 5%;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
input, select {
width: 160px;
height: 24px;
background: #0F0F0F;
border: 1px solid #3C3C3C;
border-radius: 1px;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
/* 模态框下的input */
.opts {
background-color: white;
width: 200px;
}
form > input {
width: 160px;
height: 24px;
background: #0F0F0F;
border: 1px solid #3C3C3C;
border-radius: 1px;
}
/* input-file放在同一行 */
input[type=file] {
display: inline-block;
/* cursor: pointer; */
border: none;
width: auto;
}
button {
border: none;
}
.btn-query {
width: 87px;
height: 26px;
background: #3C3C3C;
border-radius: 3px;
}
.conf {
border: 0; /*去掉未选中状态边框*/
outline: none; /*去掉选中状态边框*/
background-color: rgba(0, 0, 0, 0); /*透明背景*/
}
.bootstrap-table {
border-style: solid;
border-width: 3px;
border: 1px solid rgba(0, 0, 0, .15);
}
.pagination-info {
display: none
}
.page-list {
display: none
}
.modal-dialog {
width: 450px;
}
.modal-body {
/* background-color: #282923; */
padding: 10px 30px;
height: auto;
}
StgFileShow, ConFileShow {
background-color: #282923;
color: #90918b;
border-width: 0px;
width: 448px;
height: 100%;
}
stg_check {
padding: 10px;
}
.row {
margin-top: 10px;
}
/* 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;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
/* tab头的位置及边框颜色透明 */
.nav-tabs-custom > .nav-tabs {
margin-left: 11px;
border-bottom-color: rgba(0, 0, 0, 0.2);
border-top-right-radius: 3px;
border-top-left-radius: 3px;
}
/* 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;
}
.bootstrap-table .fixed-table-container .table {
width: 100%;
margin-bottom: 0 !important;
color: #939393;
}
.fixed-table-toolbar {
background-color: #141414;
}
.uploadsearch, .packsearch, .papersearch {
width: 87px;
height: 26px;
background: #3C3C3C;
border-radius: 3px;
}
.upstrategy {
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;
}
/* bootstraptable上边框 */
.table-bordered {
border: 1px solid transparent !important;
}
/* bootstraptable左侧边框 */
.table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th {
border: 1px solid #1E1E1E;
background: #1E1E1E;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
.table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td {
border: 1px solid #1E1E1E;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
color: #DCDCDC;
}
#stcriskinfoTable > thead > tr > td, #stcriskinfoTable > tbody > tr > td {
border: 1px solid #f5f5f5;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
color: #939393;
}
tr.activetable, input.activetable {
background-color: white;
width: 100px;
height: 30px;
margin: 1px;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
/*确认页面的table,去掉边框线*/
tr.confirm_table, input.confirm_table {
background-color: white;
width: 100px;
height: 30px;
margin: 1px;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
border-width: 0;
text-align: center;
}
.bootstrap-table .fixed-table-container .table {
width: 100%;
margin-bottom: 0 !important;
color: #939393;
}
.fixed-table-toolbar {
background-color: #141414;
}
/* integrated css */
.tab-content {
margin-left: 10px;
}
.div-query {
float: left;
margin-top: 15px;
margin-left: 10px;
margin-bottom: 15px;
color: #ffffff;
}
/* 新增按钮格式 */
.btn-add {
float: left;
width: 100px;
height: 26px;
/* background: inherit; */
background-color: rgba(255, 204, 153, 1);
border-radius: 5px;
/* box-sizing: border-box;
border-width: 1px;
border-style: solid;
border-color: rgba(121, 121, 121, 1);
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none; */
color: #2B2B2B;
}
/* 查询按钮格式 */
.btn-query {
width: 80px;
height: 26px;
background: #3C3C3C;
border-radius: 5px;
}
/* 按钮通用属性 */
.btn-stc {
border-radius: 5px;
text-align: center;
display: table-cell;
vertical-align: middle;
cursor: pointer;
}
/* 提交按钮 */
.btn-confirm {
width: 100px;
height: 26px;
background: red;
color: #ffffff;
}
/* 取消按钮 */
.btn-cancel {
width: 60px;
height: 26px;
background: #3C3C3C;
color: #ffffff;
}
/* 查询面板label格式 */
.lbl-query {
margin-left: 30px;
float: left;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
color: #cdcdcd;
}
/* 订单查询label面板 */
.order-query {
margin-left: 15px;
float: left;
font-size: 12px;
font-family: NotoSansCJKsc-Regular, sans-serif;
color: #cdcdcd;
}
/* 查询面板输入框格式 */
.input-query {
width: 120px;
}
/*环境选择input框*/
.envir-input-query {
width: 75px;
}
/*清算速度input框*/
.tid-input-query {
width: 70px;
}
/*方向input框*/
.direction-input-query {
width: 45px;
}
/*策略ID input框*/
.id-input-query {
width: 50px;
}
/*债券代码 input 框*/
.instrid-input-query {
width: 60px;
}
/*历史持仓 日期 input 框*/
.date-input-query {
width: 70px;
}
.table-stc {
table-layout: fixed;
word-break: break-all;
word-wrap: break-all;
font-size: 12px;
width: 100%;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
section {
margin: 0px 5px 0px 10px;
}
/* 操作成功样式 */
.operat-success {
margin-top:25px;
margin-left:5px;
font-size: 18px;
text-align: center;
}
/* 输入框校验失败文字展示 */
.input-validation {
color: red;
margin-left: 102px;
}
/* 人工录入错误提示输入框*/
.error_input {
color: red;
margin-left: 112px;
}
/* 策略参数输入框错误提示*/
.input-stgpara {
color: red;
margin-left: 40px;
}
/*滚动条样式*/
div::-webkit-scrollbar {
width: 4px;
display: block;
}
/*滚动条样式*/
div::-webkit-scrollbar-thumb {
border-radius: 20px;
background: #777;
}
/*滚动条样式*/
div::-webkit-scrollbar-button {
display: none;
}
/*滚动条样式*/
div::-webkit-scrollbar-track {
background: #2b2b2b;
}
/* 统一修改页面宽高、背景色和字体 */
body {
background: #000;
height: 100%;
width: 100%;
font-family: NotoSansCJKsc-Regular, sans-serif;
}
/*隐藏浏览器滚动条,但是不影响滑动*/
body::-webkit-scrollbar {
width: 0px;
}
/*风控信息隐藏浏览器滚动条,但是不影响滑动*/
#stcriskinfo_id::-webkit-scrollbar {
width: 0px;
}
/*右侧页码样式*/
.pagination > li > a, .pagination > li > span {
position: relative;
float: left;
padding: 6px 12px;
margin-left: -1px;
line-height: 1.42857143;
color: #ddd;
text-decoration: none;
background-color: rgba(0, 0, 0, .0001);
border: 1px solid rgba(255, 255, 255, .15);
}
</style>
</head>
<body class="hold-transition skin-blue sidebar-mini">
<div class="wrapper">
<!-- Left side column. contains the logo and sidebar -->
<!-- <aside class="main-sidebar"></aside> -->
<!-- Content Wrapper. Contains page content -->
<div class="content-wrapper">
{% block content %}
{% endblock %}
</div>
<!-- /.content-wrapper -->
<!-- Main Footer -->
<footer class="main-footer">
<!-- To the right -->
<div class="pull-right hidden-xs">
</div>
</footer>
</div>
<!-- Jquery -->
<script src="{% static '/bower_components/jquery/dist/jquery.min.js' %}"></script>
<!-- Bootstrap 3.3.7 -->
<script src="{% static '/bower_components/bootstrap/dist/js/bootstrap.min.js' %}"></script>
<!-- DataTables -->
<script src="{% static '/bower_components/datatables.net/js/jquery.dataTables.min.js' %}"></script>
<script src="{% static '/bower_components/datatables.net-bs/js/dataTables.bootstrap.min.js' %}"></script>
<script src="{% static '/plugins/jquery-form/jquery.form.min.js' %}"></script>
<script src="{% static '/plugins/iCheck/icheck.min.js' %}"></script>
<!-- 验证 -->
<script src="{% static '/plugins/bootstrapValidator/bootstrapValidator.min.js' %}"></script>
<script src="{% static '/plugins/jquery-cookie/jquery.cookie.min.js' %}"></script>
<!-- Echart -->
<script src="{% static '/plugins/echart/echarts.min.js' %}"></script>
<script src="{% static '/plugins/bootstrap-dialog/bootstrap-dialog.min.js' %}"></script>
<!-- Theme style -->
<!-- <script src="{% static '/dist/js/adminlte.min.js' %}"></script> -->
<script src="{% static '/public.js' %}"></script>
<script src="{% static '/datetimepicker/bootstrap-datetimepicker.js' %}"></script>
<script src="{% static '/plugins/timepicker/bootstrap-timepicker.js' %}"></script>
<script src="{% static '/datetimepicker/jszip.min.js' %}"></script>
<script>
</script>
<!--bootstrap-->
<!--bootstrap-table-->
<script src="{% static '/bootstrap_table/bootstrap-table/dist/bootstrap-table.min.js' %}"></script>
<!--bootstrap-table-lanuage-->
<!--bootstrap-table-export-->
<script src="{% static '/bootstrap_table/bootstrap-table-export.js' %}"></script>
<!--在客户端保存生成的导出文件-->
<script src="{% static '/bootstrap_table/FileSaver.min.js' %}"></script>
<!--以XLSX(Excel 2007+ XML格式)格式导出表(SheetJS)-->
<script src="{% static '/bootstrap_table/xlsx.core.min.js' %}"></script>
<!--无论期望的格式如何,最后都包含 tableexport.jquery.plugin(不是tableexport)-->
<script src="{% static '/bootstrap_table/tableExport.js' %}"></script>
<!--导入websocket和jquery--->
{#<script src="{% static 'bootstrap_table/jquery-1.12.4.min.js' %}"></script>#}
{# <script src="{% static 'model.js' %}"></script>#}
<script src="{% static 'bootstrap_table/websocket.js' %}"></script>
<script src="{% static 'bootstrap_table/model.js' %}"></script>
<!--导入配置文件-->
<script src="{% static 'config.js' %}"></script>
{% block scripts %}
{% endblock %}
</body>
</html>
paper_monitor
{% extends "base.html" %}
{% load staticfiles %}
<head>
</head>
{% block title %}仿真策略监控{% endblock %}
{% block styles %}
<style>
{#表头样式#}
.thead-light {
width: auto;
background-color: #ddd;
}
.pagination-info {
display: none
}
.page-list {
display: none
}
#sse {
color: #36a9ce;
}
#startstg .modal-body {
background-color: #282923;
padding: 0px;
}
{#无数据时鼠标悬浮的背景颜色#}
.table-hover > tbody > tr:hover {
background-color: #444444;
cursor: pointer;
}
#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: 75px;
}
{#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 {
margin-bottom: 0 !important;
color: #A0A0A0;
}
.alterpara {
background-color: white;
}
</style>
{% endblock %}
{% block content %}
<!--策略修改模态框-->
<div class="modal fade" id="editstg" data-backdrop="static" tabindex="-1" role="dialog"
aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">修改策略参数</h4>
</div>
<div class="modal-body">
<form>
<div style="margin-top:3%;display: none">
<label>参数ID</label>
<input class="opts" type="text" id="paraid">
</div>
<div style="margin-top:3%">
<label>参数名</label>
<input class="opts" type="text" id="paraname" disabled style="background-color: #919191">
</div>
<div style="margin-top:3%">
<label>参数值</label>
<input class="opts" type="text" id="paravalue">
<div id="para_spaninfo" style="color: red;margin-left: 112px"></div>
</div>
<div style="margin-top:3%">
<label>描述</label>
<input class="opts" type="text" id="paradesc">
</div>
<hr>
<div style="height: 20px; display: block">
<div style="padding-left:120px; float: left;">
<span class="btn-cancel btn-stc"
onclick="quit__()">取消</span>
</div>
<div style="padding-left:5px;float: left;">
<span class="btn-confirm btn-stc"
onclick="sub()">提交</span>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!--修改成功模态框-->
<div class="modal fade" data-backdrop="static" id="oper_success">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="title_success">修改参数</h4>
</div>
<div class="modal-body">
<div>
<p id="msg_success" class="operat-success">
策略修改成功!</p>
</div>
</div>
</div>
</div>
</div>
<!--人工录入模态框-->
<div class="modal fade" id="input_" data-backdrop="static" tabindex="-1" role="dialog"
aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">人工录入</h4>
</div>
<div class="modal-body">
<form>
<div style="display: none">
<input id="manual-ruleid">
<input id="manual-rulename">
</div>
<div style="margin-top:3%; display: block">
<label>债券代码*</label>
<input class="opts" id="bondcode" name="bondcode" type="text"
placeholder="债券代码">
<div id="bodecode_spaninfo" class="error_input"></div>
</div>
<div style="margin-top:3%">
<label>方向*</label>
<select class="opts" id="direction" name="direction">
<option selected value="">
</option>
<option value="0">
Bid
</option>
<option value="1">
Ofr
</option>
</select>
<div id="direction_spaninfo" class="error_input"></div>
</div>
<div style="margin-top:3%">
<label>清算速度*</label>
<select class="opts" id="speedliqui" name="speedliqui">
<option value="1">
T+0
</option>
<option selected value="2">
T+1
</option>
</select>
<div id="speedliqui_spaninfo" class="error_input"></div>
</div>
<div style="margin-top:3%; display: block">
<label>成交价(%)*</label>
<input class="opts" id="tranc" name="tranc" type="text"
placeholder="成交价">
<div id="tranc_spaninfo" class="error_input"></div>
</div>
<div style="margin-top:3%; display: block">
<label>净价(元)*</label>
<input class="opts" id="netprice" name="netprice" type="text"
placeholder="净价">
<div id="netprice_spaninfo" class="error_input"></div>
</div>
<div style="margin-top:3%; display: block">
<label>成交量(%)*</label>
<input class="opts" id="vol" name="vol" type="text"
placeholder="成交量">
<div id="vol_spaninfo" class="error_input"></div>
</div>
<hr>
<div style="height: 20px; display: block">
<div style="padding-left:120px; float: left;">
<span class="btn-cancel btn-stc"
onclick="quit__()">取消</span>
</div>
<div style="padding-left:5px;float: left;">
<span class="btn-confirm btn-stc"
onclick="input_sub()">提交</span>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!--人工录入确认模态框-->
<div class="modal fade" id="input_confirm">
</div>
<!--风控信息模态框-->
<div class="modal fade" id="stcriskinfo_id" data-backdrop="static" tabindex="-1" role="dialog"
aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog" style="width: 90%;">
<div class="modal-content " style=" background-color: #f5f5f5">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span></button>
<h4 class="modal-title">风控信息</h4>
</div>
<table id="stcriskinfoTable"
style="table-layout: fixed;word-break:break-all; word-wrap:break-all;">
</table>
</div>
</div>
</div>
<!-- Main content -->
<section class="content" style="background-color: #000000">
<div class="row" id="content" name="{{ customerid }}">
<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 class="table table-bordered table-striped table-hover" id="RuleTable"
style="table-layout: fixed;word-break:break-all; word-wrap:break-all;">
</table>
</div>
</div>
</div>
</div>
<div class="nav-tabs-custom right" style="background-color: #000000">
<ul class="nav nav-tabs">
<li id="stgpara" class="active"><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 active" 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"
style="table-layout: fixed;word-break:break-all; word-wrap:break-all;">
</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"
style="table-layout: fixed;word-break:break-all; word-wrap:break-all;">
</table>
</div>
</div>
</div>
</div>
<div class="nav-tabs-custom buttom" style="background-color: #000000">
<ul class="nav nav-tabs">
<li class="active"><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 active" id="fa-stgmm">
<div class=" mailbox-messages">
<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">
<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>
{% endblock %}
{% block scripts %}
<script type="text/javascript">
//风控信息
//bstable渲染页面(固定写法)
function InitBootstrapTable(my_url, my_table_id, my_columns, my_filename, stgid) {
$('#' + my_table_id).bootstrapTable('destroy').bootstrapTable({
url: my_url,
method: 'get',
columns: my_columns,
{#height: 500,//固定高度,可以固定表头#}
striped: true,
cache: false,
pagination: true,
sortable: true,
theadClasses: "thead-light",
sortOrder: "asc",
queryParams: function (pageRequest) {
pageRequest['ruleid'] = stgid;
return pageRequest;
},
sidePagination: "server",
pageNumber: 1,
pageSize: 20,
pageList: [10, 25, 50, 100],
minimumCountColumns: 2,
clickToSelect: true,
cardView: false,
detailView: false,
buttonsAlign: "right",
})
}
// 风控信息
my_columns = [
{
field: 'risktype',
title: '风控类型'
}, {
field: 'riskindicator',
title: '禁止指标',
width: 200
}, {
field: 'risksource',
title: '指标来源',
}, {
field: 'bondid',
title: '代码',
}, {
field: 'direction',
title: '方向',
}, {
field: 'settletype',
title: '清算速度'
}, {
field: 'price',
title: '报价%'
}, {
field: 'volume',
title: '报量(万元)'
}, {
field: 'orderinserttime',
title: '报单时间'
}
];
//风控信息
function _stcriskinfoTable(id) {
InitBootstrapTable("/trade/stcPriskinfo/", "stcriskinfoTable", my_columns, '0', id);
//移除table-border和tbale-hover
$("#stcriskinfoTable").removeClass("table-bordered");
$("#stcriskinfoTable").removeClass("table-hover");
$("#stcriskinfoTable").addClass("table-hov");
$(".bootstrap-table").children().removeClass("fixed-table-toolbar");
$('#stcriskinfo_id').modal("show");
}
var timer, clickFlag, stgtype, stgrefresh, stgStatus1, stgStatus2 = false;//外部变量,这三个变量是定时器是否存在的标志
//初始化策略列表
$("#RuleTable").bootstrapTable('destroy').bootstrapTable({
uniqueId: "RuleID",
height: 400,//固定高度,可以固定表头
theadClasses: "thead-table",
unique: "RuleID",
sortable: true,
// 策略列表table
columns: [{
field: 'RuleID',
title: '策略ID',
width: 50,
sortOrder: "asc",
}, {
field: 'RuleName',
title: '策略名称',
width: 100
}, {
field: 'RuleRunStatus',
title: '运行状态',
width: 50,
formatter: function (value, row, index) {
if (value === "1") {
return "停止"
} else if (value === "0") {
return "启动"
} else if (value === "01") {
return "启动中"
} else if (value === "11") {
return "停止中"
}
}
}, {
field: 'LOption',
title: '操作',
width: 100,
formatter: function (value, row, index) {
if (row.RuleRunStatus === "0") {
var start = "<span style='width: 50px' class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' disabled> <i class='fa'></i>启动</span> ";
var end = "<span style='width: 50px' onclick=\"endstg('" + row.RuleID + "','" + row.RuleName + "')\" type='button' class='btn btn-danger btn-xs btn-flat btn_operation'> <i class='fa'></i>停止</span>";
} else if (row.RuleRunStatus === "1") {
var start = "<span style='width: 50px' onclick=\"startstg('" + row.RuleID + "','" + row.RuleName + "')\" class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' > <i class='fa'></i>启动</span> ";
var end = "<span style='width: 50px' type='button' class='btn btn-danger btn-xs btn-flat btn_operation' disabled> <i class='fa'></i>停止</span>";
} else {
var start = "<span style='width: 50px' class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' disabled> <i class='fa'></i>启动</span> ";
var end = "<span style='width: 50px' type='button' class='btn btn-danger btn-xs btn-flat btn_operation' disabled> <i class='fa'></i>停止</span>";
}
return start + end + " <span onclick=\"input_('" + row.RuleID + "','" + row.RuleName + "')\" type='button' class='btn btn-primary btn-xs btn-flat btn_operation'> <i class='fa'></i>人工录入</span>";
}
},
{
field: 'LRisk',
title: '风控',
width: 50,
formatter: function (value, row, index) {
var stcriskinfo = "<span onclick=\"_stcriskinfoTable('" + row.RuleID + "')\" class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal'> <i class='fa'></i>风控信息</span> ";
return stcriskinfo
}
}],
onClickRow: function (row, $element, field) {
//当前行的父元素tbody下的所有tr移除样式类
$element.parent().children().removeClass("onclickrow")
$element.addClass("onclickrow")
var ruleid = row.RuleID
var rule_status = row.RuleRunStatus
//点击一行数据,初始化策略参数和策略指标
RulePropIndic(ruleid, rule_status)
//刚开始需要把上次的定时器取消掉,然后再进行定时任务。
if (clickFlag) {
clearInterval(timer)
}
clickFlag = true;
timer = setInterval(function () {
//记录刷新前的滚动条位置
var scrollPositionpara = $("#StgparaTable").bootstrapTable("getScrollPosition");
var scrollPositionind = $("#StgdicTable").bootstrapTable("getScrollPosition");
//先移除所有的数据,然后再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
//数据刷新之后,自动跳转到滚动条的位置。
setTimeout(function () {
$("#StgparaTable").bootstrapTable('scrollTo', scrollPositionpara)
}, 1)
setTimeout(function () {
$("#StgdicTable").bootstrapTable('scrollTo', scrollPositionind)
}, 1)
}, 2000)
//关闭定时器
{#clearInterval(timer)#}
}
})
//启动时的策略参数columns
var columnStart = [{
field: 'RuleID',
title: '策略ID',
visible: false
}, {
field: 'PropKey',
title: '参数名'
}, {
field: 'PropValue',
title: '参数值'
}, {
field: 'Description',
title: '描述',
}, {
field: 'PropValueItems',
title: '策略属性值选项',
visible: false
}, {
field: 'PropType',
title: '策略属性值类型',
visible: false,
formatter: function (value, row, index) {
var val = unescape(value.replace(/\\u/g, "%u"));
return val
}
}, {
field: 'POption',
title: '操作',
formatter: function (value, row, index) {
return "<span onclick=\"modify('" + row.RuleID + "'" + "," + "'" + row.PropKey + "'" +
"," + "'" + row.PropValue + "'" + "," + "'" + row.Description + "'" + "," + "'" + row.PropValueItems
+ "')\" class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' data-target='#startstg'> <i class='fa'></i>编辑</span> "
}
}]
//停止时的策略参数columns
var columnEnd = [{
field: 'RuleID',
title: '策略ID',
visible: false
}, {
field: 'PropKey',
title: '参数名'
}, {
field: 'PropValue',
title: '参数值'
}, {
field: 'Description',
title: '描述',
}, {
field: 'PropValueItems',
title: '策略属性值选项',
visible: false
}, {
field: 'PropType',
title: '策略属性值类型',
visible: false,
formatter: function (value, row, index) {
var val = unescape(value.replace(/\\u/g, "%u"));
return val
}
}, {
field: 'POption',
title: '操作',
formatter: function (value, row, index) {
return "<span class='btn btn-success btn-xs btn-flat btn_operation' data-toggle='modal' data-target='#startstg' disabled> <i class='fa'></i>编辑</span> "
}
}]
//初始化策略指标
$("#StgdicTable").bootstrapTable('destroy').bootstrapTable({
uniqueId: "RuleID",
height: 400,//固定高度,可以固定表头
theadClasses: "thead-table",
// 策略列表table
columns: [{
field: 'RuleID',
title: '策略ID',
visible: false
}, {
field: 'IndicatorKey',
title: '指标名'
}, {
field: 'IndicatorValue',
title: '指标值'
}, {
field: 'Description',
title: '指标描述',
}]
})
//初始化做市策略
$("#StgMMTable").bootstrapTable('destroy').bootstrapTable({
uniqueId: "RuleID",
// 做市策略table
height: 500,//固定高度,可以固定表头
theadClasses: "thead-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",
height: 500,//固定高度,可以固定表头
theadClasses: "thead-table",
// 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: "close",
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 = [];
var ws = $.trade("ws://" + PaperIP + ":" + PaperPORT + "/", {//
OnFrontConnected: function () {
$("#console").append("<br/>TD连接成功");
sendMsg_login()
},
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);#}
$('#RuleTable').bootstrapTable('insertRow', {
index: 0,
row: 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() {
ws.ReqUserLogin("1001", "123456");
}
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) {
//将wrapper的style属性移除,因为该类有一个样式 height:auto,会形成两个滚动条。
$(".wrapper").css("height", "1200px")
initypestg()
if (n == 1) {
$('#fa-stgmm').addClass('active');
$('#fa-stgcta').removeClass('active');
clearInterval(stgtype)
stgtype = setInterval(function () {
//记录刷新前的滚动条位置
var scrollPositionMM = $("#StgMMTable").bootstrapTable("getScrollPosition");
var scrollPositionCta = $("#StgCtaTable").bootstrapTable("getScrollPosition");
//先移除所有的数据,然后再append
$('#StgMMTable').bootstrapTable('removeAll');
$('#StgCtaTable').bootstrapTable('removeAll');
//bootstraptable渲染不同类型的策略
for (let key in rules) {
if (rules[key]["RuleType"] === "1") {
$('#StgMMTable').bootstrapTable('append', rules[key]);
} else if (rules[key]["RuleType"] === "2") {
$('#StgCtaTable').bootstrapTable('append', rules[key]);
}
}
//数据刷新之后,自动跳转到滚动条的位置。
setTimeout(function () {
$("#StgMMTable").bootstrapTable('scrollTo', scrollPositionMM)
}, 1)
setTimeout(function () {
$("#StgCtaTable").bootstrapTable('scrollTo', scrollPositionCta)
}, 1)
}, 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]["RuleType"] === "1") {
$('#StgMMTable').bootstrapTable('append', rules[key]);
} else if (rules[key]["RuleType"] === "2") {
$('#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]["RuleType"] === "1") {
$('#StgMMTable').bootstrapTable('append', rules[key]);
} else if (rules[key]["RuleType"] === "2") {
$('#StgCtaTable').bootstrapTable('append', rules[key]);
}
}
}
//策略参数和策略指标的初始化(点击某一行策略时触发)
function RulePropIndic(ruleid, rulestatus) {
if (rulestatus === "0") {
//初始化策略参数
$("#StgparaTable").bootstrapTable('destroy').bootstrapTable({
// 策略列表table
height: 400,//固定高度,可以固定表头
theadClasses: "thead-table",
columns: columnStart
})
} else if (rulestatus === "1") {
//初始化策略参数
$("#StgparaTable").bootstrapTable('destroy').bootstrapTable({
// 策略列表table
height: 400,//固定高度,可以固定表头
theadClasses: "thead-table",
columns: columnEnd
})
}
//先移除所有的数据,然后再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));
};
var start_setTime1, start_setTime2 = null
//启动策略
function startstg(id, rulename) {
var customerid = $("#content").attr("name")
$("#RuleTable").bootstrapTable('updateByUniqueId', {
id: id,
row: {
RuleRunStatus: "01"
}
})
ws.ReqOptionStg("start", id, customerid, "1", rulename);//flag为1时表示启动或者停止策略
//启动后清空策略参数展示
$('#StgparaTable').bootstrapTable('removeAll');
$('#StgdicTable').bootstrapTable('removeAll');
//清除已经存在的SetTimeout对象
if (start_setTime1) {
clearTimeout(start_setTime1)
start_setTime1 = null
} else if (start_setTime2) {
clearTimeout(start_setTime2)
start_setTime2 = null
}
//2s后轮询,判断状态是否异常
start_setTime1 = setTimeout(function () {
var stgobj = $('#RuleTable').bootstrapTable('getRowByUniqueId', id)
if (stgobj.RuleRunStatus === "01") {
stgStatus1 = setInterval(function () {
console.log("轮询")
for (let key in rules) {
if (key === id && rules[key]["RuleRunStatus"] === "0") {
$('#RuleTable').bootstrapTable('updateRow', {
index: id,
row: rules[key]
})
}
}
}, 2000)
} else {
clearInterval(stgStatus1)
}
}, 2000)
//5s后清除轮询
start_setTime2 = setTimeout(function () {
var stgobj = $('#RuleTable').bootstrapTable('getRowByUniqueId', id)
if (stgobj.RuleRunStatus === "01") {
clearInterval(stgStatus1)
$("#title_success").text("启动")
$("#msg_success").text(rulename + "启动失败,请检查策略!")
$("#oper_success").modal("show");
setTimeout(function () {
$("#oper_success").modal("hide");
window.location.reload()
}, 2000)
}
}, 5000)
}
var end_setTime1, end_setTime2 = null;
//停止策略
function endstg(id, rulename) {
var customerid = $("#content").attr("name")
$("#RuleTable").bootstrapTable('updateByUniqueId', {
id: id,
row: {
RuleRunStatus: "11"
}
})
ws.ReqOptionStg("end", id, customerid, "1", rulename);
//停止后清空策略参数展示
$('#StgparaTable').bootstrapTable('removeAll');
$('#StgdicTable').bootstrapTable('removeAll');
//清除已经存在的SetTimeout对象
if (end_setTime1) {
clearTimeout(end_setTime1)
end_setTime1 = null
} else if (end_setTime2) {
clearTimeout(end_setTime2)
end_setTime2 = null
}
//2s后轮询,判断状态是否异常
end_setTime1 = setTimeout(function () {
var stgobj = $('#RuleTable').bootstrapTable('getRowByUniqueId', id)
if (stgobj.RuleRunStatus === "11") {
stgStatus2 = setInterval(function () {
console.log("轮询")
for (let key in rules) {
if (key === id && rules[key]["RuleRunStatus"] === "1") {
$('#RuleTable').bootstrapTable('updateRow', {
index: id,
row: rules[key]
})
}
}
}, 2000)
} else {
clearInterval(stgStatus2)
}
}, 2000)
//5s后清除轮询
end_setTime2 = setTimeout(function () {
var stgobj = $('#RuleTable').bootstrapTable('getRowByUniqueId', id)
if (stgobj.RuleRunStatus === "11") {
clearInterval(stgStatus2)
$("#title_success").text("停止")
$("#msg_success").text(rulename + "停止失败,请检查策略!")
$("#oper_success").modal("show");
setTimeout(function () {
$("#oper_success").modal("hide");
window.location.reload()
}, 2000)
}
}, 5000)
}
//编辑策略参数
function modify(id, key, value, desc, valueitems) {
$("#para_spaninfo").text("")
$("#paraid").val(id)
$("#paraname").val(key)
$("#paravalue").val(value)
$("#paradesc").val(desc)
$("#paravalue").attr("name", valueitems); //为参数值设置name属性,值为valueitems,用于传到websockets传给userapi
{#$("#paraname").attr("name", proptype); //为参数名设置name属性,值为proptype,用于传到websockets传给userapi#}
$("#editstg").modal("show")
{#return data#}
}
//隐藏编辑策略模态框
function quit__() {
$("#editstg").modal("hide");
$("#input_").modal('hide')
}
//提交修改后的数据(策略参数)
function sub() {
var customerid = $("#content").attr("name")
var paraid = $("#paraid").val();
var paraname = $("#paraname").val();
var paravalue = $("#paravalue").val();
var paradesc = $("#paradesc").val();
if (paravalue === "") {
$("#para_spaninfo").text("策略参数不能为空!")
return;
}
ws.ReqStgPara(customerid, paraid, paraname, paravalue, paradesc, "0",)
$.ajax({
type: "post",
url: "/trade/alterpara/",
data:
{
"ruleid": paraid,
"paraname": paraname,
"paravalue": paravalue,
"paradesc": paradesc,
},
success: function (data) {
$("#editstg").modal("hide");
if (data === "y") {
$("#oper_success").modal("show");
setTimeout(function () {
$("#oper_success").modal("hide");
}, 2000)
} else {
$("#msg_success").text(data)
$("#oper_success").modal("show");
setTimeout(function () {
$("#oper_success").modal("hide");
}, 2000)
}
}
})
}
//人工录入的模态框
function input_(ruleid, rulename) {
$("input").val("");
$("select").val("");
$("#speedliqui").val("2");
$("#vol_spaninfo").text("")
$("#netprice_spaninfo").text("")
$("#tranc_spaninfo").text("")
$("#speedliqui_spaninfo").text("")
$("#direction_spaninfo").text("")
$("#bodecode_spaninfo").text("")
//债券代码和方向的初始值
for (let key in ruleProps) {
if (key === ruleid + "indic_symbol") {
$("#bondcode").val(ruleProps[key]["IndicatorValue"])
} else if (key === ruleid + "indic_direction") {
if (ruleProps[key]["IndicatorValue"] === "1") {
$("#direction").val("0")
} else if (ruleProps[key]["IndicatorValue"] === "0") {
$("#direction").val("1")
}
}
}
$("#manual-ruleid").val(ruleid)
{#$("#manual-rulename").val(encodeURI(rulename))#}
$("#manual-rulename").val(rulename)
$("#input_").modal("show")
}
//4位小数校验,不足补0
function checkfour(inp, typ) {
var inp_split = inp.split(".");
if (inp_split.length > 2) {
if (typ === "tranc") {
$("#tranc_spaninfo").text("成交价输入格式有误!")
} else if (typ === "netprice") {
$("#netprice_spaninfo").text("净价输入格式有误!")
}
return "n"
} else if (inp_split.length === 1) {
return inp + ".0000"
} else {
var l = inp_split[1].length
var a = 4 - l;
if (a > 0 && a !== 0) {
for (let i = 0; i < a; i++) {
inp += "0"
}
}
return inp
}
}
//人工录入模态框的提交
function input_sub() {
//所有的提示信息都清空
$("#vol_spaninfo").text("")
$("#netprice_spaninfo").text("")
$("#tranc_spaninfo").text("")
$("#speedliqui_spaninfo").text("")
$("#direction_spaninfo").text("")
$("#bodecode_spaninfo").text("")
const bondcode = $("#bondcode").val();
const direction = $("#direction").val();
const speedliqui = $("#speedliqui").val();
var tranc = $("#tranc").val();
var netprice = $("#netprice").val();
const vol = $("#vol").val()
//点击提交首先检验
if (bondcode === "") {
$("#bodecode_spaninfo").text("债券代码不能为空!")
return;
} else if (direction === "") {
$("#direction_spaninfo").text("方向不能为空!")
return;
} else if (speedliqui === "") {
$("#speedliqui_spaninfo").text("清算速度不能为空!")
return;
} else if (tranc === "") {
$("#tranc_spaninfo").text("成交价不能为空!")
return;
} else if (vol === "") {
$("#vol_spaninfo").text("成交量不能为空!")
return;
} else if (parseFloat(tranc).toString() === "NaN") {
$("#tranc_spaninfo").text("成交价必须为数字!")
return;
} else if (parseFloat(netprice).toString() === "NaN") {
$("#netprice_spaninfo").text("净价必须为数字!")
return;
} else if (/^\d+$/.test(vol) === false || parseInt(vol) > 100000 || parseInt(vol) < 1) {
$("#vol_spaninfo").text("成交量必须为1-100000的整数!")
return;
}
//id,name传给后台
var ruleid = $("#manual-ruleid").val()
var rulename = $("#manual-rulename").val()
var flag1 = checkfour(tranc, "tranc")
var flag2 = checkfour(netprice, "netprice")
if (flag1 === "n" || flag2 === "n") {
return;
} else {
tranc = flag1;
netprice = flag2;
}
const direction_val = $("#direction").find("option[value=" + direction + "]").text().replace(/\s+/g, "");
const speedliqui_val = $("#speedliqui").find("option[value=" + speedliqui + "]").text().replace(/\s+/g, "");
var input_confirm = '<div class="modal-dialog">\n' +
' <div class="modal-content">\n' +
' <div class="modal-header">\n' +
' <h4 class="modal-title">人工录入确认</h4>\n' +
' </div>\n' +
' <div class="modal-body">\n' +
' <form>\n' + '<div style="display: none">\n' +
' <input id="cmanual-ruleid" value="' + ruleid + '">\n' +
' <input id="cmanual-rulename" value="' + rulename + '">\n' +
' </div>' +
' <div style="margin-top:3%; display: block">\n' +
' <label>债券代码*</label>\n' +
' <input class="opts conf" id="cbondcode" name="bondcode" type="text"\n' +
' value="' + bondcode + '">\n' +
' </div>\n' +
' <div style="margin-top:3%">\n' +
' <label>方向*</label>\n' +
' <input class="opts conf" id="cdirection" name="' + direction + '" type="text"\n' +
' value="' + direction_val + '">\n' +
' </div>\n' +
' <div style="margin-top:3%">\n' +
' <label>清算速度*</label>\n' +
' <input class="opts conf" id="cspeedliqui" name="' + speedliqui + '" type="text" value="' + speedliqui_val + '">\n' +
' </div>\n' +
' <div style="margin-top:3%; display: block">\n' +
' <label>成交价(%)*</label>\n' +
' <input class="opts conf" id="ctranc" name="tranc" type="text"\n' +
' value="' + tranc + '">\n' +
' </div>\n' +
' <div style="margin-top:3%; display: block">\n' +
' <label>净价(元)*</label>\n' +
' <input class="opts conf" id="cnetprice" name="netprice" type="text"\n' +
' value="' + netprice + '">\n' +
' </div>\n' +
' <div style="margin-top:3%; display: block">\n' +
' <label>成交量(%)*</label>\n' +
' <input class="opts conf" id="cvol" name="vol" type="text"\n' +
' value="' + vol + '">\n' +
' </div>\n' +
' <hr>\n' +
' <div style="height: 20px; display: block">\n' +
' <div style="padding-left:120px; float: left;">\n' +
' <span class="btn-cancel btn-stc"\n' +
' onclick="quit__inp()">取消</span>\n' +
' </div>\n' +
' <div style="padding-left:5px;float: left;">\n' +
' <span class="btn-confirm btn-stc"\n' +
' onclick="hand_sub()">提交</span>\n' +
' </div>\n' +
' </div>\n' +
' </form>\n' +
' </div>\n' +
' </div>\n' +
' </div>'
$("#input_confirm").append(input_confirm)
$("#input_confirm").modal("show")
}
//人工录入后台发送请求
function hand_sub() {
const bondcode = $("#cbondcode").val();
const direction_val = $("#cdirection").attr('name');
const speedliqui_val = $("#cspeedliqui").attr('name');
var tranc = $("#ctranc").val();
var netprice = $("#cnetprice").val();
const vol = $("#cvol").val()
var ruleid = $("#cmanual-ruleid").val()
var rulename = $("#cmanual-rulename").val()
var customerid = $("#content").attr("name")
$.ajax({
type: "post",
url: "/trade/hand_input/",
data:
{
"bondcode": bondcode,
"direction_val": direction_val,
"speedliqui_val": speedliqui_val,
"tranc": tranc,
"netprice": netprice,
"vol": vol,
"ruleid": ruleid,
"rulename": rulename,
"customerid": customerid
},
success: function (data) {
$("#input_").modal("hide");
$("#input_confirm").modal("hide");
$("#title_success").text("人工录入")
if (data === "y") {
$("#msg_success").text("录入成功!")
$("#oper_success").modal("show");
setTimeout(function () {
$("#oper_success").modal("hide");
}, 2000)
} else {
$("#msg_success").text(data)
$("#oper_success").modal("show");
setTimeout(function () {
$("#oper_success").modal("hide");
}, 2000)
}
}
})
}
//隐藏确认框
function quit__inp() {
$("#input_confirm").modal("hide")
$("#input_confirm").html("")
}
$(function () {
initypestg()
$('#fa-stgmm').addClass('active');
$('#fa-stgcta').removeClass('active');
clearInterval(stgtype);
stgtype = setInterval(function () {
//记录刷新前的滚动条位置
var scrollPositionMM = $("#StgMMTable").bootstrapTable("getScrollPosition");
var scrollPositionCta = $("#StgCtaTable").bootstrapTable("getScrollPosition");
//先移除所有的数据,然后再append
$('#StgMMTable').bootstrapTable('removeAll');
$('#StgCtaTable').bootstrapTable('removeAll');
//bootstraptable渲染不同类型的策略
for (let key in rules) {
if (rules[key]["RuleType"] === "1") {
$('#StgMMTable').bootstrapTable('append', rules[key]);
} else if (rules[key]["RuleType"] === "2") {
$('#StgCtaTable').bootstrapTable('append', rules[key]);
}
}
//数据刷新之后,自动跳转到滚动条的位置。
setTimeout(function () {
$("#StgMMTable").bootstrapTable('scrollTo', scrollPositionMM)
}, 1)
setTimeout(function () {
$("#StgCtaTable").bootstrapTable('scrollTo', scrollPositionCta)
}, 1)
}, 2000)
/**
clearInterval(stgrefresh);
stgrefresh = setInterval(function () {
$('#RuleTable').bootstrapTable('removeAll');
for (let key in rules) {
$("#RuleTable").bootstrapTable("append", rules[key]);
}
}, 2000);
**/
})
</script>
<style>
{#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);
}
.tab-content {
height: 380px;
}
.stginfo {
height: 400px;
}
.onclickrow {
background-color: #444444;
}
.opts {
background-color: white;
margin-left: 10px;
}
.table-hov > tbody > tr:hover {
background-color: #eee;
cursor: pointer;
}
</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"
};
_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).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, customerid, flag, rulename) {
console.log("策略名称", encodeURI(rulename))
this.ws.send(FTD_TID_ReqOptionStg, {
"optype": optype,
"stgid": ruleid,
"customerid": customerid,
"flag": flag,
"rulename": encodeURI(rulename)
})
};
//策略参数发送到websocket
tradeWS.ReqStgPara = function (customerid, ruleid, key, value, desc) {
console.log(encodeURI(desc));
this.ws.send(FTD_TID_ReqStgPara, {
"customerid": customerid,
"stgid": ruleid,
"stgname": encodeURI(key),
"stgvalue": encodeURI(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);
后台服务
# encoding: UTF-8
from time import sleep
from hft_api import HftApi
import time
import os
import re
import json
import threading
import copy
import multiprocessing
from websocket_server import WebsocketServer
import shutil
global signal
import datetime
from enum import Enum
from process_manager import process_manager
import pandas as pd
from sqlalchemy import create_engine
import urllib.parse
import socket
import WebsocketLog
import configparser
## 配置信息
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
config_init = configparser.ConfigParser()
config_init.read(os.path.join(BASE_DIR, 'config.ini'))
# 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 HFTMdApi(HftApi):
def __init__(self, Queue1):
"""Constructor"""
super(HFTMdApi, 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)
# ----------------------------------------------------------------------
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)
def onRtnRuleIndicator(self, data):
"""策略指标回报"""
package = {'Tid': 0x00001045, 'data': data}
self.save_as_json(package)
# save ruleid+key
def onRtnRuleProp(self, data):
"""策略属性变化回报"""
package = {'Tid': 0x00001044, 'data': data}
self.save_as_json(package)
# 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)
"""
主动API接口封装
"""
# 在C++环境中创建MdApi对象,传入参数是希望用来保存.con文件的地址
# @param pszFlowPath 存贮订阅信息文件的目录,默认为当前目录
# @return 创建出的UserApi
# modify for udp marketdata
def CreateFtdcMdApi(self):
self.createUserApi()
# 删除接口对象本身
# @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'] = config_init.get("LoginReq", 'CustomerID')
loginReq['Password'] = config_init.get("LoginReq", 'Password')
loginReq['MacAddress'] = config_init.get("LoginReq", 'MacAddress')
loginReq['SubInstrumentMethod'] = config_init.get("LoginReq", 'SubInstrumentMethod')
loginReq['IsRule'] = int(config_init.get("LoginReq", 'IsRule'))
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()
tkernel = config_init.get("TKernel", "ip")
self.RegisterFront(tkernel)
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):
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__()
# 数据库
# sql_user = 'root'
# sql_password = 'XCL29hao'
# sql_host = '111.231.16.33'
# sql_port = 3306
# sql_database = 'strategy'
sql_user = config_init.get("MySql", "user")
sql_password = config_init.get("MySql", "password")
sql_host = config_init.get("MySql", "host")
sql_port = config_init.get("MySql", "port")
sql_database = config_init.get("MySql", 'name')
# 数据库连接
self.engine_str = f"mysql+mysqlconnector://{sql_user}:{sql_password}@{sql_host}:{sql_port}/{sql_database}?charset=utf8"
self.engine = create_engine(self.engine_str)
self.db_run = config_init.get("DBRun", "db_run")
ipstr = os.popen(""" ifconfig | sed -n "2p" """).readlines()[0]
self.ip_address = re.findall("((?<![\.\d])(?:\d{1,3}\.){3}\d{1,3}(?![\.\d]))", ipstr)[0]
# self.ip_address = socket.gethostbyname(socket.getfqdn(socket.gethostname()))
# self.ip_address = "172.16.78.228"
self.total_dict = {} # 全量数据
self.work_path = os.getcwd()
# 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("行情定阅成功")
elif msg["Tid"] == 8450:
print("点击了修改策略参数按钮")
try:
customerid = msg["data"]["customerid"]
print("策略参数customerid", customerid)
ruleReq2 = {} # 创建一个空字典
ruleReq2['RuleID'] = int(msg["data"]["stgid"]) # 策略代码
ruleReq2['PropKey'] = urllib.parse.unquote(msg["data"]["stgname"]).encode('gbk') # 策略属性键
ruleReq2['PropValue'] = urllib.parse.unquote(msg["data"]["stgvalue"]).encode('gbk') # 策略属性值
ruleReq2['PropType'] = "1" # 策略属性值类型
ruleReq2['PropValueItems'] = "1" # 策略属性值选项
ruleReq2['Description'] = urllib.parse.unquote(msg["data"]["stgdesc"]).encode('gbk') # 描述
print("修改参数:", ruleReq2)
api.ReqUpdateRuleProp(ruleReq2)
WebsocketLog.websocketlog(customerid, "modifypara", "", ruleReq2['RuleID'])
print("策略参数修改完毕")
except Exception as e:
print("策略参数修改失败", e)
elif msg["Tid"] == 4097:
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
elif msg["Tid"] == 8449:
try:
optype = msg['data']["optype"]
stgid = msg['data']['stgid']
customerid = msg['data']['customerid']
flag = msg['data']['flag']
rulename = urllib.parse.unquote(msg['data']['rulename'])
if optype == "end":
# 一键撤单的日志
if flag == "2":
WebsocketLog.websocketlog(customerid, "korder", "", "")
else:
self.init_run(6, stgid)
# 策略停止,写入日志
if flag == "1":
WebsocketLog.websocketlog(customerid, "end", rulename, stgid)
elif optype == "start":
self.init_run(5, stgid)
WebsocketLog.websocketlog(customerid, "start", rulename, stgid)
# print("待启动发送成功!!")
except Exception as e:
print(e)
def onmessage(client, server, msg):
try:
jsonObject = json.loads(msg)
print("前端传来了:", jsonObject)
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": "登陆失败"}}))
except Exception as e:
print("123", e)
def sendDataToAllClient():
while True:
jsonData = self.tid_Queue.get()
print("启动后收到:", jsonData)
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']['PropKey']] = jsonData
elif jsonData["Tid"] == FTD_Tid_RtnRuleIndicator: # 指标4165
self.total_dict[str(jsonData['data']['RuleID']) + '_' + jsonData['data']['IndicatorKey']] = jsonData
# 给所有的客户端都发数据
server.send_message_to_all(json.dumps(jsonData))
### linux服务器上的ip地址
server = WebsocketServer(int(config_init.get("WebSocketAddr", "port")),
host=config_init.get("WebSocketAddr", "ip"))
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错误
'''
process_main = process_manager()
print('运行一次进程启动...')
# 进程管理器
process_main.get_current_process()
try:
# #读取需要启停的进程
content = f'select a.RunStatus,a.EnvID,a.StgID,a.RunPath from {self.db_run} a, t_environment b \
where a.EnvID = b.EnvID and b.IP = "{self.ip_address}" and a.StgID = {stg_id} '
sql_df = pd.read_sql_query(content, self.engine)
if len(sql_df) == 0:
return 0
path = sql_df['RunPath'].tolist()[0]
env_id = sql_df['EnvID'].tolist()[0]
# path="/usr/local/process/50002/50002"
# env_id=8
# 类型转换
run_status = int(run_status)
stg_id = str(stg_id)
# # 复制exe到指定路径
# strategy_path = ('/').join(path.split('/')[:-1])
# file_path = strategy_path + '/strategy.ini'
# target_path = self.work_path + '/strategy.ini'
# shutil.copyfile(file_path, target_path)
# 启动进程
if run_status == 5: # run_status_enum.待运行.value:
# 运行进程
flag = process_main.start_process(path)
if flag:
# 更新run表
try:
content = f'update {self.db_run} set RunStatus = "1"\
, StartTime = \
"{datetime.datetime.today().strftime("%Y-%m-%d %H:%M:%S")}" \
, EndTime = "" \
where StgID = {stg_id} and EnvId = {env_id}'
# print("启动进程", content)
pd.read_sql_query(content, self.engine, chunksize=1)
print("修改数据库状态成功!")
except Exception as e:
print("启动进程出错", e)
# 终止进程
# 点击停止后,向queue中put数据,给useapi传数据
elif run_status == 6: # run_status_enum.待停止.value:
try:
package = {"RuleID": int(stg_id), "RuleOperType": "1"}
api.ReqUpdateRuleStatus(package)
except Exception as e:
print("终止进程出错", e)
pass
except Exception as e:
print("init_run 出错", e)
"""
消耗多进程队列的func
"""
def main():
"""
@ERYI.tcy
"""
try:
# 初始化
# print('父进程>>', os.getpid())
# 创建一个实时更新队列-存储实时数据
tid_Queue = multiprocessing.Queue()
p1 = multiprocessing.Process(target=HFTMdApi, args=(tid_Queue,))
global api
api = HFTMdApi(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("父进程结束")
except Exception as e:
print(e)
if __name__ == '__main__':
main()