0bps

Strategy vault / Other

Teaching Strategy TCI Strategy Mai Language Transplantation

JavaScript strategy by 发明者量化-小小梦.

JavaScriptOtherEMASMA
Author
发明者量化-小小梦
Complexity
Advanced · 356 lines
Parameters
0
Backtest bar
1d · swing
Tested on
BTC/USD · futures

The original spec has no written description. Read the source below.

Source JavaScript

/*backtest
start: 2019-05-01 00:00:00
end: 2019-11-12 00:00:00
period: 1d
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
*/

/* Original Mai Language strategy
N1:=10;
N2:=21;
AP:=(HIGH+LOW+CLOSE)/3;
ESA:=EMA(AP,N1);
D:=EMA(ABS(AP-ESA),N1);
CI:=(AP-ESA)/(0.015*D);
TCI:=EMA(CI,N2);
WT1:TCI;
WT2:SMA(WT1,4,1);
AA:=CROSS(WT1,WT2);
BB:=CROSSDOWN(WT1,WT2);
REF(AA,1),BPK;
REF(BB,1),SPK;
*/

// Global Variables
var IDLE = 0
var LONG = 1
var SHORT = 2
var OPENLONG = 3
var OPENSHORT = 4
var COVERLONG = 5
var COVERSHORT = 6  

var BREAK = 9
var SHOCK = 10  

var _State = IDLE
var Amount = 1                 // Record Position Quantity
var TradeInterval = 500        // Polling Interval
var PriceTick = 1              // Price jumps
var Point = 5                  // Slip price points
var Symbol = "quarter"  

// Temporary parameters
var N1 = 10
var N2 = 21

function CalcAP (r) {   // AP:=(HIGH+LOW+CLOSE)/3;
    var arrAP = []

    for (var i = 0; i < r.length; i++) {
        v = (r[i].High + r[i].Low + r[i].Close) / 3
        arrAP.push(v)
    }

    return arrAP
}

function CalcESA (ap, n1) {   // ESA:=EMA(AP,N1);
    if (ap.length <= n1) {
        return false
    }

    return TA.EMA(ap, n1)
}

function CalcD (ap, esa, n1) {    // D:=EMA(ABS(AP-ESA),N1);
    var arrABS_APminusESA = []
    if (ap.length != esa.length) {
        throw "ap.length != esa.length"
    }

    for (var i = 0; i < ap.length; i++) {
        if (ap[i] && esa[i] && !isNaN(ap[i]) && !isNaN(esa[i])) {
            v = Math.abs(ap[i] - esa[i])
            arrABS_APminusESA.push(v)
        } else {
            arrABS_APminusESA.push(NaN)
        }
    }

    if (arrABS_APminusESA.length <= n1) {
        return false
    }

    return TA.EMA(arrABS_APminusESA, n1)
}

function CalcCI (ap, esa, d) {    // CI:=(AP-ESA)/(0.015*D);
    var arrCI = []
    if (ap.length != esa.length || ap.length != d.length) {
        throw "ap.length != esa.length || ap.length != d.length"
    }
    for (var i = 0; i < ap.length; i++) {
        if (ap[i] && esa[i] && d[i] && !isNaN(ap[i]) && !isNaN(esa[i]) && !isNaN(d[i])) {
            v = (ap[i] - esa[i]) / (0.015 * d[i])
            arrCI.push(v)
        } else {
            arrCI.push(NaN)
        }
    }

    if (arrCI.length == 0) {
        return false
    }

    return arrCI
}

function CalcTCI (ci, n2) {   // TCI:=EMA(CI,N2);
    if (ci.length <= n2) {
        return false
    }

    return TA.EMA(ci, n2)
}

function SMA (arr, n, m) {
    var sma = []
    var currSMA = null
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] && !isNaN(arr[i])) {
            if (!currSMA) {
                currSMA = arr[i]
                sma.push(currSMA)
                continue
            }

            // [M*C2+(N-M)*S1]/N
            currSMA = (m * arr[i] + (n - m) * currSMA) / n
            sma.push(currSMA)
        } else {
            sma.push(NaN)
        }
    }

    return sma
}

function CalcWT2 (wt1) {    // WT2:SMA(WT1,4,1);
    if (wt1.length <= 4) {
        return false 
    }

    return SMA(wt1, 4, 1)
}

