Benjamin Graham, el mentor de Warren Buffett, ha mencionado un modo de negociación de balance dinámico de acciones y bonos en el libro
El modo de negociación es muy simple: - Invertir el 50% de los fondos en fondos de acciones y el 50% restante en fondos de bonos. - De acuerdo con el intervalo fijo o los cambios del mercado, realizar un reequilibrio de activos para restablecer la proporción de activos de acciones y activos de bonos a la proporción original de 1: 1. Esta es la lógica de toda la estrategia, incluyendo cuándo comprar y vender y cuánto comprar y vender.
En este método, la volatilidad de los fondos de bonos es muy pequeña en realidad, mucho menor que la volatilidad de las acciones, por lo que los bonos se utilizan como "anclajes de referencia" aquí, es decir, para medir si las acciones han aumentado demasiado o demasiado poco por los bonos. Si el precio de las acciones sube, el valor de mercado de las acciones será mayor que el valor de mercado de los bonos. Cuando la relación del valor de mercado de los dos exceda el umbral establecido, la posición total se reajustará, las acciones se venderán y los bonos se comprarán, de modo que la relación del valor de mercado de las acciones y los bonos volverá a la 1:1 original. Por el contrario, si el precio de las acciones disminuye, el valor de mercado de las acciones será menor que el valor de mercado de los bonos. De esta manera, podemos disfrutar de los frutos del crecimiento de las acciones y reducir la volatilidad de los activos equilibrando la proporción entre las acciones y los bonos dinámicamente. Como pionero de la inversión de valor, Graham nos ha proporcionado una buena idea.
Estrategia de balance dinámico en el activo blockchain BTC
La lógica de la estrategia
De esta manera, no importa si BTC se aprecia o se deprecia, siempre mantenemos el saldo de la cuenta y el valor de mercado de BTC
Así que, ¿cómo implementarlo en código? Tomamos la plataforma de comercio de FMZ Quant como ejemplo, echemos un vistazo al marco de la estrategia primero:
// function to cancel orders
function CancelPendingOrders() {}
// function to place an order
function onTick() {}
// main function
function main() {
// filter non-important information
SetErrorFilter("GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout");
while (true) { // polling mode
if (onTick()) { // execute onTick function
CancelPendingOrders(); // cancel the outstanding pending orders
Log(_C(exchange.GetAccount)); // print the current account information
}
Sleep(LoopInterval * 1000); // sleep
}
}
En realidad, todo el marco de la estrategia es muy simple, incluyendo una función principal, una función de colocación de pedidos onTick, una función CancelPendingOrders y los parámetros necesarios.
// order-placing function
function onTick() {
var acc = _C(exchange.GetAccount); // obtain account information
var ticker = _C(exchange.GetTicker); // obtain Tick data
var spread = ticker.Sell - ticker.Buy; // obtain bid ask spread of Tick data
// 0.5 times of the difference between the account balance and the current position value
var diffAsset = (acc.Balance - (acc.Stocks * ticker.Sell)) / 2;
var ratio = diffAsset / acc.Balance; // diffAsset / account balance
LogStatus('ratio:', ratio, _D()); // Print ratio and current time
if (Math.abs(ratio) < threshold) { // If the absolute value of the ratio is less than the specified threshold
return false; // return false
}
if (ratio > 0) { // if ratio > 0
var buyPrice = _N(ticker.Sell + spread, ZPrecision); // Calculate the price of an order
var buyAmount = _N(diffAsset / buyPrice, XPrecision); // Calculate the order quantity
if (buyAmount < MinStock) { // If the order quantity is less than the minimum transaction quantity
return false; // return false
}
exchange.Buy(buyPrice, buyAmount, diffAsset, ratio); // Purchase order
} else {
var sellPrice = _N(ticker.Buy - spread, ZPrecision); // Calculate the price of an order
var sellAmount = _N(-diffAsset / sellPrice, XPrecision); // Calculate the order quantity
if (sellAmount < MinStock) { // If the order quantity is less than the minimum transaction quantity
return false; // return false
}
exchange.Sell(sellPrice, sellAmount, diffAsset, ratio); // Sell and place an order
}
return true; // return true
}
La lógica de negociación de órdenes está bien organizada, y todos los comentarios han sido escritos en el código.
El proceso principal es el siguiente:
// Withdrawal function
function CancelPendingOrders() {
Sleep(1000); // Sleep for 1 second
var ret = false;
while (true) {
var orders = null;
// Obtain the unsettled order array continuously. If an exception is returned, continue to obtain
while (!(orders = exchange.GetOrders())) {
Sleep(1000); // Sleep for 1 second
}
if (orders.length == 0) { // If the order array is empty
return ret; // Return to order withdrawal status
}
for (var j = 0; j < orders.length; j++) { // Iterate through the array of unfilled orders
exchange.CancelOrder(orders[j].Id); // Cancel unfilled orders in sequence
ret = true;
if (j < (orders.length - 1)) {
Sleep(1000); // Sleep for 1 second
}
}
}
}
El módulo de retiro es más sencillo.
// Backtest environment
/*backtest
start: 2018-01-01 00:00:00
end: 2018-08-01 11:00:00
period: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
*/
// Order withdrawal function
function CancelPendingOrders() {
Sleep(1000); // Sleep for 1 second
var ret = false;
while (true) {
var orders = null;
// Obtain the unsettled order array continuously. If an exception is returned, continue to obtain
while (!(orders = exchange.GetOrders())) {
Sleep(1000); // Sleep for 1 second
}
if (orders.length == 0) { // If the order array is empty
return ret; // Return to order withdrawal status
}
for (var j = 0; j < orders.length; j++) { // Iterate through the array of unfilled orders
exchange.CancelOrder(orders[j].Id); // Cancel unfilled orders in sequence
ret = true;
if (j < (orders.length - 1)) {
Sleep(1000); // Sleep for 1 second
}
}
}
}
// Order function
function onTick() {
var acc = _C(exchange.GetAccount); // obtain account information
var ticker = _C(exchange.GetTicker); // obtain Tick data
var spread = ticker.Sell - ticker.Buy; // obtain bid ask spread of Tick data
// 0.5 times of the difference between the account balance and the current position value
var diffAsset = (acc.Balance - (acc.Stocks * ticker.Sell)) / 2;
var ratio = diffAsset / acc.Balance; // diffAsset / account balance
LogStatus('ratio:', ratio, _D()); // Print ratio and current time
if (Math.abs(ratio) < threshold) { // If the absolute value of ratio is less than the specified threshold
return false; // return false
}
if (ratio > 0) { // if ratio > 0
var buyPrice = _N(ticker.Sell + spread, ZPrecision); // Calculate the order price
var buyAmount = _N(diffAsset / buyPrice, XPrecision); // Calculate the order quantity
if (buyAmount < MinStock) { // If the order quantity is less than the minimum trading quantity
return false; // return false
}
exchange.Buy(buyPrice, buyAmount, diffAsset, ratio); // buy order
} else {
var sellPrice = _N(ticker.Buy - spread, ZPrecision); // Calculate the order price
var sellAmount = _N(-diffAsset / sellPrice, XPrecision); // Calculate the order quantity
if (sellAmount < MinStock) { // If the order quantity is less than the minimum trading quantity
return false; // return false
}
exchange.Sell(sellPrice, sellAmount, diffAsset, ratio); // sell order
}
return true; // return true
}
// main function
function main() {
// Filter non-important information
SetErrorFilter("GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout");
while (true) { // Polling mode
if (onTick()) { // Execute onTick function
CancelPendingOrders(); // Cancel pending orders
Log(_C(exchange.GetAccount)); // Print current account information
}
Sleep(LoopInterval * 1000); // sleep
}
}
Parámetros externos
A continuación, vamos a probar esta simple estrategia de equilibrio dinámico para ver si funciona.
Entorno de pruebas de retroceso
Desempeño de las pruebas de retroceso
Curva de pruebas de retroceso
Durante el período de backtest, BTC ha continuado disminuyendo durante hasta 8 meses, incluso con una disminución máxima de más del 70%, lo que hizo que muchos inversores perdieran la confianza en los activos de blockchain.
El código fuente de la estrategia se ha publicado en el sitio web oficial de FMZ Quant:https://www.fmz.com/strategy/110545No hay necesidad de configurar, se puede backtesting en línea directamente.
La estrategia de equilibrio dinámico en este artículo tiene solo un parámetro central (umbral), que es un método de inversión muy simple. Lo que persigue no es un rendimiento excesivo, sino un rendimiento constante. Contrariamente a la estrategia de tendencia, la estrategia de equilibrio dinámico es contra la tendencia. Pero la estrategia de equilibrio dinámico es justo lo contrario. Cuando el mercado es popular, reducir la posición, mientras que cuando el mercado es impopular, escalar en la posición, que es similar a la regulación macroeconómica.
De hecho, la estrategia de balance dinámico es un artefacto que hereda el concepto de precios impredecibles y captura las fluctuaciones de precios al mismo tiempo. El núcleo de la estrategia de balance dinámico es establecer y ajustar la relación de asignación de activos, así como el umbral de activación. En vista de la longitud, un artículo no puede ser completo. Debes saber que más allá de las palabras, hay un corazón. La parte más importante de la estrategia de balance dinámico es la idea de inversión. Incluso puedes reemplazar los activos individuales de BTC en este artículo con una cesta de carteras de activos blockchain.
Finalmente, cerremos este artículo con las famosas palabras de Benjamin Graham en el libro