Esta estratégia é uma estratégia que era aplicável à criptomoeda 796 Futures Exchange há muito tempo. O contrato de futuros é cripto-marginado, ou seja, a margem é deduzida da moeda (por exemplo, em contratos BTC, é deduzida do BTC), e o valor da ordem do contrato pode ser decimal, semelhante ao contrato cripto-marginado da Binance. A estratégia é tomada para estudo de design de estratégia, o processamento de lógica ainda é muito bom, por isso a estratégia é principalmente para aprendizagem.
Notas de código de estratégia:
var FirstTradeType = [ORDER_TYPE_BUY, ORDER_TYPE_SELL][OpType]; // the opening direction of the first trade is determined by the opening direction of the parameter OpType, and the value of the global variable FirstTradeType is ORDER_TYPE_BUY or ORDER_TYPE_SELL
var OrgAccount = null; // the global variable, to record the account assets
var Counter = {s : 0, f: 0}; // declare the variable Counter; its value is initialized as an object (similar structure in Python is called dictionary); "s" represents the number of win, and "f" represents the number of failure
var LastProfit = 0; // the recent profit and loss
var AllProfit = 0; // the total rofit and loss
var _Failed = 0; // the number of stop loss
// Cancel all orders except the order of a certain ID in the pending order list. When the orderId parameter is not passed, all pending orders of the current trading pair are cancelled. The parameter "e" is the reference of the exchange object. For example, import exchange as parameter, "e" is now the alias of exchange
function StripOrders(e, orderId) {
var order = null; // the initialization variable "order" is null
if (typeof(orderId) == 'undefined') { // If the parameter orderId is not written, then typeof(orderId) == 'undefined' is true, and the code block of the if statement is executed, and the orderId is assigned to null
orderId = null;
}
while (true) { // processing loop
var dropped = 0; // the number of processing marks
var orders = _C(e.GetOrders); // call GetOrders to obtain the current pending orders (not executed orders), and assign to orders
for (var i = 0; i < orders.length; i++) { // traverse the list of unexecuted orders "orders"
if (orders[i].Id == orderId) { // if the order ID is the same as the ID in the parameter "orderId", the local variable "order" assign a value to orders[i], which is the current order structure when traversing
order = orders[i];
} else { // if the order ID is not the same as the ID in the parameter "orderId", execute the cancellation operation
var extra = ""; // according to part of executed situation, set the extending information "extra"
if (orders[i].DealAmount > 0) {
extra = "Executed:" + orders[i].DealAmount;
} else {
extra = "Unexecuted:";
}
e.SetDirection(orders[i].Type == ORDER_TYPE_BUY ? "buy" : "sell");
e.CancelOrder(orders[i].Id, orders[i].Type == ORDER_TYPE_BUY ? "buy order" : "sell order", extra); // cancellation operation along with exporting "extra" information, which will be displayed in logs
dropped++; // dropped, accumulated counts
}
}
if (dropped == 0) { // when the traverse is done, "dropped" is equal to 0, namely no cancellation processing during the traverse (which means no order to be canceled); that is to say the cancellation processing is done, and break out of the loop
break;
}
Sleep(300); // prevent over fast rotating frequency; create a time interval
}
return order; // return the order that needs to be found
}
var preMsg = ""; // the variable to record the cached information
function GetAccount(e, waitFrozen) { // to get account asset information; parameter e is also a reference to exchange, and parameter waitFrozen controls whether to wait for freezing
if (typeof(waitFrozen) == 'undefined') { // if the parameter waitFrozen is not passed in the call, assign false to the parameter waitFrozen, that is, the default is no waiting for freezing
waitFrozen = false;
}
var account = null;
var alreadyAlert = false; // the varibale to mark whether it already alerted
while (true) { // to obtain the account information and detect frozen; if no wait for freezing, break the while loop directly
account = _C(e.GetAccount);
if (!waitFrozen || account.FrozenStocks < MinStock) {
break;
}
if (!alreadyAlert) {
alreadyAlert = true; // trigger the alert once, and then reset alreadyAlert to avoid repeating alert
Log("frozen assets or symbols are found in the account", account); // export the alert log
}
Sleep(Interval);
}
msg = "Success: " + Counter.s + " times, failure: " + Counter.f + " times, the current account symbols: " + account.Stocks;
if (account.FrozenStocks > 0) {
msg += " frozen symbols: " + account.FrozenStocks;
}
if (msg != preMsg) { // detect whether the current information is the same as that last time; if not, update the informtaion on the status bar
preMsg = msg;
LogStatus(msg, "#ff0000");
}
return account; // the function returns the account stucture of the account information
}
function GetPosition(e, orderType) { // obtain positions, or obtain the positions with specified direction (orderType)
var positions = _C(e.GetPosition); // obtain positions
if (typeof(orderType) == 'undefined') { // the parameter orderType is to specify the position type; if the parameter is not imported, directly return all positions
return positions;
}
for (var i = 0; i < positions.length; i++) { // traverse the list of positions
if (positions[i].Type == orderType) { // if the traversed current position data is the direction (orderType) we need
return positions[i]; // return positions of orderType
}
}
return null;
}
function GetTicker(e) { // obtain ticker market data
while (true) {
var ticker = _C(e.GetTicker); // obtain tick market data
if (ticker.Buy > 0 && ticker.Sell > 0 && ticker.Sell > ticker.Buy) { // detect the reliability of the market data
return ticker; // return ticker data
}
Sleep(100);
}
}
// mode = 0 : direct buy, 1 : buy as buy1
function Trade(e, tradeType, tradeAmount, mode, slidePrice, maxSpace, retryDelay) { // trading function
// e for exchange object reference, tradeType for trade direction (buy/sell), tradeAmount for the trading amount, mode for trading mode, slidePrice for slippoint price, maxSpace for maximum pending order distance, and retryDelay for retry interval
var initPosition = GetPosition(e, tradeType); // obtain the position data with a specified direction, recorded as initPosition
var nowPosition = initPosition; // declare another variable "nowPosition", assigned by "initPosition
var orderId = null;
var prePrice = 0; // the ordering price in the loop last time
var dealAmount = 0; // executed trading amount
var diffMoney = 0;
var isFirst = true; // mark of initially executing the loop
var tradeFunc = tradeType == ORDER_TYPE_BUY ? e.Buy : e.Sell; // the function of placing orders; according to the parameter tradeType, determine to call e.Buy or e.Sell
var isBuy = tradeType == ORDER_TYPE_BUY; // whether it is the buy mark
while (true) { // while loop
var account = _C(e.GetAccount); // obtain the current account asset data
var ticker = GetTicker(e); // obtain the current market data
var tradePrice = 0; // determine a trading price based on the parameter mode
if (isBuy) {
tradePrice = _N((mode == 0 ? ticker.Sell : ticker.Buy) + slidePrice, 4);
} else {
tradePrice = _N((mode == 0 ? ticker.Buy : ticker.Sell) - slidePrice, 4);
}
if (orderId == null) {
if (isFirst) { // according to the mark variable isFirst to judge, if it is the first execution, nothing will be done
isFirst = false; // if the mark isFirst is set to false, it means it is not the first execution
} else { // not the first executin, so update the position data
nowPosition = GetPosition(e, tradeType);
}
dealAmount = _N((nowPosition ? nowPosition.Amount : 0) - (initPosition ? initPosition.Amount : 0), 6); // by the initial position data and the current position data, calculate the executed amount
var doAmount = Math.min(tradeAmount - dealAmount, account.Stocks * MarginLevel, 4); // by the executed amount and the account available assets, calculate the rest amount that needs to be traded
if (doAmount < MinStock) { // if the calculated trading amount is less than the minimum of the executed amount, terminate the logic and break the while loop
break;
}
prePrice = tradePrice; // cache the trading price in the current round of loop
e.SetDirection(tradeType == ORDER_TYPE_BUY ? "buy" : "sell"); // set futures trading direction
orderId = tradeFunc(tradePrice, doAmount); // trade of placing orders; its parameters are the calculated price and the order amount of this time
} else { // when the order recording variable "orderId" is not null, it means the orders has been placed
if (mode == 0 || Math.abs(tradePrice - prePrice) > maxSpace) { // if it is the mode of pending orders, the current price and the price cached last time exceed the maximum pending order range
orderId = null; // reset orderId to null, and restart to place orders in next round of loop
}
var order = StripOrders(exchange, orderId); // call StripOrders to find the order with ID of orderId in the pending order list
if (order == null) { // if not found, reset orderId to null, and continue the next round of placing orders
orderId = null;
}
}
Sleep(retryDelay); // temporarily specify a certain time, to control the loop frequency
}
if (dealAmount <= 0) { // after the while loop is finished, if the executed amount "dealAmount" is less than or equal to 0, that means the trade is a failure, and return null
return null;
}
return nowPosition; // under normal circumstances, return the latest position data
}
function coverFutures(e, orderType) { // function of closing positions
var coverAmount = 0; // declare a variable coverAmount, with the default of 0, to record the amount already closed
while (true) {
var positions = _C(e.GetPosition); // obtain positions
var ticker = GetTicker(e); // obtain the current market quotes
var found = 0; // search for marks
for (var i = 0; i < positions.length; i++) { // traverse the array of holding positions, namely "positions"
if (positions[i].Type == orderType) { // find the positions that are needed
if (coverAmount == 0) {
coverAmount = positions[i].Amount; // initially record the position amount, namely the amount to be closed
}
if (positions[i].Type == ORDER_TYPE_BUY) { // execute the operation of closing positions, according to position type
e.SetDirection("closebuy"); // set the futures trading direction
e.Sell(ticker.Buy, positions[i].Amount); // function of placing orders
} else {
e.SetDirection("closesell");
e.Buy(ticker.Sell, positions[i].Amount);
}
found++; // mark the accumulation
}
}
if (found == 0) { // if the mark variable found is 0, it means no position to be processed, and then break out of the while loop
break;
}
Sleep(2000); // interval time of 2 seconds
StripOrders(e); // cancel all pending orders
}
return coverAmount; // return the amount of closing positions
}
function loop(pos) {
var tradeType = null; // initialize the trading direction
if (typeof(pos) == 'undefined' || !pos) { //judge whether it is the initial round of execution
tradeType = FirstTradeType;
pos = Trade(exchange, tradeType, OpAmount, OpMode, SlidePrice, MaxSpace, Interval); // initial trade
if (!pos) {
throw "Rough start, fail to open positions";
} else {
Log(tradeType == ORDER_TYPE_BUY ? "opening long positions completed" : "Opening short positions completed", "Average price:", pos.Price, "Amount:", pos.Amount);
}
} else {
tradeType = pos.Type; // continue to specify the trading direction according to position direction
}
var holdPrice = pos.Price; // position price
var holdAmount = pos.Amount; // position amount
var openFunc = tradeType == ORDER_TYPE_BUY ? exchange.Buy : exchange.Sell; // long positions; open positions to buy; if not, open positions to sell
var coverFunc = tradeType == ORDER_TYPE_BUY ? exchange.Sell : exchange.Buy; // long positions; close positions to sell; if not, close positions to buy
var reversePrice = 0; // reverse price
var coverPrice = 0; // closing position price
var canOpen = true; // mark for enabling opening positions
if (tradeType == ORDER_TYPE_BUY) {
reversePrice = _N(holdPrice * (1 - StopLoss), 4); // stop loss price
coverPrice = _N(holdPrice * (1 + StopProfit), 4); // take profit price
} else {
reversePrice = _N(holdPrice * (1 + StopLoss), 4);
coverPrice = _N(holdPrice * (1 - StopProfit), 4);
}
var coverId = null;
var msg = "position price:" + holdPrice + " stopLoss price:" + reversePrice;
for (var i = 0; i < 10; i++) { // control to order 10 times maximum
if (coverId) { // when the order ID is null, and break will not be triggered, continue the loop, until to the maximum of 10 times
break;
}
if (tradeType == ORDER_TYPE_BUY) { // according to the order direction, pend orders to close, namely the take profit orders
exchange.SetDirection("closebuy");
coverId = exchange.Sell(coverPrice, holdAmount, msg);
} else {
exchange.SetDirection("closesell");
coverId = exchange.Buy(coverPrice, holdAmount, msg);
}
Sleep(Interval);
}
if (!coverId) { // raise the error after 10 times of order failure, and stop the strategy
StripOrders(exchange); // cancel all pending orders
Log("fail to order", "@") // add push alert
throw "fail to order"; // raise the error, and stop the bot
}
while (true) { // enter to detect the reverse loop
Sleep(Interval);
var ticker = GetTicker(exchange); // obtain the latest market quotes
if ((tradeType == ORDER_TYPE_BUY && ticker.Last < reversePrice) || (tradeType == ORDER_TYPE_SELL && ticker.Last > reversePrice)) { // detect the trigger of stoploss, namely reverse
StripOrders(exchange); // cancel all pending orders
var coverAmount = coverFutures(exchange, tradeType); // close all positions
if (_Failed >= MaxLoss) { // if the maximum number of stoploss (reverse times) is exceeded, break the loop and restart again
Counter.f++; // counted as one failure
Log("exceed the maximum number of failures", MaxLoss);
break; // break out of the loop
}
var reverseAmount = _N(coverAmount * ReverseRate, 4); // according to the closing position amount, double the trading amount
var account = GetAccount(exchange, true); // update the account information, and here frozen assets are forbidden
// detect whether the assets are enough; if not, break the loop, and restart, such as _Failed >= MaxLoss
if (_N(account.Stocks * MarginLevel, 4) < reverseAmount) { // detect whether the assets are enough
Log("open positions without currency, need to open positions:", reverseAmount, "currency symbols, and there are only ", account.Stocks, "currency symbols");
Counter.f++;
break;
}
var reverseType = tradeType; // record the type of the reverse operation; the default is forward
if (ReverseMode == 0) { // the adjustment of the reverse mode, that is, if the parameter is set to reverse, the adjustment is needed here
reverseType = tradeType == ORDER_TYPE_BUY ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; // reverse indicates if the positions just now are long, the reverse now will do short; if the positions just now are short, the reverse now will do long
}
var pos = Trade(exchange, reverseType, reverseAmount, OpMode, SlidePrice, MaxSpace, Interval); // double the operation of placing orders
if (pos) { // detect the positions after the execution of the trading logic
Log(reverseType == ORDER_TYPE_BUY ? "long position" : "short position", "double open positions completed");
}
return pos; // return the position structure
} else { // the trading logic executed when the reverse is not triggered
var orders = _C(exchange.GetOrders); // when the take-profit pending orders are executed, add 1 to the number of win
if (orders.length == 0) {
Counter.s++;
var account = GetAccount(exchange, true); // update the account assets
LogProfit(account.Stocks, account); // print the account assets
break;
}
}
}
// if the return is normal in a non-while loop, null is returned, such as successful take-profit, exceeding the number of failures, insufficient assets
return null;
}
function onexit() { // when the bot is stopped, execute the onexit function
StripOrders(exchange); // cancel all pending orders
Log("Exit");
}
function main() {
if (exchange.GetName().indexOf("Futures") == -1) { // detect whether the currently first added exchange object is a futures platform
throw "only support futures, and spot not supported temporarily";
}
// EnableLogLocal(SaveLocal);
if (exchange.GetRate() != 1) { // disable the exchange rateconverting
Log("disable exchange rate converting");
exchange.SetRate(1);
}
StopProfit /= 100; // the parameter is processed to be decimal; assuming that StopProfit is 1, namely 1% of take-profit, recalculate the assignment and the value of StopProfit is 0.01, namely 1%.
StopLoss /= 100; // stop loss (reverse), as above
var eName = exchange.GetName();
if (eName == "Futures_CTP") { // detect whether the currently first added exchange object is commodity futures ; if it is, raise an error to stop the bot
throw "temporarily only support cryptocurrency futures"
}
exchange.SetContractType(Symbol); // set the code of the cryptocurrency contract, namely the contract to be traded and operated
exchange.SetMarginLevel(MarginLevel); // set margin level
Interval *= 1000; // change the polling interval parameter from second to millisecond
SetErrorFilter("502:|503:|unexpected|network|timeout|WSARecv|Connect|GetAddr|no such|reset|http|received|EOF"); // set the type of the error filtered
StripOrders(exchange); // cancel all pending orders
OrgAccount = GetAccount(exchange, true); // obtain the current account information
LogStatus("start successfully"); // update the status bar information
var pos = null; // initialize the variable pos to null in the main function, to record the position data structure
var positions = GetPosition(exchange); // obtaining the current positions is to call the encapsulated GetPosition without orderType, to obtain all positions held; note that here the called function is not "exchange.GetPosition" of API interface
if (positions.length == 1) { // if there are positions at the beginning, assign the value to the variable pos
pos = positions[0];
Log("found 1 position, and already rcovered the process automatically");
} else if (positions.length > 1) { // when there are multiple positions, that is, the status in which the strategy cannot run, the strategy raises an error to make the bot stop
throw "found over 1 position ";
}
while (true) { // strategy main loop
pos = loop(pos); // execute the main function loop in the trading logic; take pos as parameter; return the new position data structure
if (!pos) { // under the circumstance if the condition is triggered, and return null, such as the success of take-profit, exceeding the number of failures, and inadequate assets
_Failed = 0; // reset the number of stop loss to 0, and restart it
} else {
_Failed++; // the accumulated number of stop loss
}
Sleep(Interval);
}
}
Depois de ler o código da estratégia, você pode descobrir que a lógica da estratégia não é complicada, e o código não é muito, mas o design é engenhoso e muitas partes dele podem ser usadas como referência.
A função principal da lógica de negociação da estratégia é a de:loop
função, que é chamada repetidamente no loop principal domain
Quando oloop
A função inicia, primeiro colocar ordens para manter posições, e depois pendurar as ordens para tirar lucro e esperar que as ordens de stop-profit sejam executadas.
A lógica da estratégia é simplesmente descrita como tal, mas ainda há alguns outros detalhes, como a definição do número máximo de reversão, a detecção dos ativos disponíveis da conta e o processamento do número máximo de falhas de 10 vezes.
Algumas funções da estratégia são concebidas para funcionar de forma diferente de acordo com diferentes parâmetros, tais como:StripOrders
função,GetAccount
função,GetPosition
Estas funções têm diferentes ações dependendo das diferenças dos parâmetros importados. Isso faz bom uso do código e evita a redundância do código e torna o design da estratégia conciso e fácil de entender.
Estratégia original:https://www.fmz.com/strategy/3648
Dobrar o inverso tem certos riscos, especialmente em futuros; a estratégia é apenas para estudo, e por favor, use-a com cautela.