Benjamin Graham, Warren Buffett’s mentor, has mentioned a dynamic balance trading mode of stocks and bonds in the book “The Intelligent Investor”.
The trading mode is very simple: -Invest 50% of the funds in stock funds and the remaining 50% in bond funds. That is, stocks and bonds account for half of each other. -According to the fixed interval or market changes, carry out an asset rebalancing to restore the proportion of stock assets and bond assets to the original 1:1. This is the logic of the whole strategy, including when to buy and sell and how much to buy and sell. It’s easy enough!
In this method, the volatility of bond funds is very small actually, far lower than the volatility of stocks, so bonds are used as “reference anchors” here, that is, to measure whether stocks have risen too much or too little by bonds. If the stock price rises, the market value of the stock will be greater than the market value of the bonds. When the market value ratio of the two exceeds the threshold set, the total position will be readjusted, the stocks will be sold, and the bonds will be purchased, so that the market value ratio of the stocks and bonds will return to the original 1:1. On the contrary, if the stock price decreases, the market value of the stock will be smaller than the market value of the bonds. When the market value ratio of the two exceeds the threshold set, the total position will be readjusted, stocks will be bought, and bonds will be sold, so that the market value ratio of stocks and bonds will return to the original 1:1. In this way, we can enjoy the fruits of stock growth and reduce asset volatility by balancing the proportion between stocks and bonds dynamically. As a pioneer of value investment, Graham has provided us with a good idea. Since this is a complete strategy, why don’t we use it in the digital currency?
Dynamic balance strategy in blockchain asset BTC
Strategy logic
In this way, no matter whether BTC appreciates or depreciates, we always keep the account balance and BTC’s market value equal dynamically. If the BTC depreciates, we buy, and if it rises again, we sell some, just like a balance.
So, how to implement it in code? We take the FMZ Quant Trading Platform as an example, let’s take a look at the strategy framework first:
// 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
}
}
The entire strategy framework is very simple actually, including a main function, an onTick order-placing function, a CancelPendingOrders function, and the necessary parameters.
// 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
}
The order trading logic is well organized, and all comments have been written into the code. You can click the image to zoom in.
The main process is as follows:
// 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
}
}
}
}
The withdrawal module is simpler. The steps are as follows:
// 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
}
}
External parameters
Next, let’s test this simple dynamic balancing strategy to see if it works. The following is a backtest on the historical data of BTC for reference only.
Backtesting environment
Backtesting Performance
Backtesting curve
During the backtest period, BTC has continued to decline for up to 8 months, even with a maximum decline of more than 70%, which caused many investors to lose confidence in blockchain assets. The cumulative return of this strategy is up to 160%, and the annualized return risk ratio exceeds 5. For such a simple investment strategy, the rate of return on investment has exceeded that of most people who are in a full position.
The strategy source code has been published on the FMZ Quant official website: https://www.fmz.com/strategy/110545. There is no need to configure, you can backtesting online directly.
The dynamic balance strategy in this article has only one core parameter (threshold), which is a very simple investment method. What it pursues is not excess return, but steady return. Contrary to the trend strategy, the dynamic balance strategy is against the trend. But the dynamic balance strategy is just the opposite. When the market is popular, reducing the position, while when the market is unpopular, scaling in the position, which is similar to macroeconomic regulation.
In fact, the dynamic balance strategy is a craft that inherits the concept of unpredictable prices and captures price fluctuations at the same time. The core of the dynamic balance strategy is to set and adjust the asset allocation ratio, as well as the trigger threshold. In view of the length, an article can not be comprehensive. You should know that beyond the words, there is a heart. The most important part of the dynamic balance strategy is the investment idea. You can even replace the individual BTC assets in this article with a basket of blockchain asset portfolios.
Finally, let’s close this article with Benjamin Graham’s famous words in the book “The Intelligent Investor”: The stock market is not a “weighing machine” that can measure value accurately, but rather a “voting machine”. The decisions made by countless people are a mixture of rationality and sensibility. Many times these decisions are far from rational value judgments. The secret of investment is to invest when the price is far lower than the intrinsic value, and believe that the market trend will rebound. ——Benjamin Graham “The Intelligent Investor”