It was the Bitcointalk forum that inspired us to create Bitcointalksearch.org - Bitcointalk is an excellent site that should be the default page for anybody dealing in cryptocurrency, since it is a virtual gold-mine of data. However, our experience and user feedback led us create our site; Bitcointalk's search is slow, and difficult to get the results you need, because you need to log in first to find anything useful - furthermore, there are rate limiters for their search functionality.
The aim of our project is to create a faster website that yields more results and faster without having to create an account and eliminate the need to log in - your personal data, therefore, will never be in jeopardy since we are not asking for any of your data and you don't need to provide them to use our site with all of its capabilities.
We created this website with the sole purpose of users being able to search quickly and efficiently in the field of cryptocurrency so they will have access to the latest and most accurate information and thereby assisting the crypto-community at large.
var 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));
}
}
function 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);
}
}