This commit is contained in:
botvs
2016-10-21 23:08:45 +08:00
parent c5b8599407
commit b1b41d1fcf
5 changed files with 460 additions and 16 deletions
@@ -449,7 +449,7 @@ var TTManager = {
var canOpen = parseInt(account.Balance / (opCode == 1 ? obj.symbolDetail.LongMarginRatio : obj.symbolDetail.ShortMarginRatio) / (lastPrice * 1.2) / obj.symbolDetail.VolumeMultiple);
unit = Math.min(unit, canOpen);
if (unit < obj.symbolDetail.MinLimitOrderVolume) {
obj.setLastError("可开 " + unit + " 手 过小无法开仓");
obj.setLastError("可开 " + unit + " 手 无法开仓, " + (canOpen >= obj.symbolDetail.MinLimitOrderVolume ? "风控触发" : "资金限制"));
return;
}
obj.setTask((opCode == 1 ? ACT_LONG : ACT_SHORT), unit, function(ret) {
@@ -0,0 +1,199 @@
/*
策略出处: https://www.botvs.com/strategy/20266
策略名称: MACD低买高卖自动跟单滑动止损 (复制)
策略作者: 13633698059
策略描述:
交叉后前一柱指金叉后的第一柱的值, 交叉后前一柱指金叉前的最后一个柱的值, 滑动价格指下单时加的价格,比如买单会现价加上这个价格,卖单会减去这个价格
参数 默认值 描述
------------ ------ -------
ac1 0.005 交叉后第一柱
bc1 -1e-06 交叉后前一柱
SlidePrice 0.3 滑动价格
TrailingStop 0.5 止损点
orderTimeout 30 买单超时(秒)
*/
Fixed = function(v) {
return Math.floor(v*1000)/1000;
};
// for orders
WaitOrder = function(exchange, orderId, timeoutToCancel) {
var ts = (new Date()).getTime();
while (true) {
Sleep(3000);
var orderInfo = exchange.GetOrder(orderId);
if (!orderInfo) {
continue;
}
if (orderInfo.Status == ORDER_STATE_CLOSED || orderInfo.Status == ORDER_STATE_CANCELED) {
return orderInfo;
}
if (((new Date()).getTime() - ts) > timeoutToCancel) {
exchange.CancelOrder(orderId);
}
}
};
Buy = function(exchange, maxPrice, slidePrice, balanceRatio, timeoutS) {
var ts = (new Date()).getTime();
var account;
var dealAmount = 0.0;
var usedBlance = 0.0;
var maxBalanceUse = 0.0;
var isFirst = true;
do {
if (isFirst) {
isFirst = false;
} else {
Sleep(3000);
}
var ticker = exchange.GetTicker();
if (!ticker) {
continue;
}
var buyPrice = ticker.Sell + slidePrice;
// Price too high, wait...
if (buyPrice > maxPrice) {
continue;
}
// Initialize at first
if (!account) {
account = exchange.GetAccount();
if (!account) {
continue;
}
// Initialize maxBalanceUse
maxBalanceUse = account.Balance * balanceRatio;
}
var buyAmount = Fixed((maxBalanceUse - usedBlance) / buyPrice);
if (buyAmount < exchange.GetMinStock()) {
break;
}
orderId = exchange.Buy(buyPrice, buyAmount);
if (!orderId) {
Log(buyPrice, buyAmount, maxBalanceUse, usedBlance);
continue;
}
var orderInfo = WaitOrder(exchange, orderId, timeoutS);
dealAmount += orderInfo.DealAmount;
usedBlance += orderInfo.Price * orderInfo.DealAmount;
if (orderInfo.Status == ORDER_STATE_CLOSED) {
break;
}
} while (((new Date()).getTime() - ts) < timeoutS);
return {amount: dealAmount, price: (dealAmount > 0 ? usedBlance / dealAmount : 0)};
};
Sell = function(exchange, sellAmount, slidePrice) {
// Account info must set
var account = exchange.GetAccount();
while (!account) {
Sleep(2000);
account = exchange.GetAccount();
}
sellAmount = Math.min(sellAmount, account.Stocks);
var cash = 0.0;
var remain = sellAmount;
while (remain >= exchange.GetMinStock()) {
var ticker = exchange.GetTicker();
if (!ticker) {
Sleep(2000);
continue;
}
var sellPrice = ticker.Buy - slidePrice;
var sellOrderId = exchange.Sell(sellPrice, remain);
if (!sellOrderId) {
Sleep(2000);
continue;
}
var orderInfo = WaitOrder(exchange, sellOrderId, 10000);
remain -= orderInfo.DealAmount;
cash += orderInfo.Price * orderInfo.DealAmount;
}
return {amount: sellAmount, price: (sellAmount > 0 ? cash / sellAmount : 0)};
};
var BuyInfo;
var BanlanceRatio = 1.0;
var Profit = 0.0;
var timeAtBuy = 0;
function onTick(exchange) {
var ticker = exchange.GetTicker();
var records = exchange.GetRecords();
if (!ticker || !records || records.length < 45) {
return;
}
var ticks = [];
for (var i = 0; i < records.length; i++) {
ticks.push(records[i].Close);
}
var macd = TA.MACD(records, 12, 26, 9);
var dif = macd[0];
var dea = macd[1];
var his = macd[2];
var op = 0;
if (!BuyInfo) {
if (dif[ticks.length-1] > 0 && his[ticks.length-1] > ac1 && his[ticks.length-2] < bc1) {
op = 1;
}
} else {
if (records[records.length-2].Time > timeAtBuy && records[records.length-1].Close < records[records.length-1].Open - 0.5
&& records[records.length-2].Close < records[records.length-2].Open - 0.5
&& records[records.length-1].Close < records[records.length-2].Close - 0.5) {
op = 2;
} else if (records[records.length-2].Time > timeAtBuy && BuyInfo.price > records[records.length-1].Close && records[records.length-1].Close < records[records.length-1].Open - 0.5) {
op = 2;
} else if ((BuyInfo.price < ticker.Last || dif[ticks.length-1] < 0) && his[ticks.length-1] <= 0) {
op = 2;
} else if ((BuyInfo.price > ticker.Last) && ((BuyInfo.price - ticker.Last) / BuyInfo.price > TrailingStop)) {
op = 2;
}
}
if (op == 1) {
var info = Buy(exchange, ticker.Sell + (SlidePrice * 3), SlidePrice, BanlanceRatio, orderTimeout * 1000);
if (info.amount > 0) {
BuyInfo = info;
timeAtBuy = records[records.length-1].Time;
}
} else if (op == 2) {
var info = Sell(exchange, BuyInfo.amount, SlidePrice);
if (info.amount > 0) {
Profit += info.amount * (info.price - BuyInfo.price);
LogProfit(Profit);
BuyInfo = null;
}
}
}
function main() {
var account = exchange.GetAccount();
if (account) {
Log(exchange.GetName(), exchange.GetCurrency(), account);
}
while (true) {
onTick(exchange);
Sleep(30000);
}
}
+3 -2
View File
@@ -4,13 +4,14 @@
> 开源策略列表
- [商品期货交易类库](https://www.botvs.com/strategy/12961)
- [python版现货数字货币交易类库](https://www.botvs.com/strategy/21104)
- [CTP商品期货多品种海龟交易策略](https://www.botvs.com/strategy/17289)
- [Dual Thrust_双轨突破_策略1](https://www.botvs.com/strategy/22983)
- [Python 机器学习之 SVM 预测买卖](https://www.botvs.com/strategy/21370)
- [顺势_高频_交易_测试3](https://www.botvs.com/strategy/22183)
- [Python API 测试](https://www.botvs.com/strategy/21365)
- [均线策略(python版)](https://www.botvs.com/strategy/21157)
- [python版现货数字货币交易类库](https://www.botvs.com/strategy/21104)
- [顺势_高频_交易_测试2——失败](https://www.botvs.com/strategy/21938)
- [顺势_高频_交易_测试1](https://www.botvs.com/strategy/21742)
- [各大平台API可用率统计](https://www.botvs.com/strategy/4404)
@@ -22,8 +23,8 @@
- [图表模板(增加状态栏表格)](https://www.botvs.com/strategy/20967)
- [策略框架](https://www.botvs.com/strategy/20663)
- [高频交易策略之 - Penny Jump](https://www.botvs.com/strategy/358)
- [MACD低买高卖自动跟单滑动止损 (复制)](https://www.botvs.com/strategy/20266)
- [获取周K线](https://www.botvs.com/strategy/20226)
- [商品期货交易类库](https://www.botvs.com/strategy/12961)
- [对冲2.7收益率统计系统 支持多组账号统计](https://www.botvs.com/strategy/18678)
- [从第三方获取K线 (商品期货版) Rev. 160810](https://www.botvs.com/strategy/15498)
- [收益率统计](https://www.botvs.com/strategy/19329)
@@ -186,6 +186,7 @@ def _GetAccount(e = exchange):
return _C(e.GetAccount)
_MACalcMethod = [TA.EMA,TA.MA][MAType]
Interval = 200
def Cross(a,b):
if isFirstCheck:
CheckVersion()
+256 -13
View File
@@ -225,6 +225,28 @@ var trans = {
"WithdrawQuota": "可取资金",
};
function AccountToTable(jsStr, title) {
if (typeof(title) === 'undefined') {
title = '账户信息';
}
var tbl = { type : "table", title : title, cols : ["字段", "描述", "值"], rows : [] };
try {
var fields = JSON.parse(jsStr);
for (var k in fields) {
if (k == 'AccountID' || k == 'BrokerID') {
continue
}
var desc = trans[k];
var v = fields[k];
if (typeof(v) === 'number') {
v = _N(v, 5);
}
tbl.rows.push([k, typeof(desc) === 'undefined' ? '--' : desc, v]);
}
} catch(e) {}
return tbl;
}
var PositionManager = (function() {
function PositionManager(e) {
if (typeof(e) === 'undefined') {
@@ -246,19 +268,7 @@ var PositionManager = (function() {
PositionManager.prototype.GetAccount = function(getTable) {
this.account = _C(this.e.GetAccount);
if (typeof(getTable) !== 'undefined' && getTable) {
var tbl = { type : "table", title : "账户信息", cols : ["字段", "描述", "值"], rows : [] };
try {
var fields = JSON.parse(this.e.GetRawJSON());
for (var k in fields) {
var desc = trans[k];
var v = fields[k];
if (typeof(v) === 'number') {
v = _N(v, 5);
}
tbl.rows.push([k, typeof(desc) === 'undefined' ? '--' : desc, v]);
}
} catch(e) {};
return tbl;
return AccountToTable(this.e.GetRawJSON())
}
return this.account;
};
@@ -367,6 +377,233 @@ $.IsTrading = function(symbol) {
return false;
};
$.NewTaskQueue = function(onTaskFinish) {
var self = {}
self.ERR_SUCCESS = 0
self.ERR_SET_SYMBOL = 1
self.ERR_GET_RECORDS = 2
self.ERR_GET_ORDERS = 3
self.ERR_GET_POS = 4
self.ERR_TRADE = 5
self.ERR_GET_DEPTH = 6
self.ERR_NOT_TRADING = 7
self.ERR_BUSY = 8
self.onTaskFinish = typeof(onTaskFinish) === 'undefined' ? null : onTaskFinish
self.retryInterval = 300
self.tasks = []
self.pushTask = function(e, symbol, action, amount, onFinish) {
var task = {
e: e,
action: action,
symbol: symbol,
amount: amount,
init: false,
finished: false,
dealAmount: 0,
preAmount: 0,
preCost: 0,
retry: 0,
maxRetry: 10,
onFinish: onFinish
}
switch (task.action) {
case "buy":
task.desc = task.symbol + " 开多仓, 数量 " + task.amount
break
case "sell":
task.desc = task.symbol + " 开空仓, 数量 " + task.amount
break
case "closebuy":
task.desc = task.symbol + " 平多仓, 数量 " + task.amount
break
case "closesell":
task.desc = task.symbol + " 平空仓, 数量 " + task.amount
break
default:
task.desc = task.symbol + " " + task.action + ", 数量 " + task.amount
}
self.tasks.push(task)
Log("接收到任务", task.desc)
}
self.cancelAll = function(e) {
while (true) {
var orders = e.GetOrders();
if (!orders) {
return self.ERR_GET_ORDERS;
}
if (orders.length == 0) {
break;
}
for (var i = 0; i < orders.length; i++) {
e.CancelOrder(orders[i].Id);
Sleep(self.retryInterval);
}
}
return self.ERR_SUCCESS
}
self.pollTask = function(task) {
var insDetail = task.e.SetContractType(task.symbol);
if (!insDetail) {
return self.ERR_SET_SYMBOL;
}
var SlideTick = 1;
var ret = false;
if (task.action == "closebuy" || task.action == "closesell") {
var hasPosition = false;
do {
if (!(task.e.IO("status") && $.IsTrading(task.symbol))) {
return self.ERR_NOT_TRADING;
}
hasPosition = false;
var positions = task.e.GetPosition();
if (!positions) {
return self.ERR_GET_POS;
}
var depth = task.e.GetDepth();
if (!depth) {
return self.ERR_GET_DEPTH;
}
var orderId = null;
for (var i = 0; i < positions.length; i++) {
if (positions[i].ContractType !== task.symbol) {
continue;
}
hasPosition = true;
var amount = Math.min(insDetail.MaxLimitOrderVolume, positions[i].Amount, task.amount);
if (task.action == "closebuy" && (positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD)) {
task.e.SetDirection(positions[i].Type == PD_LONG ? "closebuy_today" : "closebuy");
orderId = task.e.Sell(_N(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), 2), Math.min(amount, depth.Bids[0].Amount), task.symbol, positions[i].Type == PD_LONG ? "平今" : "平昨", 'Bid', depth.Bids[0]);
} else if (task.action == "closesell" && (positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD)) {
task.e.SetDirection(positions[i].Type == PD_SHORT ? "closesell_today" : "closesell");
orderId = task.e.Buy(_N(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), 2), Math.min(amount, depth.Asks[0].Amount), task.symbol, positions[i].Type == PD_SHORT ? "平今" : "平昨", 'Ask', depth.Asks[0]);
}
}
if (hasPosition) {
if (!orderId) {
return self.ERR_TRADE;
}
Sleep(500);
var ret = self.cancelAll(task.e)
if (ret != self.ERR_SUCCESS) {
return ret
}
}
} while (hasPosition);
ret = true;
} else {
do {
if (!$.IsTrading(task.symbol)) {
return self.ERR_NOT_TRADING;
}
Sleep(500);
var ret = self.cancelAll(task.e)
if (ret != self.ERR_SUCCESS) {
return ret
}
var positions = task.e.GetPosition();
// Error
if (!positions) {
return self.ERR_GET_POS;
}
// search position
var pos = null;
for (var i = 0; i < positions.length; i++) {
if (positions[i].ContractType == task.symbol && (((positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) && task.action == "buy") || ((positions[i].Type == PD_SHORT || positions[i].Type == PD_SHORT_YD) && task.action == "sell"))) {
if (!pos) {
pos = positions[i];
pos.Cost = positions[i].Price * positions[i].Amount;
} else {
pos.Amount += positions[i].Amount;
pos.Profit += positions[i].Profit;
pos.Cost += positions[i].Price * positions[i].Amount;
}
}
}
// record pre position
if (!task.init) {
task.init = true;
if (pos) {
task.preAmount = pos.Amount;
task.preCost = pos.Cost;
} else {
task.preAmount = 0;
task.preCost = 0;
}
}
var remain = task.amount;
if (pos) {
task.dealAmount = pos.Amount - task.preAmount;
remain = parseInt(task.amount - task.dealAmount);
if (remain <= 0 || task.retry >= task.maxRetry) {
ret = {
price: (pos.Cost - task.preCost) / (pos.Amount - task.preAmount),
amount: (pos.Amount - task.preAmount),
position: pos
};
break;
}
} else if (task.retry >= task.maxRetry) {
ret = null;
break;
}
var depth = task.e.GetDepth();
if (!depth) {
return self.ERR_GET_DEPTH;
}
var orderId = null;
if (task.action == "buy") {
task.e.SetDirection("buy");
orderId = task.e.Buy(_N(depth.Asks[0].Price + (insDetail.PriceTick * SlideTick), 2), Math.min(remain, depth.Asks[0].Amount), task.symbol, 'Ask', depth.Asks[0]);
} else {
task.e.SetDirection("sell");
orderId = task.e.Sell(_N(depth.Bids[0].Price - (insDetail.PriceTick * SlideTick), 2), Math.min(remain, depth.Bids[0].Amount), task.symbol, 'Bid', depth.Bids[0]);
}
// symbol not in trading or other else happend
if (!orderId) {
task.retry++;
return self.ERR_TRADE;
}
} while (true);
}
task.finished = true
if (self.onTaskFinish) {
self.onTaskFinish(task, ret)
}
if (task.onFinish) {
task.onFinish(task, ret);
}
return self.ERR_SUCCESS;
}
self.poll = function() {
var processed = 0
_.each(self.tasks, function(task) {
if (!task.finished) {
processed++
self.pollTask(task)
}
})
if (processed == 0) {
self.tasks = []
}
}
self.size = function() {
return self.tasks.length
}
return self
}
$.AccountToTable = AccountToTable;
function main() {
var p = $.NewPositionManager();
p.OpenShort("MA609", 1);
@@ -377,4 +614,10 @@ function main() {
p.Cover("MA609");
LogProfit(p.Profit());
Log($.IsTrading("MA609"));
var q = $.NewTaskQueue();
q.pushTask(exchange, "MA701", "buy", 3, function(task, ret) {
Log(task.desc, ret)
})
q.poll()
}