
Alors que je discutais récemment de stratégies avec mes amis, j’ai appris que de nombreuses personnes qui utilisent mon langage pour écrire des stratégies souffrent du problème de flexibilité. Dans de nombreux cas, il est nécessaire d’utiliser une période K-line standard qui n’est pas fournie par le système. Par exemple, l’exigence la plus fréquemment demandée est d’utiliser une ligne K de 4 heures. Ce problème a été résolu dans un article. Si cela vous intéresse, vous pouvez d’abord y jeter un œil :Lien. Cependant, dans la stratégie de mon langage, en raison de la nature hautement encapsulée du langage, ce problème ne peut pas traiter les données de manière flexible par lui-même. À l’heure actuelle, il est nécessaire de transplanter la pensée stratégique dans d’autres langages.
Il est très simple de transplanter des stratégies de tendance. Nous pouvons utiliser un exemple de code pour remplir la partie calcul des données de la stratégie de pilotage et remplir les conditions de déclenchement du signal de trading.
Prenons comme exemple la stratégie utilisée pour les contrats à terme OKEX.
// 全局变量
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 // 记录持仓数量
var TradeInterval = 500 // 轮询间隔
var PriceTick = 1 // 价格一跳
var Symbol = "this_week"
function OnTick(){
// 驱动策略的行情处理部分
// 待填充...
// 交易信号触发处理部分
// 待填充...
// 执行交易逻辑
var pos = null
var price = null
var currBar = records[records.length - 1]
if(_State == OPENLONG){
pos = GetPosition(PD_LONG)
// 判断是不是 满足状态,如果满足 修改状态
if(pos[1] >= Amount){
_State = LONG
Amount = pos[1] // 更新实际量
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] // 更新实际量
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)
}
}
// 交易逻辑部分
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){ // 处理交易
if(Type == OPENLONG || Type == OPENSHORT){ // 处理开仓
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){ // 处理平仓
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() {
// 设置合约
exchange.SetContractType(Symbol)
while(1){
OnTick()
Sleep(1000)
}
}
Backtest du langage Mai :

Code de stratégie de la langue Mai :
MA5^^MA(C,5);
MA15^^MA(C,15);
CROSSUP(MA5,MA15),BPK;
CROSSDOWN(MA5,MA15),SPK;
Tout d’abord, remplissez les parties acquisition de marché et calcul d’indicateur dans l’exemple de code réutilisable :
// 驱动策略的行情处理部分
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]
Comme vous pouvez le constater, la stratégie de la double moyenne mobile est très simple. Il suffit d’obtenir d’abord les données de la ligne K.records, puis utilisezTA函数库Fonction de moyenne mobileTA.MACalculez la moyenne mobile sur 5 jours et la moyenne mobile sur 15 jours (vous pouvez voir sur l’interface de backtest que la période de la ligne K est définie sur la ligne K quotidienne, doncTA.MA(records, 5)La moyenne mobile sur 5 jours est calculée.TA.MA(records, 15)(Moyenne mobile sur 15 jours).
Alors, prendsma5L’avant-dernier point des données de l’indicateurma5_curr(Valeur indicatrice), du troisième au dernier pointma5_pre(Valeur indicatrice),ma15Il en va de même pour les données des indicateurs. Vous pouvez ensuite utiliser ces données indicatrices pour juger la croix dorée et la croix morte, comme indiqué dans la figure :
Tant qu’un tel état est formé, c’est une croix d’or confirmée ou une croix morte.
La partie jugement du signal peut alors s’écrire comme suit :
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
}
Cela signifie que la greffe est OK et que vous pouvez revenir en arrière et la tester :
Stratégies de backtesting JavaScript
Configuration du backtest :

回测结果:

Backtesting de mon langage

On peut voir que les résultats des backtests sont fondamentalement les mêmes, donc si vous souhaitez continuer à ajouter des fonctions interactives à la stratégie, ajouter un traitement de données (comme la synthèse K-line) et ajouter un dessin et un affichage de graphiques personnalisés, vous pouvez le faire il.
Les étudiants intéressés peuvent l’essayer.