update
This commit is contained in:
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user