ahr999均值回归策略 | AHR999 Mean Reversion Strategy AHR999, AHR999X, 调和平均数, 对数价格, 净值
####概述 该策略基于AHR999指标和AHR999X指标,通过比较当前价格与调和平均价格和对数价格的关系,判断比特币是否处于超买或超卖状态,并据此进行定投和抄底操作。同时,策略还设置了止盈条件,在AHR999X指标低于某个阈值时进行部分卖出。
####策略原理 1. 计算比特币历史价格的调和平均数,作为长期均衡价格的参考。 2. 根据比特币诞生天数,计算对应的对数价格,作为长期趋势价格的参考。 3. 计算当前的AHR999指标,即当前价格与调和平均价格和对数价格的比值乘积。 4. 计算当前的AHR999X指标,即调和平均价格与当前价格的比值乘以对数价格与当前价格的比值的3倍。 5. 根据AHR999指标的值,判断是否进行定投或抄底操作。当AHR999在设定的区间内时进行定投,低于下限时进行抄底。 6. 根据AHR999X指标的值,判断是否进行止盈操作。当AHR999X低于设定的阈值时,卖出一定比例的持仓。
####优势分析 1. 策略基于比特币的长期均衡价格和趋势价格,有一定的理论基础和可解释性。 2. 引入了调和平均数,减少了短期波动的影响,使得判断更加稳健。 3. 通过AHR999和AHR999X两个指标的结合,可以较好地捕捉比特币的超买超卖状态和止盈时机。 4. 定投和抄底相结合,在价格相对低估时逐步建仓,成本相对较低。 5. 设置了止盈操作,可以在价格相对高估时获得一定收益,同时保留大部分持仓以获得长期增值。
####风险分析 1. 策略依赖于AHR999和AHR999X指标,如果未来比特币价格的走势发生显著变化,导致这两个指标失效,策略可能面临较大风险。 2. 调和平均数对历史价格的长度较为敏感,如果选取的历史价格区间不够合理,可能影响策略表现。 3. 对数价格的计算基于比特币诞生天数,如果比特币的长期增长趋势发生改变,对数价格可能失去参考意义。 4. 策略在价格处于相对高位时可能面临抄底资金不足的风险,导致无法及时建仓。 5. 止盈操作可能导致策略在价格继续上涨的情况下错失一部分收益。
####优化方向 1. 对AHR999和AHR999X指标的阈值进行优化,找到最适合的定投、抄底和止盈区间。 2. 引入更多指标,如交易量、波动率等,以辅助判断趋势和超买超卖状态。 3. 对调和平均数的计算方式进行优化,如适当调整历史价格的权重,减少对久远历史价格的依赖。 4. 对定投和抄底的金额进行动态调整,在价格较低时加大投入,在价格较高时减少投入。 5. 对止盈操作进行优化,如根据价格趋势和持仓成本进行动态调整,以平衡收益和风险。
####总结 AHR999均值回归策略通过比较当前价格与历史均衡价格和趋势价格,判断比特币的超买超卖状态,并进行定投、抄底和止盈操作。该策略具有一定的理论基础和可解释性,通过两个指标的结合,可以较好地捕捉比特币的价格波动规律。但策略也面临指标失效、参数选取、资金管理等方面的风险,需要在实践中不断优化和改进。总的来说,AHR999均值回归策略为比特币的长期价值投资提供了一种可行的思路。
/*backtest
start: 2018-04-14 00:00:00
end: 2021-07-18 00:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Binance","currency":"BTC_USDT","balance":1000000,"stocks":0}]
*/
/*
* @Project:
* @Version:
* @Author: RedSword <[email protected]>
* @Description:
* @Date: 2021-07-19 11:02:43
* @LastEditors: RedSword
* @LastEditTime: 2021-07-19 15:55:16
* @Copyright:: Copyright © 2020 FMZ Quant
* 感谢ahr999大神,后续完善可以加入一些理财,币和钱都闲着,收益会低一点,也可以放到bitfinex放借贷,年化也有十几的收益,还可以放大资金,买了币以后,就去交割合约做多,暂时想到这么多,希望大家多多交流
* 参考:
* https://github.com/who3m1/ahr999-mixin
* https://btctom.com/ahr999
* https://btctom.com/ahr999x
*/
function harmonicMean(x) {
if (x.length === 0) {
return undefined;
}
var reciprocalSum = 0;
for (var i = 0; i < x.length; i++) {
if (x[i] <= 0) {
return undefined;
}
reciprocalSum += 1 / x[i];
}
return x.length / reciprocalSum;
}
function GetNowPrice() {
var ticker = exchange.GetTicker();
return ticker.Last;
}
function GetPrices() {
var records = exchange.GetRecords(PERIOD_D1);
var prices = [];
for (let i = 0; i < records.length; i++) {
const record = records[i];
// Log(record);
prices.push(record.Close);
}
return prices;
}
function CalcAHR999() {
var now = parseInt(Unix());
var prices = GetPrices();
var avgPrice = harmonicMean(prices);
var nowPrice = GetNowPrice();
var birthday = (now - 1230940800) / (24 * 60 * 60);
var logPrice = Math.pow(10, 5.84 * Math.log10(parseInt(birthday)) - 17.01);
//2020-4-16 13:16 以后使用新的指标
//https://weibo.com/5034063086/IDzPWyN8Z?from=page_1005055034063086_profile&wvr=6&mod=weibotime
if (now > 1587014160) {
logPrice = Math.pow(10, 5.8 * Math.log10(parseInt(birthday)) - 16.88);
}
var ahr999 = Math.round((nowPrice / avgPrice) * (nowPrice / logPrice) * 1000) / 1000;
var ahr999x = Math.round((avgPrice / nowPrice) * (logPrice / nowPrice) * 3 * 1000) / 1000;
return {
ahr999: ahr999,
ahr999x: ahr999x,
};
}
function init() {
exchange.SetMaxBarLen(200);
if (exchange.GetCurrency().indexOf("BTC_USD") == -1) {
throw "只支持BTC交易对";
}
}
function UpdateStatus(account, nowPrice) {
var table = {
type: "table",
title: "持仓信息",
cols: ["定投币种", "初始净值", "当前净值", "定投次数", "持仓数量", "持仓均价", "当前价格", "累计定投", "可用资金", "盈利率%"],
rows: [],
};
var netValue = account.Balance + account.Stocks * nowPrice;
table.rows.push([
exchange.GetCurrency(),
InitMoney,
_N(netValue, 2),
Global.number,
_N(account.Stocks, 6),
_N((InitMoney - account.Balance) / account.Stocks, 2),
nowPrice,
_N(Global.number * Money, 2),
_N(account.Balance, 2),
_N((netValue / InitMoney) * 100),
]);
LogStatus("`" + JSON.stringify(table) + "`");
}
var Global = {
upTime: 0, //循环间隔
number: 0, //定投次数
multipleNumber: 0, //抄底次数
};
function main() {
while (true) {
var now = parseInt(Unix());
if (now > Global.upTime) {
var price = GetNowPrice();
var account = exchange.GetAccount();
var ahr999 = CalcAHR999();
Global.upTime = now + 3600 * Interval;
if (ahr999.ahr999 >= Bottom && ahr999.ahr999 < Top) {
if (Money > account.Balance) continue;
Log("开始定投");
exchange.Buy(-1, Money);
Global.number++;
} else if (ahr999.ahr999 < Bottom) {
if (Money * Multiple > account.Balance) continue;
Log("开始抄底");
exchange.Buy(-1, Money * Multiple);
Global.number += Multiple;
}
if (TakeProfit & (ahr999.ahr999x < TakeProfitLine)) {
Log("开始顶逃");
var sell = Global.number * TakeProfitRate * Money;
var coinNumber = sell / price;
exchange.Sell(-1, coinNumber);
}
UpdateStatus(account, price);
}
Sleep(1000);
}
}