From Quantitative Trading to Asset Management - CTA Strategy Development for Absolute Return (4)

in fmz •  last year 

Next, we need to fill in the necessary detail code in the strategy framework according to the actual transaction process and transaction details.

I. Pre-processing before transaction

1. Declare the necessary global variables in the global scope.

  • Declare a chart object for the configuration chart
var chart = {}
  • Call Chart function and initialize the chart
var ObjChart = Chart ( chart )
  • Declare an empty array to store price difference series
var bars = []
  • Declare a record history data timestamp variable
var oldTime = 0

2. Configure the external parameters of the strategy.

var tradeTypeA = "this_week"; // Arbitrage A Contract
var tradeTypeB = "quarter"; // Arbitrage B Contract
var dataLength = 10; // Indicator period length
var timeCycle = 1; // K-line period
var name = "ETC"; // Currencies
var unit = 1; // Order quantity

3. Define the data processing function

  • Basic data function: Data()
    Create a constructor, Data, and define its internal properties. Including: account data, position data, K-line data timestamp, buy/sell price of arbitrage A/B contract, and positive/negative arbitrage price difference.
function Data(tradeTypeA, tradeTypeB) { // Pass in arbitrage A contract and arbitrage B contract
    this.accountData = _C(exchange.GetAccount); // Get account information
    this.positionData = _C(exchange.GetPosition); // Get position information
    var recordsData = _C(exchange.GetRecords); // Get K-line data
    exchange.SetContractType(tradeTypeA); // Subscription arbitrage A contract
    var depthDataA = _C(exchange.GetDepth); // Depth data of arbitrage A contract
    exchange.SetContractType(tradeTypeB); // Subscription arbitrage B contract
    var depthDataB = _C(exchange.GetDepth); // Depth data of arbitrage B contract
    this.time = recordsData[recordsData.length - 1].Time; // Time of obtaining the latest data
    this.askA = depthDataA.Asks[0].Price; // Sell one price of Arbitrage A contract
    this.bidA = depthDataA.Bids[0].Price; // Buy one price of Arbitrage A contract
    this.askB = depthDataB.Asks[0].Price; // Sell one price of Arbitrage B contract
    this.bidB = depthDataB.Bids[0].Price; // Buy one price of Arbitrage B contract
    // Positive arbitrage price differences (Sell one price of contract A - Buy one price of contract B)
    this.basb = depthDataA.Asks[0].Price - depthDataB.Bids[0].Price;
    // Negative arbitrage price differences (Buy one price of contract A - Sell one price of contract B)
    this.sabb = depthDataA.Bids[0].Price - depthDataB.Asks[0].Price;
}
  • Get the position function: mp ( )
    Traverse the entire position array and return the position quantity of the specified contract and direction. If not, return false.
Data.prototype.mp = function (tradeType, type) {
    var positionData = this.positionData; // Get position information
    for (var i = 0; i < positionData.length; i++) {
        if (positionData[i].ContractType == tradeType) {
            if (positionData[i].Type == type) {
                if (positionData[i].Amount > 0) {
                    return positionData[i].Amount;
                }
            }
        }
    }
    return false;
}
  • K-line and indicator function: boll()
    A new K-line sequence is synthesized according to the positive arbitrage/negative arbitrage price difference data. The data of upper track, middle track and lower track calculated by the boll indicator are returned.
Data.prototype.boll = function (num, timeCycle) {
    var self = {}; // Temporary objects
    // Median value of positive arbitrage price difference and negative arbitrage price difference
    self.Close = (this.basb + this.sabb) / 2;
    if (this.timeA == this.timeB) {
        self.Time = this.time;
    } // Compare two depth data timestamps
    if (this.time - oldTime > timeCycle * 60000) {
        bars.push(self);
        oldTime = this.time;
    } // Pass in the price difference data object into the K-line array according to the specified time period
    if (bars.length > num * 2) {
        bars.shift(); // Control the length of the K-line array
    } else {
        return;
    }
    var boll = TA.BOLL(bars, num, 2); // Call the boll indicator in the talib library
    return {
        up: boll[0][boll[0].length - 1], // boll indicator upper track
        middle: boll[1][boll[1].length - 1], // boll indicator middle track
        down: boll[2][boll[2].length - 1] // boll indicator down track
    } // Return a processed boll indicator data
}
  • Order function: trade()
    Pass in the order contract name and order type, then place the order with consideration, and return the result after placing the order. Since it is necessary to place two orders in different directions at the same time, the buy/sell one price is converted within the function according to the contract name of the order.
Data.prototype.trade = function (tradeType, type) {
    exchange.SetContractType(tradeType); // Resubscribe to a contract before placing an order
    var askPrice, bidPrice;
    if (tradeType == tradeTypeA) { // If the order is placed in contract A
        askPrice = this.askA; // set askPrice
        bidPrice = this.bidA; // set bidPrice
    } else if (tradeType == tradeTypeB) { // If the order is placed in contract B
        askPrice = this.askB; // set askPrice
        bidPrice = this.bidB; // set bidPrice
    }
    switch (type) { // Match order placement mode
        case "buy":
            exchange.SetDirection(type); // Set order placement mode
            return exchange.Buy(askPrice, unit);
        case "sell":
            exchange.SetDirection(type); // Set order placement mode
            return exchange.Sell(bidPrice, unit);
        case "closebuy":
            exchange.SetDirection(type); // Set order placement mode
            return exchange.Sell(bidPrice, unit);
        case "closesell":
            exchange.SetDirection(type); // Set order placement mode
            return exchange.Buy(askPrice, unit);
        default:
            return false;
    }
}
  • Cancel Order Function: cancelOrders()
    Get an array of all outstanding orders and cancel them one by one. In addition, false is returned if there is an unfilled order, and true is returned if there is no unfilled order.