var isOK = true
var preTime = 0
function OnTick(){
    // Market handling part of the driving strategy
    var records = _C(exchange.GetRecords)
    if (records[records.length - 1].Time == preTime) {
        if (isOK) {
            Sleep(500)
            return 
        }
    } else {
        preTime = records[records.length - 1].Time
    }

    // Calculate indicator
    // AP
    var ap = CalcAP(records)

    // ESA
    var esa = CalcESA(ap, N1)
    if (!esa) {
        return 
    }

    // D
    var d = CalcD(ap, esa, N1)
    if (!d) {
        return 
    }

    // CI
    var ci = CalcCI(ap, esa, d)
    if (!ci) {
        return 
    }

    // TCI
    var tci = CalcTCI(ci, N2)
    if (!tci) {
        return 
    }

    // WT1
    var wt1 = tci

    // WT2 
    var wt2 = CalcWT2(wt1)
    if (!wt2) {
        return 
    }

    // Part for handling triggered trading signals
    /*
    AA:=CROSS(WT1,WT2);
    BB:=CROSSDOWN(WT1,WT2);
    REF(AA,1),BPK;
    REF(BB,1),SPK;
    */
    if (wt1.length < 3 || wt2.length < 3) {
        return 
    }

    $.PlotLine("wt1", wt1[wt1.length - 2], records[records.length - 2].Time)
    $.PlotLine("wt2", wt2[wt2.length - 2], records[records.length - 2].Time)

    if ((_State == IDLE || _State == SHORT) && wt1[wt1.length - 4] < wt2[wt2.length - 4] && wt1[wt1.length - 3] > wt2[wt2.length - 3]) {
        if (_State == IDLE) {
            _State = OPENLONG
            Log("OPENLONG")    // Test
        }
        if (_State == SHORT) {
            _State = COVERSHORT
            Log("COVERSHORT")  // Test
        }
        isOK = false  
    }

    if ((_State == IDLE || _State == LONG) && wt1[wt1.length - 4] > wt2[wt2.length - 4] && wt1[wt1.length - 3] < wt2[wt2.length - 3]) {
        if (_State == IDLE) {
            _State = OPENSHORT
            Log("OPENSHORT")  // Test
        }
        if (_State == LONG) {
            _State = COVERLONG
            Log("COVERLONG")  // Test
        }
        isOK = false   
    }

    // Execute transaction logic
    var pos = null
    var price = null
    var currBar = records[records.length - 1]
    if(_State == OPENLONG){
        pos = GetPosition(PD_LONG)
        // Determine whether the status is satisfied, and if so, modify the status
        if(pos[1] >= Amount){
            _State = LONG
            Amount = pos[1]   // Update Actual Quantity
            isOK = true 
            return
        }
        price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * Point
        Trade(OPENLONG, price, Amount - pos[1], pos, PriceTick)                // (Type, Price, Amount, CurrPos, PriceTick)
    }  

    if(_State == OPENSHORT){
        pos = GetPosition(PD_SHORT)
        if(pos[1] >= Amount){
            _State = SHORT
            Amount = pos[1]   // Update Actual Quantity
            isOK = true 
            return
        }
        price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * Point
        Trade(OPENSHORT, price, Amount - pos[1], pos, PriceTick)
    }  

    if(_State == COVERLONG){
        pos = GetPosition(PD_LONG)
        if(pos[1] == 0){
            _State = IDLE
            return
        }
        price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * Point
        Trade(COVERLONG, price, pos[1], pos, PriceTick)
    }
    
    if(_State == COVERSHORT){
        pos = GetPosition(PD_SHORT)
        if(pos[1] == 0){
            _State = IDLE
            return
        }
        price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * Point
        Trade(COVERSHORT, price, pos[1], pos, PriceTick)
    }
}  

// Transaction logic part
function GetPosition(posType) {
    var positions = _C(exchange.GetPosition)
    var count = 0
    for(var j = 0; j < positions.length; j++){
        if(positions[j].ContractType == Symbol){
            count++
        }
    }  

    if(count > 1){
        throw "positions error:" + JSON.stringify(positions)
    }  

    for (var i = 0; i < positions.length; i++) {
        if (positions[i].ContractType == Symbol && positions[i].Type === posType) {
            return [positions[i].Price, positions[i].Amount];
        }
    }
    Sleep(TradeInterval);
    return [0, 0];
}  

function CancelPendingOrders() {
    while (true) {
        var orders = _C(exchange.GetOrders)
        for (var i = 0; i < orders.length; i++) {
            exchange.CancelOrder(orders[i].Id);
            Sleep(TradeInterval);
        }
        if (orders.length === 0) {
            break;
        }
    }
}  

function Trade(Type, Price, Amount, CurrPos, OnePriceTick){    // Process transactions
    if(Type == OPENLONG || Type == OPENSHORT){                 // Processing position opening
        exchange.SetDirection(Type == OPENLONG ? "buy" : "sell")
        var pfnOpen = Type == OPENLONG ? exchange.Buy : exchange.Sell
        var idOpen = pfnOpen(Price, Amount, CurrPos, OnePriceTick, Type)
        Sleep(TradeInterval)
        if(idOpen) {
            exchange.CancelOrder(idOpen)
        } else {
            CancelPendingOrders()
        }
    } else if(Type == COVERLONG || Type == COVERSHORT){        // Process closing position
        exchange.SetDirection(Type == COVERLONG ? "closebuy" : "closesell")
        var pfnCover = Type == COVERLONG ? exchange.Sell : exchange.Buy
        var idCover = pfnCover(Price, Amount, CurrPos, OnePriceTick, Type)
        Sleep(TradeInterval)
        if(idCover){
            exchange.CancelOrder(idCover)
        } else {
            CancelPendingOrders()
        }
    } else {
        throw "Type error:" + Type
    }
}  

function main() { 
    // Set Contract
    exchange.SetContractType(Symbol)  

    while(1){
        OnTick()
        Sleep(1000)
    }
}

Published by 发明者量化-小小梦 on the FMZ strategy square · last modified 2019-11-13. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.

Build this with AgenKit

A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.

in Claude Code, Cursor, Codex & 4 more
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/Teaching-Strategy-TCI-Strategy-Mai-Language-Transplantation.md
  1. npx agenkit install engineering-kit in your project
  2. Paste the command into your agent and approve each phase
  3. Get tests, backtest, risk controls and a paper-trading switch

Understand it first

Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.

Related specs

esc

Try , , or .

↑↓ move↵ open