价差交易策略
策略说明
价差交易,又叫套利交易,是一种通过相关的期货合约或者期现品种等商品、金融产品及它们的衍生品之间的价差波动来获取利润的交易方法。
这里价差交易主要指利用期货合约价格之间不合理关系进行套利交易(Spread Trading)。
在页面上填入 A、B 两个合约,期望的买开卖平和卖开买平的价差、手数。
单击开始按钮后,当前价格处显示买入价差和卖出价差。
价差交易策略分为两个 Task,第一个 TaskQuote 用来显示两个合约的价差,第二个 TaskSpread 用来实现下单交易的策略。交易买卖策略如下:
- 合约A买1价 - 合约B卖1价 <= 买开价差,买开合约A,卖开合约B
- 合约A卖1价 - 合约B买1价 >= 卖平价差,卖平合约A,买平合约B
- 合约A卖1价 - 合约B买1价 >= 卖开价差,卖开合约A,买开合约B
- 合约A买1价 - 合约B卖1价 <= 买平价差,买平合约A,卖平合约B
点击停止按钮时,可以暂停任务,并且撤掉已经发出且为成交的挂单。
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>天勤-交易</title>
<!-- base ui && js -->
<link href="assets/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="assets/highlight/styles/atom-one-light.css">
<link rel="stylesheet" href="tq.css">
</head>
<body>
<div class="container-fluid main-container">
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
价差策略
<span class="pull-right STATE">
<span class="label label-danger">停止</span>
</span>
</h3>
</div>
<ul class="list-group inputs" style="font-size:12px;">
<li class="list-group-item tq-container">
<div class="row">
<div class="col col-xs-3">
<span class="pull-right">合约代码A</span>
</div>
<div class="col col-xs-9">
<input type="text" class="form-control input-sm" placeholder="合约代码A" value="SHFE.rb1805" id="symbolA">
</div>
</div>
<div class="row">
<div class="col col-xs-3">
<span class="pull-right">合约代码B</span>
</div>
<div class="col col-xs-9">
<input type="text" class="form-control input-sm" placeholder="合约代码B" value="SHFE.rb1810" id="symbolB">
</div>
</div>
<div class="row">
<div class="col col-xs-3">
<span class="pull-right">卖平</span>
</div>
<div class="col col-xs-3">
<input type="number" class="form-control input-sm" placeholder="卖平" value="160" id="price_sell_close">
</div>
<div class="col col-xs-3">
<span class="pull-right">卖开</span>
</div>
<div class="col col-xs-3">
<input type="number" class="form-control input-sm" placeholder="卖开" value="133" id="price_sell_open">
</div>
</div>
<div class="row">
<div class="col col-xs-3">
<span class="pull-right">买开</span>
</div>
<div class="col col-xs-3">
<input type="number" class="form-control input-sm" placeholder="买开" value="154" id="price_buy_open">
</div>
<div class="col col-xs-3">
<span class="pull-right">买平</span>
</div>
<div class="col col-xs-3">
<input type="number" class="form-control input-sm" placeholder="买平" value="111" id="price_buy_close">
</div>
</div>
<div class="row">
<div class="col col-xs-3">
<span class="pull-right">手数</span>
</div>
<div class="col col-xs-9">
<input type="number" class="form-control input-sm" placeholder="手数" value="3" id="volume">
</div>
</div>
<div class="row">
<div class="col col-xs-3">
<span class="pull-right">当前价格</span>
</div>
<div class="col col-xs-9">
<span>买入价差: </span>
<span id="spread_ask"></span>
<span>;</span>
<span>卖出价差: </span>
<span id="spread_bid"></span>
<span>;</span>
</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col col-xs-12">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group btn-group-xs">
<button type="button" class="btn btn-default START" id="START">
<span class="glyphicon glyphicon-play"></span> 开始
</button>
<button type="button" class="btn btn-default STOP" id="STOP">
<span class="glyphicon glyphicon-stop"></span> 停止
</button>
</div>
</div>
</div>
</div>
</li>
</ul>
</div>
<div class="panel panel-default">
<div class="panel-heading" role="tab" id="headingOne">
<h4 class="panel-title">
<a role="button" data-toggle="collapse" href="#collapse" aria-expanded="true" aria-controls="collapse">
说明:本策略为一组合套利策略. 指定两个合约构成组合, 当组合价差低于指定值时, 买入; 当组合价差高于指定值时, 卖出.
<span id='collapse_arrow' class="glyphicon glyphicon-menu-down pull-right"></span>
</a>
</h4>
</div>
<div id="collapse" class="panel-collapse collapse" role="tabpanel" aria-labelledby="headingOne">
<ul class="list-group" style="font-size:12px;">
<li class="list-group-item">
<div class="row">
<div class="col col-xs-12">
<b>详细说明:</b>
</div>
</div>
<div class="row">
<div class="col col-xs-3">
<p class="pull-right">
下单:
</p>
</div>
<div class="col col-xs-9">
<p>设置两个价格关联的合约,这里记为A,B。</p>
<p>设置买开、卖平的期望价差点,以及卖开、买平的期望价差点。</p>
<p>监控价格到达指定价差位置,立即采用对手加价追单策略下单。</p>
</div>
</div>
<div class="row">
<div class="col col-xs-3">
<span class="pull-right">
挂单
</span>
</div>
<div class="col col-xs-9">
<p>挂单后若不成交、或部分成交,则立即撤单,以剩余手数继续下单。</p>
</div>
</div>
</li>
<li class="list-group-item">
<div class="row">
<div class="col col-xs-12">
<b>源代码:</b>
</div>
</div>
<div class="row">
<div class="col col-xs-12" id="code_container">
<pre><code class="javascript"></code></pre>
</div>
</div>
</li>
</ul>
</div>
</div>
</div>
<script type="text/javascript" src="assets/jquery.min.js"></script>
<script type="text/javascript" src="assets/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="assets/highlight/highlight.pack.js"></script>
<script type="text/javascript" src="assets/noty.js"></script>
<!-- del:start -->
<script type="text/javascript" src="libs/modules/utils.js"></script>
<script type="text/javascript" src="libs/modules/color.js"></script>
<script type="text/javascript" src="libs/modules/event.js"></script>
<script type="text/javascript" src="libs/modules/datamanager.js"></script>
<script type="text/javascript" src="libs/modules/indDefineCtx.js"></script>
<script type="text/javascript" src="libs/modules/indRunCtx.js"></script>
<script type="text/javascript" src="libs/modules/tamanager.js"></script>
<script type="text/javascript" src="libs/modules/taskmanager.js"></script>
<script type="text/javascript" src="libs/modules/websocket.js"></script>
<!-- del:end -->
<script type="text/javascript" src="libs/func/basefuncs.js"></script>
<script type="text/javascript" src="libs/tqsdk.js"></script>
<script type="text/javascript" id='TRADE-CODE'>
const TQ = new TQSDK();
function* TaskQuote() {
let quoteA = TQ.GET_QUOTE(TQ.UI.symbolA);
let quoteB = TQ.GET_QUOTE(TQ.UI.symbolB);
while (true) {
let result = yield {
QUOTE_UPDATED: function () { return TQ.IS_CHANGING(quoteA) || TQ.IS_CHANGING(quoteB); },
SYMBOL_CHANGED_A: TQ.ON_CHANGE('symbolA'),
SYMBOL_CHANGED_B: TQ.ON_CHANGE('symbolB'),
};
if (result.SYMBOL_CHANGED_A || result.SYMBOL_CHANGED_B) {
quoteA = TQ.GET_QUOTE(TQ.UI.symbolA);
quoteB = TQ.GET_QUOTE(TQ.UI.symbolB);
}
if (quoteA && quoteB) {
TQ.UI.spread_ask = quoteA.ask_price1 - quoteB.bid_price1;
TQ.UI.spread_bid = quoteA.bid_price1 - quoteB.ask_price1;
}
}
}
function* TaskSpread(C) {
let quoteA = TQ.GET_QUOTE(TQ.UI.symbolA);
let quoteB = TQ.GET_QUOTE(TQ.UI.symbolB);
TQ.SET_STATE('START');
let short_volume = 0, long_volume = 0;
let volume = parseInt(TQ.UI.volume);
let order_a = { symbol: TQ.UI.symbolA, volume};
let order_b = { symbol: TQ.UI.symbolB, volume};
let buy_open = { direction: 'BUY', offset: 'OPEN', price_field: 'ask_price1' };
let sell_close = { direction: 'SELL', offset: 'CLOSE', price_field: 'bid_price1' };
let sell_open = { direction: 'SELL', offset: 'OPEN', price_field: 'bid_price1' };
let buy_close = { direction: 'BUY', offset: 'CLOSE', price_field: 'ask_price1' };
let task_a, task_b;
while (true) {
let result = yield {
USER_CLICK_STOP: TQ.ON_CLICK('STOP'),
BUY_OPEN: function() {return quoteA.ask_price1 - quoteB.bid_price1 < TQ.UI.price_buy_open + 0.00001 && long_volume == 0},
SELL_CLOSE: function() {return quoteA.bid_price1 - quoteB.ask_price1 > TQ.UI.price_sell_close - 0.00001 && long_volume > 0},
SELL_OPEN: function() {return quoteA.bid_price1 - quoteB.ask_price1 > TQ.UI.price_sell_open - 0.00001 && short_volume == 0},
BUY_CLOSE: function() {return quoteA.ask_price1 - quoteB.bid_price1 < TQ.UI.price_buy_close + 0.00001 && short_volume > 0},
};
if (result.USER_CLICK_STOP)
break;
if(result.BUY_OPEN){
task_a = TQ.START_TASK(TaskOrder, Object.assign({}, order_a, buy_open));
task_b = TQ.START_TASK(TaskOrder, Object.assign({}, order_b, sell_open));
long_volume = volume;
}
if(result.SELL_CLOSE){
task_a = TQ.START_TASK(TaskOrder, Object.assign({}, order_a, sell_close));
task_b = TQ.START_TASK(TaskOrder, Object.assign({}, order_b, buy_close));
long_volume = 0;
}
if(result.SELL_OPEN){
task_a = TQ.START_TASK(TaskOrder, Object.assign({}, order_a, sell_open));
task_b = TQ.START_TASK(TaskOrder, Object.assign({}, order_b, buy_open));
short_volume = volume;
}
if(result.BUY_CLOSE){
task_a = TQ.START_TASK(TaskOrder, Object.assign({}, order_a, buy_close));
task_b = TQ.START_TASK(TaskOrder, Object.assign({}, order_b, sell_close));
short_volume = 0;
}
let wait_subtask_finish = yield {
USER_CLICK_STOP: TQ.ON_CLICK('STOP'),
ANY_TASK_ERROR: function() {return task_a.return=="error" || task_b.return=="error";},
BOTH_TASK_FINISH: function() {return task_a.stopped && task_b.stopped;},
}
// 错单 - 任务结束
if (wait_subtask_finish.USER_CLICK_STOP || wait_subtask_finish.ANY_TASK_ERROR)
break;
}
// 任务结束
TQ.STOP_TASK(task_a);
TQ.STOP_TASK(task_b);
TQ.CANCEL_ORDER();
TQ.SET_STATE('STOP');
}
function* TaskOrder(order_params) {
var quote = TQ.GET_QUOTE(order_params.symbol);
order_params.limit_price = quote[order_params.price_field];
var order = TQ.INSERT_ORDER(order_params);
while (true) {
let result = yield {
ORDER_TRADE: function () { return order.volume_orign > order.volume_left; },
PRICE_CHANGED: function () {
var new_price = order_params.direction === 'SELL' ? quote.bid_price1 : quote.ask_price1;
return new_price !== order_params.limit_price;
},
};
// 错单
if (order.status == 'FINISHED' && order.exchange_order_id == '')
return 'error';
if (order.volume_left == 0)
break;
if (result.PRICE_CHANGED && order.status == "ALIVE")
TQ.CANCEL_ORDER(order);
let wait_cancel = yield {
ORDER_CANCELED: function () { return order.status == 'FINISHED'; },
};
if (order.volume_left > 0) {
order_params.limit_price = order_params.direction === 'SELL' ? quote.bid_price1 : quote.ask_price1;
order_params.volume = order.volume_left;
order = TQ.INSERT_ORDER(order_params);
}
}
}
$(function () {
TQ.START_TASK(TaskQuote);
// 开始交易按钮
$('button.START').on('click', function () {
TQ.START_TASK(TaskSpread);
});
});
</script>
</body>
</html>
|