I have some strategy on BotVS . In fact , I think the hedge strategy is the low risk strategy. Although the profit is few .
Ok ! Talk is cheap, Show code to you !
Address:
https://www.botvs.com/strategy/57var initState;
var isBalance = true;
var feeCache = new Array();
var feeTimeout = optFeeTimeout * 60000;
var lastProfit = 0;
var lastAvgPrice = 0;
var lastSpread = 0;
var lastOpAmount = 0;
function adjustFloat(v) {
return Math.floor(v*1000)/1000;
}
function isPriceNormal(v) {
return (v >= StopPriceL) && (v <= StopPriceH);
}
function stripTicker(t) {
return 'Buy: ' + adjustFloat(t.Buy) + ' Sell: ' + adjustFloat(t.Sell);
}
function updateStatePrice(state) {
var now = (new Date()).getTime();
for (var i = 0; i < state.details.length; i++) {
var ticker = null;
var key = state.details[i].exchange.GetName() + state.details[i].exchange.GetCurrency();
var fee = null;
while (!(ticker = state.details[i].exchange.GetTicker())) {
Sleep(Interval);
}
if (key in feeCache) {
var v = feeCache[key];
if ((now - v.time) > feeTimeout) {
delete feeCache[key];
} else {
fee = v.fee;
}
}
if (!fee) {
while (!(fee = state.details[i].exchange.GetFee())) {
Sleep(Interval);
}
feeCache[key] = {fee: fee, time: now};
}
// Buy-=fee Sell+=fee
state.details[i].ticker = {Buy: ticker.Buy * (1-(fee.Sell/100)), Sell: ticker.Sell * (1+(fee.Buy/100))};
state.details[i].realTicker = ticker;
state.details[i].fee = fee;
}
}
function getProfit(stateInit, stateNow, coinPrice) {
var netNow = stateNow.allBalance + (stateNow.allStocks * coinPrice);
var netInit = stateInit.allBalance + (stateInit.allStocks * coinPrice);
return adjustFloat(netNow - netInit);
}
function getExchangesState() {
var allStocks = 0;
var allBalance = 0;
var minStock = 0;
var details = [];
for (var i = 0; i < exchanges.length; i++) {
var account = null;
while (!(account = exchanges[i].GetAccount())) {
Sleep(Interval);
}
allStocks += account.Stocks + account.FrozenStocks;
allBalance += account.Balance + account.FrozenBalance;
minStock = Math.max(minStock, exchanges[i].GetMinStock());
details.push({exchange: exchanges[i], account: account});
}
return {allStocks: adjustFloat(allStocks), allBalance: adjustFloat(allBalance), minStock: minStock, details: details};
}
function cancelAllOrders() {
for (var i = 0; i < exchanges.length; i++) {
while (true) {
var orders = null;
while (!(orders = exchanges[i].GetOrders())) {
Sleep(Interval);
}
if (orders.length == 0) {
break;
}
for (var j = 0; j < orders.length; j++) {
exchanges[i].CancelOrder(orders[j].Id, orders[j]);
}
}
}
}
function balanceAccounts() {
// already balance
if (isBalance) {
return;
}
cancelAllOrders();
var state = getExchangesState();
var diff = state.allStocks - initState.allStocks;
var adjustDiff = adjustFloat(Math.abs(diff));
if (adjustDiff < state.minStock) {
isBalance = true;
} else {
Log('初始币总数量:', initState.allStocks, '现在币总数量: ', state.allStocks, '差额:', adjustDiff);
// other ways, diff is 0.012, bug A only has 0.006 B only has 0.006, all less then minstock
// we try to statistical orders count to recognition this situation
updateStatePrice(state);
var details = state.details;
var ordersCount = 0;
if (diff > 0) {
var attr = 'Sell';
if (UseMarketOrder) {
attr = 'Buy';
}
// Sell adjustDiff, sort by price high to low
details.sort(function(a, b) {return b.ticker[attr] - a.ticker[attr];});
for (var i = 0; i < details.length && adjustDiff >= state.minStock; i++) {
if (isPriceNormal(details[i].ticker[attr]) && (details[i].account.Stocks >= state.minStock)) {
var orderAmount = adjustFloat(Math.min(AmountOnce, adjustDiff, details[i].account.Stocks));
var orderPrice = details[i].realTicker[attr] - SlidePrice;
if ((orderPrice * orderAmount) < details[i].exchange.GetMinPrice()) {
continue;
}
ordersCount++;
if (details[i].exchange.Sell(orderPrice, orderAmount, stripTicker(details[i].ticker))) {
adjustDiff = adjustFloat(adjustDiff - orderAmount);
}
// only operate one platform
break;
}
}
} else {
var attr = 'Buy';
if (UseMarketOrder) {
attr = 'Sell';
}
// Buy adjustDiff, sort by sell-price low to high
details.sort(function(a, b) {return a.ticker[attr] - b.ticker[attr];});
for (var i = 0; i < details.length && adjustDiff >= state.minStock; i++) {
if (isPriceNormal(details[i].ticker[attr])) {
var canRealBuy = adjustFloat(details[i].account.Balance / (details[i].ticker[attr] + SlidePrice));
var needRealBuy = Math.min(AmountOnce, adjustDiff, canRealBuy);
var orderAmount = adjustFloat(needRealBuy * (1+(details[i].fee.Buy/100)));
var orderPrice = details[i].realTicker[attr] + SlidePrice;
if ((orderAmount < details[i].exchange.GetMinStock()) ||
((orderPrice * orderAmount) < details[i].exchange.GetMinPrice())) {
continue;
}
ordersCount++;
if (details[i].exchange.Buy(orderPrice, orderAmount, stripTicker(details[i].ticker))) {
adjustDiff = adjustFloat(adjustDiff - needRealBuy);
}
// only operate one platform
break;
}
}
}
isBalance = (ordersCount == 0);
}
if (isBalance) {
var currentProfit = getProfit(initState, state, lastAvgPrice);
LogProfit(currentProfit, "Spread: ", adjustFloat((currentProfit - lastProfit) / lastOpAmount), "Balance: ", adjustFloat(state.allBalance), "Stocks: ", adjustFloat(state.allStocks));
if (StopWhenLoss && currentProfit < 0 && Math.abs(currentProfit) > MaxLoss) {
Log('交易亏损超过最大限度, 程序取消所有订单后退出.');
cancelAllOrders();
if (SMSAPI.length > 10 && SMSAPI.indexOf('http') == 0) {
HttpQuery(SMSAPI);
Log('已经短信通知');
}
throw '已停止';
}
lastProfit = currentProfit;
}
}
function onTick() {
if (!isBalance) {
balanceAccounts();
return;
}
var state = getExchangesState();
// We also need details of price
updateStatePrice(state);
var details = state.details;
var maxPair = null;
var minPair = null;
for (var i = 0; i < details.length; i++) {
var sellOrderPrice = details[i].account.Stocks * (details[i].realTicker.Buy - SlidePrice);
if (((!maxPair) || (details[i].ticker.Buy > maxPair.ticker.Buy)) && (details[i].account.Stocks >= state.minStock) &&
(sellOrderPrice > details[i].exchange.GetMinPrice())) {
details[i].canSell = details[i].account.Stocks;
maxPair = details[i];
}
var canBuy = adjustFloat(details[i].account.Balance / (details[i].realTicker.Sell + SlidePrice));
var buyOrderPrice = canBuy * (details[i].realTicker.Sell + SlidePrice);
if (((!minPair) || (details[i].ticker.Sell < minPair.ticker.Sell)) && (canBuy >= state.minStock) &&
(buyOrderPrice > details[i].exchange.GetMinPrice())) {
details[i].canBuy = canBuy;
// how much coins we real got with fee
details[i].realBuy = adjustFloat(details[i].account.Balance / (details[i].ticker.Sell + SlidePrice));
minPair = details[i];
}
}
if ((!maxPair) || (!minPair) || ((maxPair.ticker.Buy - minPair.ticker.Sell) < MaxDiff) ||
!isPriceNormal(maxPair.ticker.Buy) || !isPriceNormal(minPair.ticker.Sell)) {
return;
}
// filter invalid price
if (minPair.realTicker.Sell <= minPair.realTicker.Buy || maxPair.realTicker.Sell <= maxPair.realTicker.Buy) {
return;
}
// what a fuck...
if (maxPair.exchange.GetName() == minPair.exchange.GetName()) {
return;
}
lastAvgPrice = adjustFloat((minPair.realTicker.Buy + maxPair.realTicker.Buy) / 2);
lastSpread = adjustFloat((maxPair.realTicker.Sell - minPair.realTicker.Buy) / 2);
// compute amount
var amount = Math.min(AmountOnce, maxPair.canSell, minPair.realBuy);
lastOpAmount = amount;
var hedgePrice = adjustFloat((maxPair.realTicker.Buy - minPair.realTicker.Sell) / Math.max(SlideRatio, 2))
if (minPair.exchange.Buy(minPair.realTicker.Sell + hedgePrice, amount * (1+(minPair.fee.Buy/100)), stripTicker(minPair.realTicker))) {
maxPair.exchange.Sell(maxPair.realTicker.Buy - hedgePrice, amount, stripTicker(maxPair.realTicker));
}
isBalance = false;
}
function main() {
if (exchanges.length < 2) {
throw "交易所数量最少得两个才能完成对冲";
}
TickInterval = Math.max(TickInterval, 50);
Interval = Math.max(Interval, 50);
cancelAllOrders();
initState = getExchangesState();
if (initState.allStocks == 0) {
throw "所有交易所货币数量总和为空, 必须先在任一交易所建仓才可以完成对冲";
}
if (initState.allBalance == 0) {
throw "所有交易所CNY数量总和为空, 无法继续对冲";
}
for (var i = 0; i < initState.details.length; i++) {
var e = initState.details[i];
Log(e.exchange.GetName(), e.exchange.GetCurrency(), e.account);
}
Log("ALL: Balance: ", initState.allBalance, "Stocks: ", initState.allStocks, "Ver:", Version());
while (true) {
onTick();
Sleep(parseInt(TickInterval));
}
}
The another one is the Grid strategy: (Used in shocking market)
address :
https://www.botvs.com/strategy/629function hasOrder(orders, orderId) {
for (var i = 0; i < orders.length; i++) {
if (orders[i].Id == orderId) {
return true;
}
}
return false;
}
function cancelPending() {
var ret = false;
while (true) {
if (ret) {
Sleep(Interval);
}
var orders = _C(exchange.GetOrders);
if (orders.length == 0) {
break;
}
for (var j = 0; j < orders.length; j++) {
exchange.CancelOrder(orders[j].Id, orders[j]);
ret = true;
}
}
return ret;
}
function valuesToString(values, pos) {
var result = '';
if (typeof(pos) === 'undefined') {
pos = 0;
}
for (var i = pos; i < values.length; i++) {
if (i > pos) {
result += ' ';
}
if (values[i] === null) {
result += 'null';
} else if (typeof(values[i]) == 'undefined') {
result += 'undefined';
} else {
switch (values[i].constructor.name) {
case 'Date':
case 'Number':
case 'String':
case 'Function':
result += values[i].toString();
break;
default:
result += JSON.stringify(values[i]);
break;
}
}
}
return result;
}
function Trader() {
var vId = 0;
var orderBooks = [];
var hisBooks = [];
var orderBooksLen = 0;
this.Buy = function(price, amount, extra) {
if (typeof(extra) === 'undefined') {
extra = '';
} else {
extra = valuesToString(arguments, 2);
}
vId++;
var orderId = "V" + vId;
orderBooks[orderId] = {
Type: ORDER_TYPE_BUY,
Status: ORDER_STATE_PENDING,
Id: 0,
Price: price,
Amount: amount,
Extra: extra
};
orderBooksLen++;
return orderId;
};
this.Sell = function(price, amount, extra) {
if (typeof(extra) === 'undefined') {
extra = '';
} else {
extra = valuesToString(arguments, 2);
}
vId++;
var orderId = "V" + vId;
orderBooks[orderId] = {
Type: ORDER_TYPE_SELL,
Status: ORDER_STATE_PENDING,
Id: 0,
Price: price,
Amount: amount,
Extra: extra
};
orderBooksLen++;
return orderId;
};
this.GetOrders = function() {
var orders = _C(exchange.GetOrders);
for (orderId in orderBooks) {
var order = orderBooks[orderId];
if (order.Status !== ORDER_STATE_PENDING) {
continue;
}
var found = false;
for (var i = 0; i < orders.length; i++) {
if (orders[i].Id == order.Id) {
found = true;
break;
}
}
if (!found) {
orders.push(orderBooks[orderId]);
}
}
return orders;
}
this.GetOrder = function(orderId) {
if (typeof(orderId) === 'number') {
return exchange.GetOrder(orderId);
}
if (typeof(hisBooks[orderId]) !== 'undefined') {
return hisBooks[orderId];
}
if (typeof(orderBooks[orderId]) !== 'undefined') {
return orderBooks[orderId];
}
return null;
};
this.Len = function() {
return orderBooksLen;
};
this.RealLen = function() {
var n = 0;
for (orderId in orderBooks) {
if (orderBooks[orderId].Id > 0) {
n++;
}
}
return n;
};
this.Poll = function(ticker, priceDiff) {
var orders = _C(exchange.GetOrders);
for (orderId in orderBooks) {
var order = orderBooks[orderId];
if (order.Id > 0) {
var found = false;
for (var i = 0; i < orders.length; i++) {
if (order.Id == orders[i].Id) {
found = true;
}
}
if (!found) {
order.Status = ORDER_STATE_CLOSED;
hisBooks[orderId] = order;
delete(orderBooks[orderId]);
orderBooksLen--;
continue;
}
}
var diff = _N(order.Type == ORDER_TYPE_BUY ? (ticker.Buy - order.Price) : (order.Price - ticker.Sell));
var pfn = order.Type == ORDER_TYPE_BUY ? exchange.Buy : exchange.Sell;
if (order.Id == 0 && diff <= priceDiff) {
var realId = pfn(order.Price, order.Amount, order.Extra + "(距离: " + diff + (order.Type == ORDER_TYPE_BUY ? (" 买一: " + ticker.Buy) : (" 卖一: " + ticker.Sell))+")");
if (typeof(realId) === 'number') {
order.Id = realId;
}
} else if (order.Id > 0 && diff > (priceDiff + 1)) {
var ok = true;
do {
ok = true;
exchange.CancelOrder(order.Id, "不必要的" + (order.Type == ORDER_TYPE_BUY ? "买单" : "卖单"), "委托价:", order.Price, "量:", order.Amount, ", 距离:", diff, order.Type == ORDER_TYPE_BUY ? ("买一: " + ticker.Buy) : ("卖一: " + ticker.Sell));
Sleep(200);
orders = _C(exchange.GetOrders);
for (var i = 0; i < orders.length; i++) {
if (orders[i].Id == order.Id) {
ok = false;
}
}
} while (!ok);
order.Id = 0;
}
}
};
}
function balanceAccount(orgAccount, initAccount) {
cancelPending();
var nowAccount = _C(exchange.GetAccount);
var slidePrice = 0.2;
var ok = true;
while (true) {
var diff = _N(nowAccount.Stocks - initAccount.Stocks);
if (Math.abs(diff) < exchange.GetMinStock()) {
break;
}
var depth = _C(exchange.GetDepth);
var books = diff > 0 ? depth.Bids : depth.Asks;
var n = 0;
var price = 0;
for (var i = 0; i < books.length; i++) {
n += books[i].Amount;
if (n >= Math.abs(diff)) {
price = books[i].Price;
break;
}
}
var pfn = diff > 0 ? exchange.Sell : exchange.Buy;
var amount = Math.abs(diff);
var price = diff > 0 ? (price - slidePrice) : (price + slidePrice);
Log("开始平衡", (diff > 0 ? "卖出" : "买入"), amount, "个币");
if (diff > 0) {
amount = Math.min(nowAccount.Stocks, amount);
} else {
amount = Math.min(nowAccount.Balance / price, amount);
}
if (amount < exchange.GetMinStock()) {
Log("资金不足, 无法平衡到初始状态");
ok = false;
break;
}
pfn(price, amount);
Sleep(1000);
cancelPending();
nowAccount = _C(exchange.GetAccount);
}
if (ok) {
LogProfit(_N(nowAccount.Balance - orgAccount.Balance));
Log("平衡完成", nowAccount);
}
}
var STATE_WAIT_OPEN = 0;
var STATE_WAIT_COVER = 1;
var STATE_WAIT_CLOSE = 2;
var ProfitCount = 0;
var BuyFirst = true;
var IsSupportGetOrder = true;
var LastBusy = 0;
function setBusy() {
LastBusy = new Date();
}
function isTimeout() {
if (MaxIdle <= 0) {
return false;
}
var now = new Date();
if (((now.getTime() - LastBusy.getTime()) / 1000) >= MaxIdle) {
LastBusy = now;
return true;
}
return false;
}
function onexit() {
if (CancelAllWS) {
Log("正在退出, 尝试取消所有挂单");
cancelPending();
}
Log("策略成功停止");
Log(_C(exchange.GetAccount));
}
function fishing(orgAccount, fishCount) {
setBusy();
var account = _C(exchange.GetAccount);
Log(account);
var InitAccount = account;
var ticker = _C(exchange.GetTicker);
var amount = _N(AmountOnce);
var amountB = [amount];
var amountS = [amount];
if (typeof(AmountType) !== 'undefined' && AmountType == 1) {
for (var idx = 0; idx < AllNum; idx++) {
amountB[idx] = BAmountOnce;
amountS[idx] = SAmountOnce;
}
} else {
for (var idx = 1; idx < AllNum; idx++) {
switch (AmountCoefficient[0]) {
case '+':
amountB[idx] = amountB[idx - 1] + parseFloat(AmountCoefficient.substring(1));
break;
case '-':
amountB[idx] = amountB[idx - 1] - parseFloat(AmountCoefficient.substring(1));
break;
case '*':
amountB[idx] = amountB[idx - 1] * parseFloat(AmountCoefficient.substring(1));
break;
case '/':
amountB[idx] = amountB[idx - 1] / parseFloat(AmountCoefficient.substring(1));
break;
}
amountB[idx] = _N(amountB[idx], AmountDot);
amountS[idx] = amountB[idx];
}
}
if (FirstPriceAuto) {
FirstPrice = BuyFirst ? _N(ticker.Buy - PriceGrid, Precision) : _N(ticker.Sell + PriceGrid, Precision);
}
// Initialize fish table
var fishTable = {};
var uuidTable = {};
var needStocks = 0;
var needMoney = 0;
var actualNeedMoney = 0;
var actualNeedStocks = 0;
var notEnough = false;
var canNum = 0;
for (var idx = 0; idx < AllNum; idx++) {
var price = _N((BuyFirst ? FirstPrice - (idx * PriceGrid) : FirstPrice + (idx * PriceGrid)), Precision);
needStocks += amountS[idx];
needMoney += price * amountB[idx];
if (BuyFirst) {
if (_N(needMoney) <= _N(account.Balance)) {
actualNeedMondy = needMoney;
actualNeedStocks = needStocks;
canNum++;
} else {
notEnough = true;
}
} else {
if (_N(needStocks) <= _N(account.Stocks)) {
actualNeedMondy = needMoney;
actualNeedStocks = needStocks;
canNum++;
} else {
notEnough = true;
}
}
fishTable[idx] = STATE_WAIT_OPEN;
uuidTable[idx] = -1;
}
if (!EnableAccountCheck && (canNum < AllNum)) {
Log("警告, 当前资金只可做", canNum, "个网格, 全网共需", (BuyFirst ? needMoney : needStocks), "请保持资金充足");
canNum = AllNum;
}
if (BuyFirst) {
if (EnableProtectDiff && (FirstPrice - ticker.Sell) > ProtectDiff) {
throw "首次买入价比市场卖1价高" + _N(FirstPrice - ticker.Sell, Precision) + ' 元';
} else if (EnableAccountCheck && account.Balance < _N(needMoney)) {
if (fishCount == 1) {
throw "资金不足, 需要" + _N(needMoney) + "元";
} else {
Log("资金不足, 需要", _N(needMoney), "元, 程序只做", canNum, "个网格 #ff0000");
}
} else {
Log('预计动用资金: ', _N(needMoney), "元");
}
} else {
if (EnableProtectDiff && (ticker.Buy - FirstPrice) > ProtectDiff) {
throw "首次卖出价比市场买1价高 " + _N(ticker.Buy - FirstPrice, Precision) + ' 元';
} else if (EnableAccountCheck && account.Stocks < _N(needStocks)) {
if (fishCount == 1) {
throw "币数不足, 需要 " + _N(needStocks) + " 个币";
} else {
Log("资金不足, 需要", _N(needStocks), "个币, 程序只做", canNum, "个网格 #ff0000");
}
} else {
Log('预计动用币数: ', _N(needStocks), "个, 约", _N(needMoney), "元");
}
}
var trader = new Trader();
var OpenFunc = BuyFirst ? exchange.Buy : exchange.Sell;
var CoverFunc = BuyFirst ? exchange.Sell : exchange.Buy;
if (EnableDynamic) {
OpenFunc = BuyFirst ? trader.Buy : trader.Sell;
CoverFunc = BuyFirst ? trader.Sell : trader.Buy;
}
var ts = new Date();
var preMsg = "";
var profitMax = 0;
while (true) {
var now = new Date();
var table = null;
if (now.getTime() - ts.getTime() > 5000) {
if (typeof(GetCommand) == 'function' && GetCommand() == "收网") {
Log("开始执行命令进行收网操作");
balanceAccount(orgAccount, InitAccount);
return false;
}
ts = now;
var nowAccount = _C(exchange.GetAccount);
var ticker = _C(exchange.GetTicker);
if (EnableDynamic) {
trader.Poll(ticker, DynamicMax);
}
var amount_diff = (nowAccount.Stocks + nowAccount.FrozenStocks) - (InitAccount.Stocks + InitAccount.FrozenStocks);
var money_diff = (nowAccount.Balance + nowAccount.FrozenBalance) - (InitAccount.Balance + InitAccount.FrozenBalance);
var floatProfit = _N(money_diff + (amount_diff * ticker.Last));
var floatProfitAll = _N((nowAccount.Balance + nowAccount.FrozenBalance - orgAccount.Balance - orgAccount.FrozenBalance) + ((nowAccount.Stocks + nowAccount.FrozenStocks - orgAccount.Stocks - orgAccount.FrozenStocks) * ticker.Last));
var isHold = Math.abs(amount_diff) >= exchange.GetMinStock();
if (isHold) {
setBusy();
}
profitMax = Math.max(floatProfit, profitMax);
if (EnableAccountCheck && EnableStopLoss) {
if ((profitMax - floatProfit) >= StopLoss) {
Log("当前浮动盈亏", floatProfit, "利润最高点: ", profitMax, "开始止损");
balanceAccount(orgAccount, InitAccount);
if (StopLossMode == 0) {
throw "止损退出";
} else {
return true;
}
}
}
if (EnableAccountCheck && EnableStopWin) {
if (floatProfit > StopWin) {
Log("当前浮动盈亏", floatProfit, "开始止盈");
balanceAccount(orgAccount, InitAccount);
if (StopWinMode == 0) {
throw "止盈退出";
} else {
return true;
}
}
}
var distance = 0;
if (EnableAccountCheck && AutoMove) {
if (BuyFirst) {
distance = ticker.Last - FirstPrice;
} else {
distance = FirstPrice - ticker.Last;
}
var refish = false;
if (!isHold && isTimeout()) {
Log("空仓过久, 开始移动网格");
refish = true;
}
if (distance > MaxDistance) {
Log("价格超出网格区间过多, 开始移动网格, 当前距离: ", _N(distance, Precision), "当前价格:", ticker.Last);
refish = true;
}
if (refish) {
balanceAccount(orgAccount, InitAccount);
return true;
}
}
var holdDirection, holdAmount = "--",
holdPrice = "--";
if (isHold) {
if (RestoreProfit && ProfitAsOrg) {
if (BuyFirst) {
money_diff += LastProfit;
} else {
money_diff -= LastProfit;
}
}
holdAmount = amount_diff;
holdPrice = (-money_diff) / amount_diff;
if (!BuyFirst) {
holdAmount = -amount_diff;
holdPrice = (money_diff) / -amount_diff;
}
holdAmount = _N(holdAmount, 4);
holdPrice = _N(holdPrice, Precision);
holdDirection = BuyFirst ? "多" : "空";
} else {
holdDirection = "--";
}
table = {
type: 'table',
title: '运行状态',
cols: ['动用资金', '持有仓位', '持仓大小', '持仓均价', '总浮动盈亏', '当前网格盈亏', '撒网次数', '网格偏移', '真实委托', '最新币价'],
rows: [
[_N(actualNeedMondy, 4), holdDirection, holdAmount, holdPrice, _N(floatProfitAll, 4) + ' ( ' + _N(floatProfitAll * 100 / actualNeedMondy, 4) + ' % )', floatProfit, fishCount, (AutoMove && distance > 0) ? ((BuyFirst ? "向上" : "向下") + "偏离: " + _N(distance) + " 元") : "--", trader.RealLen(), ticker.Last]
]
};
}
var orders = _C(trader.GetOrders);
if (table) {
if (!EnableDynamic) {
table.rows[0][8] = orders.length;
}
LogStatus('`' + JSON.stringify(table) + '`');
}
for (var idx = 0; idx < canNum; idx++) {
var openPrice = _N((BuyFirst ? FirstPrice - (idx * PriceGrid) : FirstPrice + (idx * PriceGrid)), Precision);
var coverPrice = _N((BuyFirst ? openPrice + PriceDiff : openPrice - PriceDiff), Precision);
var state = fishTable[idx];
var fishId = uuidTable[idx];
if (hasOrder(orders, fishId)) {
continue;
}
if (fishId != -1 && IsSupportGetOrder) {
var order = trader.GetOrder(fishId);
if (!order) {
Log("获取订单信息失败, ID: ", fishId);
continue;
}
if (order.Status == ORDER_STATE_PENDING) {
//Log("订单状态为未完成, ID: ", fishId);
continue;
}
}
if (state == STATE_WAIT_COVER) {
var coverId = CoverFunc(coverPrice, (BuyFirst ? amountS[idx] : amountB[idx]), (BuyFirst ? '完成买单:' : '完成卖单:'), openPrice, '量:', (BuyFirst ? amountB[idx] : amountS[idx]));
if (typeof(coverId) === 'number' || typeof(coverId) === 'string') {
fishTable[idx] = STATE_WAIT_CLOSE;
uuidTable[idx] = coverId;
}
} else if (state == STATE_WAIT_OPEN || state == STATE_WAIT_CLOSE) {
var openId = OpenFunc(openPrice, BuyFirst ? amountB[idx] : amountS[idx]);
if (typeof(openId) === 'number' || typeof(openId) === 'string') {
fishTable[idx] = STATE_WAIT_COVER;
uuidTable[idx] = openId;
if (state == STATE_WAIT_CLOSE) {
ProfitCount++;
var account = _C(exchange.GetAccount);
var ticker = _C(exchange.GetTicker);
var initNet = _N(((InitAccount.Stocks + InitAccount.FrozenStocks) * ticker.Buy) + InitAccount.Balance + InitAccount.FrozenBalance, 8);
var nowNet = _N(((account.Stocks + account.FrozenStocks) * ticker.Buy) + account.Balance + account.FrozenBalance, 8);
var actualProfit = _N(((nowNet - initNet)) * 100 / initNet, 8);
if (AmountType == 0) {
var profit = _N((ProfitCount * amount * PriceDiff) + LastProfit, 8);
Log((BuyFirst ? '完成卖单:' : '完成买单:'), coverPrice, '量:', (BuyFirst ? amountS[idx] : amountB[idx]), '平仓收益', profit);
} else {
Log((BuyFirst ? '完成卖单:' : '完成买单:'), coverPrice, '量:', (BuyFirst ? amountS[idx] : amountB[idx]));
}
}
}
}
}
Sleep(CheckInterval);
}
return true;
}
function main() {
if (ResetData) {
LogProfitReset();
LogReset();
}
exchange.SetMaxDigits(Precision)
if (typeof(AmountType) === 'undefined') {
AmountType = 0;
}
if (typeof(AmountDot) === 'undefined') {
AmountDot = 3;
}
if (typeof(EnableDynamic) === 'undefined') {
EnableDynamic = false;
}
if (typeof(AmountCoefficient) === 'undefined') {
AmountCoefficient = "*1";
}
if (typeof(EnableAccountCheck) === 'undefined') {
EnableAccountCheck = true;
}
BuyFirst = (OpType == 0);
IsSupportGetOrder = exchange.GetName().indexOf('itstamp') == -1;
if (!IsSupportGetOrder) {
Log(exchange.GetName(), "不支持GetOrder, 可能影响策略稳定性.");
}
SetErrorFilter("502:|503:|S_U_001|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|refused|EOF|When");
exchange.SetRate(1);
Log('已经禁用汇率转换, 当前货币为', exchange.GetBaseCurrency());
if (!RestoreProfit) {
LastProfit = 0;
}
var orgAccount = _C(exchange.GetAccount);
var fishCount = 1;
while (true) {
if (!fishing(orgAccount, fishCount)) {
break;
}
fishCount++;
Log("第", fishCount, "次重新撒网...");
FirstPriceAuto = true;
Sleep(1000);
}
}