Die Abweichungshandelsstrategiewww.fmz.com
Das Aberration-Handelssystem wurde 1986 von Keith Fitschen erfunden. 1993 vermarktete Keith Fitschen das System im Magazin Futures Truth. Seit seiner Veröffentlichung zählt das System in den Jahren 1997, 2001 und 2005 immer wieder zu den Besten. Das System rangiert in den Top Ten der Performance-Rangliste der veröffentlichten Handelssysteme. Das Handelssystem zeichnet sich durch den gleichzeitigen Handel mit acht verschiedenen Sorten aus, darunter Getreide, Fleisch, Metalle, Energie, Devisen, Finanzen und Aktienindex-Futures aus.
Wie macht es den Verlust aus? Weil es auf mehreren unabhängigen Märkten gleichzeitig handelt, kann eine andere Art, wenn eine Art verliert, einen Gewinn erzielen. In einem Jahr gibt es immer eine oder mehrere Sorten, die riesige Gewinne erzielen können. Diese großen Gewinne kompensieren kleine Verluste in den Märkten, die keinen Trend haben. Das Aberration-Handelssystem verwaltet Fonds in Kombination, so dass es eine relativ große Geldmenge annehmen kann.
Die Optik-Liniendifferenz basiert ebenfalls auf dem Bollinger-Linien-Handelssystem, aber das Handelsziel ist länger als das Bollinger-Robber-System, da es doppelt so viel Standard-Abweichungskanal verwenden kann und kein Stop-Loss verwendet wird und der Kanalindikator selbst zum Stop-Loss verwendet wird.
Der folgende Code ist nur ein Rahmen für die oben genannten Ideen, Sie müssen das Detail für Ihre Handelsentscheidungen anpassen.
Das Codierungsrahmenwerk ist klar und wiederverwendbar. Echtzeit-Debugging beim Durchlaufen der interaktiven Funktion. Stabiler Betrieb, perfektes Detaildesign. Unterstützt mehrere Handelsvarianten gleichzeitig, kann die Handelsposition separat steuern. Automatisch fortgesetzt, basierend auf Position beim Neustart. Mit dem Risikokontrollmodul wird die Risikosität und der Stop-Loss-Zustand in Echtzeit angezeigt.
Wenn Sie keinen Server mieten möchten, können Sie Ihren eigenen Computer oder Raspberry Pi verwenden, auf dem Windows, Linux oder Mac-Systeme ausgeführt werden.
function Aberration(q, e, symbol, period, upRatio, downRatio, opAmount) {
var self = {}
self.q = q
self.e = e
self.symbol = symbol
self.upTrack = 0
self.middleTrack = 0
self.downTrack = 0
self.nPeriod = period
self.upRatio = upRatio
self.downRatio = downRatio
self.opAmount = opAmount
self.marketPosition = 0
self.lastErrMsg = ''
self.lastErrTime = ''
self.lastBar = {
Time: 0,
Open: 0,
High: 0,
Low: 0,
Close: 0,
Volume: 0
}
self.symbolDetail = null
self.lastBarTime = 0
self.tradeCount = 0
self.isBusy = false
self.setLastError = function(errMsg) {
self.lastErrMsg = errMsg
self.lastErrTime = errMsg.length > 0 ? _D() : ''
}
self.getStatus = function() {
return [self.symbol, self.opAmount, self.upTrack, self.downTrack, self.middleTrack, _N(self.lastBar.Close), (self.marketPosition == 0 ? "--" : (self.marketPosition > 0 ?
"Long#ff0000" : "short#0000ff")), self.tradeCount, self.lastErrMsg, self.lastErrTime]
}
self.getMarket = function() {
return [self.symbol, _D(self.lastBarTime), _N(self.lastBar.Open), _N(self.lastBar.High), _N(self.lastBar.Low), _N(self.lastBar.Close), self.lastBar.Volume]
}
self.restore = function(positions) {
for (var i = 0; i < positions.length; i++) {
if (positions[i].ContractType == self.symbol) {
self.marketPosition += positions[i].Amount * ((positions[i].Type == PD_LONG || positions[i].Type == PD_LONG_YD) ? 1 : -1)
}
}
if (self.marketPosition !== 0) {
self.tradeCount++
Log("restore", self.symbol, "Current position is", self.marketPosition)
}
}
self.poll = function() {
if (self.isBusy) {
return false
}
if (!$.IsTrading(self.symbol)) {
self.setLastError("Not in trading hours")
return false
}
if (!self.e.IO("status")) {
self.setLastError("Unconnected exchange")
return false
}
var detail = self.e.SetContractType(self.symbol)
if (!detail) {
self.setLastError("Switching contract failed")
return false
}
if (!self.symbolDetail) {
self.symbolDetail = detail
Log("contract", detail.InstrumentName.replace(/\s+/g, ""), ", Strategy first time to open a position:", self.opAmount, "hand, one hand", detail.VolumeMultiple, "unit, Maximum order quantity",
detail.MaxLimitOrderVolume, "Margin rate:", detail.LongMarginRatio.toFixed(4), detail.ShortMarginRatio.toFixed(4), "Delivery date", detail.StartDelivDate);
}
var records = self.e.GetRecords()
if (!records || records.length == 0) {
self.setLastError("Failed to get the bar line")
return false
}
var bar = records[records.length - 1]
self.lastBar = bar
if (records.length <= self.nPeriod) {
self.setLastError("The length of the bar line is not enough")
return false
}
if (self.lastBarTime < bar.Time) {
var sum = 0
var pos = records.length - self.nPeriod - 1
for (var i = pos; i < records.length - 1; i++) {
sum += records[i].Close
}
var avg = sum / self.nPeriod
var std = 0
for (i = pos; i < records.length - 1; i++) {
std += Math.pow(records[i].Close - avg, 2)
}
std = Math.sqrt(std / self.nPeriod)
self.upTrack = _N(avg + (self.upRatio * std))
self.downTrack = _N(avg - (self.downRatio * std))
self.middleTrack = _N(avg)
self.lastBarTime = bar.Time
}
var msg
var act = ""
if (self.marketPosition == 0) {
if (bar.Close > self.upTrack) {
msg = 'Buying Long trigger price: ' + bar.Close + ' Upper rail:' + self.upTrack;
act = "buy"
} else if (bar.Close < self.downTrack) {
msg = 'Selling short trigger price: ' + bar.Close + ' lower rail:' + self.downTrack;
act = "sell"
}
} else {
if (self.marketPosition < 0 && bar.Close > self.middleTrack) {
msg = 'close the short position trigger price: ' + bar.Close + ' close position line:' + self.middleTrack;
act = "closesell"
} else if (self.marketPosition > 0 && bar.Close < self.middleTrack) {
msg = 'close the long position trigger price: ' + bar.Close + ' close position line:' + self.middleTrack;
act = "closebuy"
}
}
if (act == "") {
return true
}
Log(self.symbol + ', ' + msg + (NotifyWX ? '@' : ''))
self.isBusy = true
self.tradeCount += 1
if (self.lastErrMsg != '') {
self.setLastError('')
}
self.q.pushTask(self.e, self.symbol, act, self.opAmount, function(task, ret) {
self.isBusy = false
if (!ret) {
return
}
if (task.action == "buy") {
self.marketPosition = 1
} else if (task.action == "sell") {
self.marketPosition = -1
} else {
self.marketPosition = 0
}
})
}
return self
}
function main() {
if (exchange.GetName() !== 'Futures_CTP') {
throw "Only support traditional commodity futures(CTP)"
}
SetErrorFilter("login|ready|initialization")
LogStatus("Ready...")
if (Reset) {
LogProfitReset()
LogReset()
}
// Ref: https://www.fmz.com/bbs-topic/362
if (typeof(exchange.IO("mode", 0)) == 'number') {
Log("Switching the market mode successfully")
}
LogStatus("Waiting to connect with the futures dealer server..")
while (!exchange.IO("status")) {
Sleep(500)
}
LogStatus("Get asset information")
var tblRuntime = {
type: 'table',
title: 'Trading Information',
cols: ['Variety', 'Each open position volume', 'upper rail', 'lower rail', 'middle rail', 'last transaction price', 'position', 'transaction count', 'last error', 'error time'],
rows: []
};
var tblMarket = {
type: 'table',
title: 'Quote information',
cols: ['Variety', 'current cycle', 'opening', 'highest', 'lowest', 'last transaction price', 'volume'],
rows: []
};
var tblPosition = {
type: 'table',
title: 'Position information',
cols: ['Variety', 'leverage', 'direction', 'average price', 'quantity', 'holding profit and loss'],
rows: []
};
var positions = _C(exchange.GetPosition)
if (positions.length > 0 && !AutoRestore) {
throw "There can be no positions when the program starts, but you can check the automatic recovery for automatic identification!"
}
var initAccount = _C(exchange.GetAccount)
var detail = JSON.parse(exchange.GetRawJSON())
if (positions.length > 0) {
initAccount.Balance += detail['CurrMargin']
}
var initNetAsset = detail['CurrMargin'] + detail['Available']
var initAccountTbl = $.AccountToTable(exchange.GetRawJSON(), "Initial funding")
if (initAccountTbl.rows.length == 0) {
initAccountTbl.rows = [
['Balance', 'Available margin', initAccount.Balance],
['FrozenBalance', 'Frozen funds', initAccount.FrozenBalance]
]
}
var nowAcccount = initAccount
var nowAcccountTbl = initAccountTbl
var symbols = Symbols.replace(/\s+/g, "").split(',')
var pollers = []
var prePosUpdate = 0
var suffix = ""
var needUpdate = false
var holdProfit = 0
function updateAccount(acc) {
nowAcccount = acc
nowAcccountTbl = $.AccountToTable(exchange.GetRawJSON(), "Current funds")
if (nowAcccountTbl.rows.length == 0) {
nowAcccountTbl.rows = [
['Balance', 'Available margin', nowAcccount.Balance],
['FrozenBalance', 'Frozen funds', nowAcccount.FrozenBalance]
]
}
}
var q = $.NewTaskQueue(function(task, ret) {
needUpdate = true
Log(task.desc, ret ? "success" : "fail")
var account = task.e.GetAccount()
if (account) {
updateAccount(account)
}
})
_.each(symbols, function(symbol) {
var pair = symbol.split(':')
pollers.push(Aberration(q, exchange, pair[0], NPeriod, Ks, Kx, (pair.length == 1 ? AmountOP : parseInt(pair[1]))))
})
if (positions.length > 0 && AutoRestore) {
_.each(pollers, function(poll) {
poll.restore(positions)
})
}
var isFirst = true
while (true) {
var cmd = GetCommand()
if (cmd) {
var js = cmd.split(':', 2)[1]
Log("Execution debug code:", js)
try {
eval(js)
} catch (e) {
Log("Exception", e)
}
}
tblRuntime.rows = []
tblMarket.rows = []
var marketAlive = false
_.each(pollers, function(poll) {
if (poll.poll()) {
marketAlive = true
}
tblRuntime.rows.push(poll.getStatus())
tblMarket.rows.push(poll.getMarket())
})
q.poll()
Sleep(LoopInterval * 1000)
if ((!exchange.IO("status")) || (!marketAlive)) {
if (isFirst) {
LogStatus("Waiting for the market open...", _D())
}
continue
}
isFirst = false
var now = new Date().getTime()
if (marketAlive && (now - prePosUpdate > 30000 || needUpdate)) {
var pos = exchange.GetPosition()
if (pos) {
holdProfit = 0
prePosUpdate = now
tblPosition.rows = []
for (var i = 0; i < pos.length; i++) {
tblPosition.rows.push([pos[i].ContractType, pos[i].MarginLevel, ((pos[i].Type == PD_LONG || pos[i].Type == PD_LONG_YD) ? 'long#ff0000' : 'short#0000ff'),
pos[i].Price, pos[i].Amount, _N(pos[i].Profit)])
holdProfit += pos[i].Profit
}
if (pos.length == 0 && needUpdate) {
LogProfit(_N(nowAcccount.Balance - initAccount.Balance, 4), nowAcccount)
}
}
needUpdate = false
if (RCMode) {
var account = exchange.GetAccount()
if (account) {
updateAccount(account)
var detail = JSON.parse(exchange.GetRawJSON())
var netAsset = detail['PositionProfit'] + detail['CurrMargin'] + detail['Available']
var risk = detail['CurrMargin'] / (detail['CurrMargin'] + detail['Available'] + detail['PositionProfit'])
suffix = ", Initial net worth of the account: " + _N(initNetAsset, 2) + " , risk control minimum net value requirement" + MinNetAsset + " , Current account equity: " + _N(netAsset, 2) +
", Profit and loss: " + _N(netAsset - initNetAsset, 3) + " yuan, risk: " + ((risk * 100).toFixed(3)) + "% #ff0000"
if (netAsset < MinNetAsset) {
Log("The risk control module triggers, stops running and closes all positions, the current net value is ", netAsset, ", Require less than the minimum net value:", MinNetAsset)
if (RCCoverAll) {
Log("Start to close all positions")
$.NewPositionManager().CoverAll()
}
throw "Stop running"
}
}
}
}
LogStatus('`' + JSON.stringify([tblRuntime, tblPosition, tblMarket, initAccountTbl, nowAcccountTbl]) + '`\nLast price update: ' + _D() +
', Last update of position: ' + _D(prePosUpdate) + '\nCurrent holding position total profit and loss: ' + _N(holdProfit, 3) + suffix)
}
}
Kleine Träume- Gut gemacht.