- The Aberration trading system was invented by Keith Fitschen in 1986. In 1993, Keith Fitschen commercialized the system in Futures Truth magazine. Since its release, the system has consistently ranked among the best in 1997, 2001 and 2005. The system ranks in the top ten in the performance rankings of published trading systems. The trading system is characterized by simultaneous trading on eight different varieties, including grain, meat, metals, energy, foreign exchange, finance and stock index futures. The Aberration trading system often trades 3–4 times a year, and holds positions 60% of the time, with an average of 60 days per transaction. It captures huge profits through long-term trading to capture trends.
- How does it make up for the loss? Because it trades in multiple unrelated markets at the same time, when one species loses, another one may make a profit. In one year, there is always one or more varieties that can make huge profits. These large profits make up for small losses in those markets that have no trend. The Aberration trading system manages funds in combination, so it can accept a relatively large amount of money.
- the optical line difference is also based on the Bollinger line trading system, but the trading target is longer than the Bollinger robber system, because it can use twice the standard deviation channel, and no stop loss is used, and the channel indicator itself is used to stop loss.
The following code just a framework of above ideas, you need adjust the detail for your trading choices. Also, please beware of the exchanges houses commission fee and other restriction policy.
Coding framework is clear and reusable.
Real-time debugging when running through the interactive function.
Stable operation, perfect details design.
Support multiple trading varieties at the same time, can control the trading position amount separately.
Automatically resumes progress based on position when restarting.
With risk control module, real-time display the risk situation, stop loss condition.
If you don’t want to rent a server, you can use your own computer or Raspberry Pi that any machine runs Windows, Linux or Mac system.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 | 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.botvs.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) } } |
And the Strategy Arguments setting:

for more information, please see: https://www.fmz.com/bbs-topic/2292
