Recientemente, al hablar sobre estrategias con mis amigos, aprendí que muchas estrategias escritas en MyLanguage sufren de flexibilidad. En muchos casos, es necesario usar el período estándar de la línea K que no es proporcionado por el sistema. Por ejemplo, el requisito máximo es usar la línea K durante 4 horas. Este problema ha sido resuelto en un artículo. Si estás interesado, por favor echa un vistazo:EnlaceSin embargo, en la estrategia de MyLanguage, debido a la alta característica de encapsulación de MyLanguage, no es flexible para procesar datos por sí mismo. En este momento, es necesario trasplantar la idea de estrategia a otros idiomas.
Es muy simple para el trasplante de estrategia de tendencia. podemos utilizar un código de muestra para rellenar la parte de cálculo de datos del código que conduce a la estrategia, y rellenar las condiciones de activación de la señal de comercio.
Tomemos la estrategia para los futuros de OKX como ejemplo.
// 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)
}
}
Prueba posterior de MyLanguage:
Código de estrategia de MyLanguage:
MA5^^MA(C,5);
MA15^^MA(C,15);
CROSSUP(MA5,MA15),BPK;
CROSSDOWN(MA5,MA15),SPK;
En primer lugar, cumplimentar las partes de adquisición de ticker y cálculo de indicadores para el código de muestra reutilizable:
// 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 pueden ver, la estrategia de doble EMA es muy simple.records
, y luego utilizar la función EMATA.MA
de lasTA function library
para calcular la EMA de 5 días y la EMA de 15 días (como podemos ver en la interfaz de backtest, el período de la línea K se establece en la línea K diaria, por lo queTA.MA(records, 5)
es el cálculo de la EMA de 5 días,TA.MA(records, 15)
El objetivo de este método es calcular la EMA de 15 días).
Entonces consigue el penúltimo puntoma5_curr
(valor del indicador), el tercer último puntoma5_pre
(valor del indicador) de los datos del indicadorma5
, y lo mismo para elma15
Entonces podemos usar estos datos de indicadores para juzgar la Cruz Dorada y el Cruce de Ojos, como se muestra en la figura:
Siempre que se forma tal estado, es una cruz de oro o cruce bajista.
Entonces la parte de juzgar la señal se puede escribir de la siguiente manera:
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
}
De esta manera, el trasplante está bien. Pruebas de retroceso de la estrategia JavaScript Configuración de pruebas de retroceso:
Resultado de las pruebas de retroceso:
Prueba de retroceso de MyLanguage
Se puede ver que los resultados de la prueba de retroceso son casi los mismos. De esta manera, si desea continuar agregando funciones interactivas, procesamiento de datos (como la síntesis de líneas K) y visualización de gráficos personalizados a la estrategia, puede lograr esto.
Si estás interesado, por favor prueba.