어버레이션 전략

저자:작은 꿈, 2016-12-10 12:53:45, 업데이트: 2016-12-10 12:57:30

트렌드를 통해 돈을 벌기 위한 어버레이션 전략


  • 소개 1

직관적인 선물에 의존하는 많은 친구들은 1986년 Keith Fitschen이 발명한 전설적인 거래 시스템, 즉 100% 이상의 연간 수익률을 달성한 Aberration 전략에 대해 조금 낯설어 있을 것이다. 1993년 그는 미국 유화 Future Truth 유화 잡지에 이 시스템을 발표했고, 이 전략이 발표된 날부터 성과가 순위 1위를 차지해 1997년, 2001년, 2005년에도 출시된 거래 시스템의 성과 순위 1위를 차지했다. 이것은 중장선 거래 시스템이며, 추세 추적 전략에 속하며, 곡물, 육류, 금속, 에너지, 외환, 주식 지표 선물 등 8가지 품종 사이에서 유연하게 작동하며, 추세를 길게 추적하여 수익을 얻는다. 그리고 동시에 여러 개의 무관하거나 낮은 관련성을 가진 시장에서 거래하기 때문에, 한 개 또는 몇 개 시장에서 추세를 얻는 높은 수익이 다른 불안정한 시장에서 손실을 보상한다. 그것의 거래 빈도는 일반적으로 일 년에 3-4 번, 60%의 시간을 보유하며, 평균적으로 60 일 동안 거래한다. 이 전략의 평준화 전략은 다음과 같다: 먼저 35일 이동평균선과 35일 종료 가격의 표준차이를 이용하여 상중 하위 3개의 궤도를 도출한다. 상위 한 바의 종료 가격에 따라 궤도를 통과할 때 더 많이, 하위 한 바의 종료 가격에 따라 궤도를 통과할 때 더 많이, 하위 한 바의 종료 가격에 따라 하위 한 바의 종료 가격에 따라 더 많이, 하위 한 바의 종료 가격에 따라 더 많이, 하위 한 바의 종료 가격에 따라 하위 한 바의 종료 가격에 따라 더 많이, 하위 한 바의 종료 가격에 따라 하위 한 바의 종료 가격에 따라 더 많이, 하위 한 바의 종료 가격에 따라 하위 한 바의 종료 가격에 따라 더 많이, 하위 한 바의 종료 가격에 따라 하위 한 바의 종료 가격에 따라 더 많이, 하위 한 바의 종료 가격에 따라 하위 한 바의 종료 가격에 따라 더 많이. 이 전략의 아이디어는 매우 간단하다. 이동평균과 표준차점 설정 통로를 이용하고, 통로를 돌파했는지 여부를 판단하여 추세를 포착하는 것이다. 그리고 추세가 점차 약화될 때, 중궤도가 먼저 완화되기 때문에, K선이 역으로 중궤도를 통과할 때, 추세가 끝났다고 간주하고, 출전을 중지할 수 있다. 그리고 추세를 잘못 판단할 경우, K선이 역으로 중궤도를 통과할 때, 또한 적시에 손실을 중지할 수 있다. 이 전략은 이전부터 발표되어 현재 거래 시장에서 거의 적용되지 않을 수도 있지만, 트렌드 포착 아이디어는 오래 지속됩니다.

  • 본격적인 설명

    어버레이션 거래 시스템은 1986년 Keith Fitschen에 의해 발명되었으며, 1993년 Future Trust 잡지에 발표되었다. 흥미롭게도 Keith는 거래자가 태어나지 않았으며, 그는 20년 이상 미국 공군에서 사용했으며, 무기 전문의 네비게이션 시스템, 타임시리즈 데이터를 처리하는 데 깊은 힘을 가지고 있다. 애버레이션 거래 시스템은 수동적인 트렌드를 따르는 것이 특징이며, 장기 신호를 사용하며, 한 품종에서 연간 3-4번 거래하며, 총 거래시간의 60% 이상을 보유하며, 다른 자본 규모에 따라 다른 품종을 배분할 수 있다. 그것은 긴 라인 거래를 통해 트렌드를 포착하여 엄청난 이익을 얻으며, 동시에 여러 가지 연관되지 않은 시장에서 거래하기 때문에, 한 품종이 철회할 때 다른 품종이 이익을 얻을 수 있다. 일 년 동안 항상 하나 이상의 품종이 큰 이익을 얻을 수 있다. 이러한 큰 이익은 그 트렌드 시장의 작은 손실을 보상한다. 돈을 벌기 위한 전략을 즉시 볼 수 없었고, 내면의 흥분을 억제할 수 없었다. 구체적으로, 압러레이션 시스템은 3개의 궤도를 이용해서 거래를 한다. 먼저 종목의 N 일간의 종료 가격의 수학적 평균 MA (close) 를 중간 궤도 (MID) 로 계산하고, 종료 가격의 표준편차 (std) (close) 를 변동성의 척도로 계산하고, 궤도 MID+m를 계산한다.std ((close) 및 하부 MID-mstd ((close) ᅳ 가격이 궤도를 돌파할 때 더 많이 하고, 가격이 중간 궤도로 돌아갈 때 평형; 반대로, 가격이 궤도를 돌파할 때 공백하고, 가격이 중간 궤도로 돌아갈 때 평형이다.ᅳ

  • 전략적 원칙

    어버레이션은 또한 통로를 돌파하는 시스템이지만, 통로의 상승과 하락은 변동률에 의해 결정된다.

    1차, 열차를 정지합니다: 오버레이션은 3개의 통로선으로 구성되어 있으며, 중전선은 특정 주기의 이동 평균 (AveMa) 이며, 상하전선은 중전선의 기초에 따라 StdValue를 더하거나 감소합니다. 즉, 우리가 잘 알고 있는 브린 선, 시스템 구조는 매우 간결합니다.

    이 계산은 다음과 같습니다. (1) 계산 중간 궤도: AveMa=Average ((Close[1],Length); (2) 계산 기준편차: StdValue = StandardDev ((Close[1],Length); (3) 궤도 계산:UpperBand=Avema+StdDevUpStdValue; (StdDevUp는 열차 변수) (4) 구간 아래를 계산합니다: LowerBand=Avema-StdDevDnStdValue; (StdDevDn는 경로변수)

    두 번째, 판매 조건: 포토: 폐쇄 가격, 중장선에서 벗어나는 포토를 개설, 중장선에서 벗어나는 포토 빈 머릿: 폐쇄 가격은 열차 하락 하락 하락 하락 오픈 포지션, 중심을 돌파.

  • 전략 소스 코드

    • 전략 프레임워크 논리가 명확하고 복제성이 강하다
    • 상호 작용 기능으로 실제 디스크를 실행할 때 실시간 디버깅
    • 안정적으로 작동하고 세부 사항은 완벽하게 처리됩니다.
    • 여러 종류의 동시 동작을 지원하며, 개봉량을 개별적으로 제어할 수 있습니다.
    • 재부팅 시 위치에 따라 진행을 자동으로 복원합니다.
    • 풍력 제어 모듈을 통해 실시간으로 위험을 표시하고 손실을 막습니다.
    • 위크메일 통지
    • 서버를 빌리고 싶지 않은 사람은 자신의 컴퓨터 또는 트레이드 라인업을 사용하여 Windows, Linux, Mac 시스템을 실행할 수 있으며 라우터 브러쉬도 할 수 있습니다.
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 ? "多#ff0000" : "空#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("恢复", self.symbol, "当前持仓为", self.marketPosition)
        }
    }

    self.poll = function() {
        if (self.isBusy) {
            return false
        }

        if (!$.IsTrading(self.symbol)) {
            self.setLastError("不在交易时间内")
            return false
        }

        if (!self.e.IO("status")) {
            self.setLastError("未连接交易所")
            return false
        }

        var detail = self.e.SetContractType(self.symbol)
        if (!detail) {
            self.setLastError("切换合约失败")
            return false
        }
        if (!self.symbolDetail) {
            self.symbolDetail = detail
            Log("合约", detail.InstrumentName.replace(/\s+/g, ""), ", 策略一次开仓:", self.opAmount, "手, 一手", detail.VolumeMultiple, "份, 最大下单量", detail.MaxLimitOrderVolume, "保证金率:", detail.LongMarginRatio.toFixed(4), detail.ShortMarginRatio.toFixed(4), "交割日期", detail.StartDelivDate);
        }
        var records = self.e.GetRecords()
        if (!records || records.length == 0) {
            self.setLastError("获取柱线失败")
            return false
        }

        var bar = records[records.length - 1]
        self.lastBar = bar

        if (records.length <= self.nPeriod) {
            self.setLastError("柱线长度不够")
            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 = '做多 触发价: ' + bar.Close + ' 上轨:' + self.upTrack;
                act = "buy"
            } else if (bar.Close < self.downTrack) {
                msg = '做空 触发价: ' + bar.Close + ' 下轨:' + self.downTrack;
                act = "sell"
            }
        } else {
            if (self.marketPosition < 0 && bar.Close > self.middleTrack) {
                msg = '平空 触发价: ' + bar.Close + ' 平仓线:' + self.middleTrack;
                act = "closesell"
            } else if (self.marketPosition > 0 && bar.Close < self.middleTrack) {
                msg = '平多 触发价: ' + bar.Close + ' 平仓线:' + 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 "只支持传统商品期货(CTP)"
    }
    
    SetErrorFilter("login|ready|初始化")

    LogStatus("Ready...")
    if (Reset) {
        LogProfitReset()
        LogReset()
    }
    
    // Ref: https://www.fmz.com/bbs-topic/362
    if (typeof(exchange.IO("mode", 0)) == 'number') {
        Log("切换行情模式成功")
    }

    LogStatus("等待与期货商服务器连接..")
    while (!exchange.IO("status")) {
        Sleep(500)
    }
    LogStatus("获取资产信息")
    var tblRuntime = {
        type: 'table',
        title: '交易信息',
        cols: ['品种', '每次开仓量', '上轨', '下轨', '中轨', '最后成交价', '仓位', '交易次数', '最后错误', '错误时间'],
        rows: []
    };
    var tblMarket = {
        type: 'table',
        title: '行情信息',
        cols: ['品种', '当前周期', '开盘', '最高', '最低', '最后成交价', '成交量'],
        rows: []
    };
    var tblPosition = {
        type: 'table',
        title: '持仓信息',
        cols: ['品种', '杠杆', '方向', '均价', '数量', '持仓盈亏'],
        rows: []
    };
    var positions = _C(exchange.GetPosition)
    if (positions.length > 0 && !AutoRestore) {
        throw "程序启动时不能有持仓, 但您可以勾选自动恢复来进行自动识别 !"
    }
    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(), "初始资金")

    if (initAccountTbl.rows.length == 0) {
        initAccountTbl.rows = [
            ['Balance', '可用保证金', initAccount.Balance],
            ['FrozenBalance', '冻结资金', 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(), "当前资金")
        if (nowAcccountTbl.rows.length == 0) {
            nowAcccountTbl.rows = [
                ['Balance', '可用保证金', nowAcccount.Balance],
                ['FrozenBalance', '冻结资金', nowAcccount.FrozenBalance]
            ]
        }
    }

    var q = $.NewTaskQueue(function(task, ret) {
        needUpdate = true
        Log(task.desc, ret ? "成功" : "失败")
        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("执行调试代码:", 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("正在等待开盘...", _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) ? '多#ff0000' : '空#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 = ", 账户初始净值约: " + _N(initNetAsset, 2) + " , 风控最小净值要求" + MinNetAsset + " , 当前账户净值约: " + _N(netAsset, 2) + ", 盈亏约: " + _N(netAsset - initNetAsset, 3) + " 元, 风险: " + ((risk * 100).toFixed(3)) + "% #ff0000"
                    if (netAsset < MinNetAsset) {
                        Log("风控模块触发, 中止运行并平掉所有仓位, 当前净值约 ", netAsset, ", 要求低于最小净值:", MinNetAsset)
                        if (RCCoverAll) {
                            Log("开始平掉所有仓位")
                            $.NewPositionManager().CoverAll()
                        }
                        throw "中止运行"
                    }
                }
            }
        }
        LogStatus('`' + JSON.stringify([tblRuntime, tblPosition, tblMarket, initAccountTbl, nowAcccountTbl]) + '`\n价格最后更新: ' + _D() + ', 持仓最后更新: ' + _D(prePosUpdate) + '\n当前持仓总盈亏: ' + _N(holdProfit, 3) + suffix)
    }
}
  • 테스트 결과

    이 시스템은 포트폴리오를 구축하는 것만으로도 시스템의 힘을 발휘할 수 있기 때문입니다.img

  • 요약

    1. 이 전략의 다양성과 주기적 적응성은 대부분의 거래 품종에 적합합니다. 2. 이 전략은 대규모의 철수를 요구하며 엄격한 자금 관리와 위험 통제가 필요합니다.


더 많은