এই কৌশলটি এমন একটি কৌশল যা দীর্ঘদিন আগে ক্রিপ্টোকারেন্সি 796 ফিউচার এক্সচেঞ্জে প্রযোজ্য ছিল। ফিউচার চুক্তিটি ক্রিপ্টো-মার্জিনযুক্ত, অর্থাৎ, মার্জিনটি মুদ্রা থেকে কেটে নেওয়া হয় (উদাহরণস্বরূপ, বিটিসি চুক্তিতে এটি বিটিসি থেকে কেটে নেওয়া হয়), এবং চুক্তি আদেশের পরিমাণ দশমিক হতে পারে, বাইনারেন্স ক্রিপ্টো-মার্জিনযুক্ত চুক্তির মতো। কৌশলটি কৌশল নকশা গবেষণার জন্য নেওয়া হয়, লজিক প্রসেসিং এখনও খুব ভাল, তাই কৌশলটি মূলত শেখার জন্য।
কৌশল কোড নোটঃ
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);
}
}
কৌশল কোডটি পড়ার পর আপনি দেখতে পাবেন যে কৌশল যুক্তি জটিল নয়, এবং কোডটি খুব বেশি নয়, কিন্তু নকশাটি উদ্ভাবনী এবং এর অনেক অংশ রেফারেন্সের জন্য ব্যবহার করা যেতে পারে।
কৌশলগত ট্রেডিং লজিকের প্রধান কাজ হলloop
ফাংশন, যা বারবার প্রধান লুপ মধ্যে কল করা হয়main
ফাংশন.loop
ফাংশনটি শুরু হয়, প্রথমে অবস্থানগুলি ধরে রাখার অর্ডারগুলি স্থাপন করুন এবং তারপরে লাভ নেওয়ার অর্ডারগুলি অপেক্ষা করুন এবং স্টপ-লাভ অর্ডারগুলি কার্যকর হওয়ার জন্য অপেক্ষা করুন। তারপরে, নিম্নলিখিত দুটি সামগ্রী সনাক্ত করতে সনাক্তকরণের অবস্থা প্রবেশ করুন।
কৌশল যুক্তিটি কেবলমাত্র যেমন বর্ণনা করা হয়েছে, তবে আরও কিছু বিবরণ রয়েছে, যেমন সর্বোচ্চ সংখ্যক বিপরীতের সেটিং, উপলব্ধ অ্যাকাউন্টের সম্পদ সনাক্তকরণ এবং সর্বোচ্চ সংখ্যক ব্যর্থতার 10 গুণ পর্যন্ত প্রক্রিয়াজাতকরণ।
কৌশলটির কিছু ফাংশন বিভিন্ন পরামিতি অনুযায়ী ভিন্নভাবে সম্পাদন করার জন্য ডিজাইন করা হয়েছে, যেমনঃStripOrders
ফাংশন,GetAccount
ফাংশন,GetPosition
এই ফাংশনগুলি আমদানি করা পরামিতিগুলির পার্থক্যের উপর নির্ভর করে বিভিন্ন ক্রিয়াকলাপ রয়েছে। এটি কোডের ভাল ব্যবহার করে, এবং কোড রিডান্ডান্সি এড়ায়, এবং কৌশল নকশা সংক্ষিপ্ত এবং সহজেই বোঝা যায়।
মূল কৌশল:https://www.fmz.com/strategy/3648
দ্বিগুণ বিপরীত কিছু ঝুঁকি আছে, বিশেষ করে ফিউচার; কৌশল শুধুমাত্র অধ্যয়ন জন্য, এবং সতর্কতার সাথে এটি ব্যবহার করুন। আলোচনা জন্য বার্তা ছেড়ে স্বাগত জানাই।