Recentemente, ao falar sobre estratégias com meus amigos, eu aprendi que muitas estratégias escritas em MyLanguage sofrem de flexibilidade. Em muitos casos, é necessário usar o período padrão de linha K que não é fornecido pelo sistema. Por exemplo, o requisito máximo é usar a linha K por 4 horas. Este problema foi resolvido em um artigo. Se você estiver interessado, por favor dê uma olhada:LigaçãoNo entanto, na estratégia MyLanguage, devido ao alto recurso de encapsulamento da MyLanguage, não é flexível processar dados por si só.
É muito simples para o transplante de estratégia de tendência. podemos usar um código de amostra para preencher a parte de cálculo de dados do código que dirige a estratégia, e preencher as condições de gatilho de sinal de negociação.
Tome a estratégia para os futuros da OKX como exemplo.
// 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 = 0 // Record the number of positions
var TradeInterval = 500 // Polling intervals
var PriceTick = 1 // Price per jump
var Symbol = "this_week"
function OnTick(){
// Ticker processing part of the driving strategy
// To be filled...
// Trading signal trigger processing section
// To be filled...
// Execution of trading logic
var pos = null
var price = null
var currBar = records[records.length - 1]
if(_State == OPENLONG){
pos = GetPosition(PD_LONG)
// Determine whether the state is satisfied, and if so, modify the state.
if(pos[1] >= Amount){
_State = LONG
Amount = pos[1] // Update the actual volume.
return
}
price = currBar.Close - (currBar.Close % PriceTick) + PriceTick * 2
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 the actual volume.
return
}
price = currBar.Close - (currBar.Close % PriceTick) - PriceTick * 2
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 * 2
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 * 2
Trade(COVERSHORT, price, pos[1], pos, PriceTick)
}
}
// Trading logic section
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){ // Processing transactions
if(Type == OPENLONG || Type == OPENSHORT){ // Processing of opening positions
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){ // Processing of closing positions
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 up the contract
exchange.SetContractType(Symbol)
while(1){
OnTick()
Sleep(1000)
}
}
Backtest do MyLanguage:
Código de estratégia do MyLanguage:
MA5^^MA(C,5);
MA15^^MA(C,15);
CROSSUP(MA5,MA15),BPK;
CROSSDOWN(MA5,MA15),SPK;
Em primeiro lugar, preencher as partes de aquisição do ticker e cálculo do indicador para o código de amostra reutilizável:
// The ticker processing part of the driving strategy
var records = _C(exchange.GetRecords)
if (records.length < 15) {
return
}
var ma5 = TA.MA(records, 5)
var ma15 = TA.MA(records, 15)
var ma5_pre = ma5[ma5.length - 3]
var ma15_pre = ma15[ma15.length - 3]
var ma5_curr = ma5[ma5.length - 2]
var ma15_curr = ma15[ma15.length - 2]
Como você pode ver, a estratégia de EMA dupla é muito simples.records
, e depois utilizar a função EMATA.MA
deTA function library
para calcular a EMA de 5 dias e a EMA de 15 dias (como podemos ver na interface do backtest, o período da linha K é definido na linha K diária, entãoTA.MA(records, 5)
é calcular a EMA de 5 dias,TA.MA(records, 15)
O valor de referência é o valor de referência para o cálculo da EMA de 15 dias).
Então, obtém o penúltimo ponto.ma5_curr
(valor do indicador), o último terceiro pontoma5_pre
(valor do indicador) dos dados do indicadorma5
, e o mesmo para oma15
Então podemos usar esses dados de indicadores para julgar o Golden Cross e Bearish Crossover, como mostrado na figura:
Sempre que tal estado é formado, é uma cruz de ouro definida ou um cruzamento de baixa.
Em seguida, a parte de julgar o sinal pode ser escrita da seguinte forma:
if(_State == IDLE && ma5_pre < ma15_pre && ma5_curr > ma15_curr){
_State = OPENLONG
Amount = 1
}
if(_State == IDLE && ma5_pre > ma15_pre && ma5_curr < ma15_curr){
_State = OPENSHORT
Amount = 1
}
if(_State == LONG && ma5_pre > ma15_pre && ma5_curr < ma15_curr){
_State = COVERLONG
Amount = 1
}
if(_State == SHORT && ma5_pre < ma15_pre && ma5_curr > ma15_curr){
_State = COVERSHORT
Amount = 1
}
Desta forma, o transplante está bem. Backtesting da estratégia JavaScript Configuração de backtesting:
Resultado dos testes de regresso:
Backtesting do MyLanguage
Pode-se ver que os resultados do backtest são quase os mesmos. Desta forma, se você quiser continuar a adicionar funções interativas, processamento de dados (como síntese de linhas K) e exibição de gráficos personalizados para a estratégia, você pode alcançar isso.
Se estiver interessado, por favor tente.