Data.prototype.cancelOrders = function () {
    Sleep(500); // Delay before cancellation, because some exchanges, you know what I mean
    var orders = _C(exchange.GetOrders); // Get an array of unfilled orders
    if (orders.length > 0) { // If there are unfilled orders
        for (var i = 0; i < orders.length; i++) { // Iterate through the array of unfilled orders
            exchange.CancelOrder(orders[i].Id); // Cancel unfilled orders one by one
            Sleep(500); // Delay 0.5 seconds
        }
        return false; // Return false if an unfilled order is cancelled
    }
    return true; // Return true if there are no unfilled orders
}
  • Handle holding a single contract: isEven()
    In the case of a single leg in the arbitrage transaction, we will simply close all positions. Of course, it can also be changed to the tracking method.
Data.prototype.isEven = function () {
    var positionData = this.positionData; // Get position information
    var type = null; // Switch position direction
    // If the remaining 2 of the position array length is not equal to 0 or the position array length is not equal to 2
    if (positionData.length % 2 != 0 || positionData.length != 2) {
        for (var i = 0; i < positionData.length; i++) { // Iterate through the position array
            if (positionData[i].Type == 0) { // If it is a long order
                type = 10; // Set order parameters
            } else if (positionData[i].Type == 1) { // If it is a short order
                type = -10; // Set order parameters
            }
            // Close all positions
            this.trade(positionData[i].ContractType, type, positionData[i].Amount);
        }
    }
}
  • Drawing function: drawingChart ( )
    Call ObjChart Add () method, draw the necessary market data and indicator data in the chart: upper track, middle track, lower track, positive/negative arbitrage price difference.
Data.prototype.drawingChart = function (boll) {
    var nowTime = new Date().getTime();
    ObjChart.add([0, [nowTime, boll.up]]);
    ObjChart.add([1, [nowTime, boll.middle]]);
    ObjChart.add([2, [nowTime, boll.down]]);
    ObjChart.add([3, [nowTime, this.basb]]);
    ObjChart.add([4, [nowTime, this.sabb]]);
    ObjChart.update(chart);
}

4. In the entry function main(), execute the pre-transaction pre-processing code, which will only run once after the program is started, including:

  • SetErrorFilter ( ) to filter the unimportant information in the console
  • exchange.IO ( ) to set the digital currency to be traded
  • ObjChart.reset ( ) to clear the previous chart drawn before starting the program
  • LogProfitReset ( ) to clear the status bar information before starting the program

After the above pre-transaction pre-processing is defined, the next step is to enter the polling mode and execute the onTick() function repeatedly. It also sets the sleep time for Sleep () polling, because the API of some digital currency exchanges has built-in access limit for a certain period of time.

function main() {
    // Filter the unimportant information in the console
    SetErrorFilter("429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP");
    exchange.IO("currency", name + '_USDT'); // Set the digital currency to be traded
    ObjChart.reset(); // Clear the previous chart drawn before starting the program
    LogProfitReset(); // Clear the status bar information before starting the program
    while (true) { // Enter the polling mode
        onTick(); // Execute onTick function
        Sleep(500); // Sleep for 0.5 seconds
    }
}

II. Get and calculate data

  1. Obtain basic data object, account balance, and boll indicator data for use in the trading logic.
function onTick() {
    var data = new Data(tradeTypeA, tradeTypeB); // Create a basic data object
    var accountStocks = data.accountData.Stocks; // Account balance
    var boll = data.boll(dataLength, timeCycle); // Get boll indicator data
    if (!boll) return; // Return if there is no boll data
}

III. Place an order and handle the follow-up

  1. Execute the buying and selling operation according to the above strategic logic. First, judge whether the price and indicator conditions are valid, then judge whether the position conditions are valid, and finally execute the trade () order function.
// Explanation of the price difference
// basb = (Sell one price of contract A - Buy one price of contract B)
// sabb = (Buy one price of contract A - Sell one price of contract B)
if (data.sabb > boll.middle && data.sabb < boll.up) { // If sabb is higher than the middle track
    if (data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order
        data.trade(tradeTypeA, "closebuy"); // Contract A closes long position
    }
    if (data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order
        data.trade(tradeTypeB, "closesell"); // Contract B closes short position
    }
} else if (data.basb < boll.middle && data.basb > boll.down) { // If basb is lower than the middle track
    if (data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order
        data.trade(tradeTypeA, "closesell"); // Contract A closes short position
    }
    if (data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order
        data.trade(tradeTypeB, "closebuy"); // Contract B closes long position
    }
}
if (accountStocks * Math.max(data.askA, data.askB) > 1) { // If there is balance in the account
    if (data.basb < boll.down) { // If basb price difference is lower than the down track
        if (!data.mp(tradeTypeA, 0)) { // Check whether contract A has long orders before placing an order
            data.trade(tradeTypeA, "buy"); // Contract A opens long position
        }
        if (!data.mp(tradeTypeB, 1)) { // Check whether contract B has short orders before placing an order
            data.trade(tradeTypeB, "sell"); // Contract B opens short position
        }
    } else if (data.sabb > boll.up) { // If sabb price difference is higher than the upper track
        if (!data.mp(tradeTypeA, 1)) { // Check whether contract A has short orders before placing an order
            data.trade(tradeTypeA, "sell"); // Contract A opens short position
        }
        if (!data.mp(tradeTypeB, 0)) { // Check whether contract B has long orders before placing an order
            data.trade(tradeTypeB, "buy"); // Contract B opens long position
        }
    }
}

To be continued...

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!