Bitcoin Fear and Greed Index: Nützlich als Vorhersage-Tool?

Der Bitcoin Fear and Greed Index ist sicherlich einer der bekanntesten Sentiment-Indikatoren für den Bitcoin, aber ist er tatsächlich hilfreich, wenn es darum geht, zukünftige Kursentwicklungen vorherzusagen? Man könnte natürlich leicht eigene Varianten eines Trend- oder Sentiment-Indikators erstellen, aber wir wollen an dieser Stelle einmal testen, wie gut das „Original“ von alternative.me sich zur Vorhersage des Bitcoinkurses eignen könnte.

Der Bitcoin Fear and Greed Index scheint sich, wie sich herausstellen wird, tatsächlich überraschend gut zur Vorhersage des Bitcoinkurses zu eignen. Dazu sehen wir uns in diesem Artikel Faktor-Plots, den Backtest einer beispielhaften Strategie auf Basis des Index und zur Visualisierung der Effektivität die Resultate einer linearen Strategie an. Problematisch ist die intransparente Berechnung.

Bitcoin Fear and Greed Index: Korrelation mit Bitcoin-Preis?

Vorweg muss gesagt werden, dass es hier einige Einschränkungen zu beachten gilt: 

  • Der Index ist anscheinend closed source, daher ist schwer zu sagen, wie genau sich die Indexwerte zusammensetzen.
  •  Zusätzlich ist selbstverständlich möglich, dass der Index nachträglich an die Entwicklung des Bitcoinkurses angepasst wurde, also nachdem die Preise bekannt waren (Lookahead Bias / Backfitting).
  • Zwischenzeitlich wurde die Berechnung des Index geändert. Ursprünglich waren offenbar noch Umfragedaten enthalten, die aber mittlerweile nicht mehr Teil des Index sind.

Es ist selten eine gute Idee, eine Analyse zu starten, ohne wenigstens eine vage Hypothese zu haben. Was wäre also hier, zumal es sich um einen Sentiment-Index handelt, eine mögliche Hypothese? 

Be fearful when others are greedy, and greedy when others are fearful.

Warren Buffet

In der Regel nimmt man an, dass große Euphorie vor einem Top auftritt und spiegelbildlich große Verzweiflung vor einem Tief der Börsenkurse. Wir würden also erwarten, ohne die Daten bisher analysiert zu haben, dass niedrige Werte des Fear and Greed Index Kaufgelegenheiten darstellen – umgekehrt wären entsprechend Werte des Index im „Euphoriebereich“ potenzielle Verkaufssignale. Finden wir dieses Muster in den Daten?

Daten für den Bitcoin Fear and Greed Index herunterladen

Erfreulicherweise können die historischen Daten zum Bitcoin Fear and Greed Index direkt per API heruntergeladen werden. 

Der Index schwankt zwischen Werten von 0 bis 100, wobei in der bisherigen Historie minimal Werte von 5 und Maximalwerte von 95 erreicht wurden. Der Index beginnt Anfang des Jahres 2018. Wir haben also mittlerweile genug Daten, um den Index sinnvoll testen zu können. 

R Code (öffnen)


library(tidyverse)

dl <- read_lines("https://api.alternative.me/fng/?limit=2000&format=json&date_format=us")

library(rjson)
library(lubridate)

dl2 <- fromJSON(paste0(dl, collapse = " "))

dl2 <- map_dfr(dl2$data, function(x) {
    tibble(date = mdy(x$timestamp), 
           value = as.numeric(x$value))
})

ggplot(dl2, aes(x = date, y = value)) +
    geom_point() +
    scale_y_continuous(breaks = scales::pretty_breaks(n = 20)) +
    geom_line() +
    theme_bw(base_size = 25) +
    ylab("") +
    xlab("") +
    ggtitle("Bitcoin Fear and Greed Index")Code-Sprache: PHP (php)
OK: Unsere Grafik entspricht der Darstellung bei alternative.me

Bitcoin Kursdaten herunterladen

Nun brauchen wir natürlich zusätzlich den Bitcoinpreis seit 2018. Wir nutzen hier das tidyquant-Paket für R, um diese Daten von Tiingo herunterzuladen. Das ist nach kostenloser Registrierung möglich, falls du diese Analyse reproduzieren willst. Die Daten der API sind im JSON-Format, müssen also zunächst in ein Tabellenformat umgewandelt werden, bevor wir die Daten plotten können.

In dieser Grafik haben wir einmal den Bitcoin Kurs seit 2018 bis April 2022 dargestellt und jeden Tag entsprechend des Wertes im Fear and Greed Index eingefärbt. Man sieht recht deutlich, dass in den zwischenzeitlichen Tiefs (Crashs) des Bitcoins die Indexwerte niedrig waren. In den substantiellen Bullruns hingegen lagen die Werte bei über 50 oder sogar über 75, wie Anfang 2021. 

R Code (öffnen)

library(tidyquant)
tiingo_api_key('your_key')
btc <- tq_get(c("btcusd"),
       get    = "tiingo.crypto",
       from   = "2018-01-01",
       to     = "2022-04-29",
       resample_frequency = "1day")

btc_plot <- left_join(
    btc, 
    dl2
) %>% 
    drop_na() %>% 
    rename(`Fear and Greed Index` = value)

ggplot(btc_plot, aes(x = date, y = close, color = `Fear and Greed Index`)) +
    geom_line(size = 3) +
    geom_point(size = 3) +
    theme_classic(base_size = 25) +
    scale_y_log10(breaks = scales::pretty_breaks(n = 10)) +
    scale_color_viridis_c() +
    ggtitle("Bitcoin 2018 - heute", "Log-Skala") Code-Sprache: PHP (php)

Analyse des Bitcoin Fear and Greed Index: Datenvorbereitung

Nun erstellen wir die Lags (also Vergangenheitswerte) 1,5, …, 300 für den Fear and Greed Index. Um anschließend den Zusammenhang zwischen bestimmten Indexwerten in der Vergangenheit mit dem Bitcoinkurs untersuchen zu können, erstellen wir ähnliche Lags des Bitcoinkurses, aber einen Tag in die Zukunft verschoben. Somit können wir z.B. den gestrigen Indexwert mit dem heutigen Bitcoinkurs in Verbindung setzen.

Das Gleiche möchten wir natürlich nicht nur für einen Tag machen, sondern auch für größere Zeiträume, daher die Lags bis zu 300 Tagen. Beispielsweise ist fg_300 der Wert des Fear and Greed Index vor 300 Tagen und btc_return_300 die Bitcoin-Rendite in den 300 darauffolgenden Tagen. So können wir stets passende Zeiträume miteinander in Verbindung setzen.

R Code (öffnen)

dl2 <- dl2 %>% 
    arrange(date) %>% 
    mutate(
        fg_1 = dplyr::lag(value, 1),
        fg_5 = dplyr::lag(value, 5),
        fg_10 = dplyr::lag(value, 10),
        fg_20 = dplyr::lag(value, 20),
        fg_40 = dplyr::lag(value, 40),
        fg_300 = dplyr::lag(value, 300)
    ) %>% 
    rename(fg = value)

btc2 <- btc %>% 
    select(date, close) %>% 
    mutate(date = as_date(date))

dat <- dl2 %>% 
    left_join(btc2) %>% 
    arrange(date) %>% 
    mutate(
        btc_return_1 = lead(close) / close - 1,
        btc_return_5 = lead(close) / lag(close, 4) - 1,
        btc_return_10 = lead(close) / lag(close, 9) - 1,
        btc_return_20 = lead(close) / lag(close, 19) - 1,
        btc_return_40 = lead(close) / lag(close, 39) - 1,
        btc_return_300 = lead(close) / lag(close, 299) - 1
    )Code-Sprache: PHP (php)

Scatterplots des Bitcoin Fear and Greed Index vs. Bitcoinkurs

Wenn wir für jeden Tag in den Daten jetzt den Vergangenheitswert des Index mit der entsprechenden Bitcoin-Rendite als Streudiagramm darstellen, ergeben sich die folgenden Grafiken. Wir haben hier einfach einmal Lag 20 benutzt, hätten aber auch z.b. 10 oder 40 nehmen können.

Überraschend ist, dass wir hier in den meisten Grafiken eine U-Form vorfinden. Wie eingangs erwähnt hätten wir vielmehr erwartet, dass niedrige Indexwerte zu hohen Renditen führen, während hohe Indexwerte zu niedrigen Renditen führen. Also eher eine Art fallende Gerade. In den Daten zeigt sich nun allerdings, dass anscheinend auch hohe Indexwerte hohe Bitcoin-Renditen implizieren. Bei Indexwerten um 50 ist die erwartete Rendite nahe null.

R Code (öffnen)

ggplot(dat %>% 
         mutate(btc_return_20 = btc_return_20 * 100), 
       aes(x = fg_20, y = btc_return_20)) +
    geom_point(size = 3, alpha = 0.3) +
    geom_smooth(size = 3, se = FALSE) +
    theme_classic() +
    xlim(0, 100) +
    xlab("Bitcoin Fear and Greed Index") +
    ylab("Bitcoin-Rendite (%) 20 Tage später")Code-Sprache: PHP (php)

Wie stabil ist der Zusammenhang zwischen Bitcoin Fear and Greed und dem Kurs?

Eine andere interessante Darstellung sind Binned Barplots. Hier sehen wir die mittlere Bitcoin Rendite, abhängig davon, in welchem Bereich sich die Indexwerte befinden. Auch hier sehen wir wieder die U-Form. Zum Beispiel ist die erwartete Rendite in den nächsten 20 Tagen, wenn der Fear and Greed Index zwischen 10 und 20 steht, bei etwa 8%.

R Code (öffnen)

fg_20_bins <- dat %>% 
    mutate(fg_20_bin = cut(x = fg_20, breaks = seq(from = 0, to = 100, by = 10))) %>% 
    group_by(fg_20_bin) %>% 
    summarise(mean_ret = mean(btc_return_20, na.rm = T)) %>% 
    mutate(mean_ret = mean_ret * 100) %>% 
    ungroup %>% 
    drop_na()
        
ggplot(fg_20_bins, aes(x = fg_20_bin, y = mean_ret)) + 
    geom_bar(stat = "identity") +
    xlab("Fear and Greed Index Bereiche") +
    ylab("Mittlere BTC-Rendite (%)") +
    ggtitle("Zusammenhang zwischen BTC und Bereichen des Fear and Greed Index", "lag = 20") +
    theme_classic()Code-Sprache: JavaScript (javascript)

Darüber hinaus ist immer interessant zu sehen, ob diese Effekte auch über die Jahre hinweg stabil sind. Hier wird man niemals perfekte Stabilität vorfinden, aber in diesem Fall sieht man immerhin in den meisten Jahren wieder recht gut die U-Form. Einige Ausreißer könnten darin begründet sein, dass es in manchen Jahren bestimmte Indexwerte nur sehr selten gab. Beispielsweise gab es im Jahr 2019 nur zwei Tage mit Indexwerten über 90.

R Code (öffnen)

fg_20_bins <- dat %>% 
    mutate(year = year(date)) %>% 
    mutate(fg_20_bin = cut(x = fg_20, breaks = seq(from = 0, to = 100, by = 10))) %>% 
    group_by(fg_20_bin, year) %>% 
    summarise(mean_ret = mean(btc_return_20, na.rm = T)) %>% 
    mutate(mean_ret = mean_ret * 100) %>% 
    ungroup %>% 
    drop_na()
        
ggplot(fg_20_bins, aes(x = fg_20_bin, y = mean_ret)) + 
    geom_bar(stat = "identity") +
    xlab("Fear and Greed Index Bereiche") +
    ylab("Mittlere BTC-Rendite (%)") +
    ggtitle("Zusammenhang zwischen BTC und Bereichen des Fear and Greed Index", 
        "lag = 20") +
    theme_classic(base_size = 25) +
    theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
    facet_wrap(~year)Code-Sprache: JavaScript (javascript)

Newsletter

Backtest des Bitcoin Fear and Greed Index

Lineare Strategie

Bevor wir zu realistischeren Simulationen kommen, ist es eine gute Idee, den vermuteten Effekt möglichst isoliert in einem Backtest darzustellen. Dazu bauen wir hier eine lineare Strategie, bei der die Bitcoin-Position linear (im Sinne einer linearen Funktion) von den Indexwerten abhängt. Die Positionsgröße soll maximal sein in den Extremwerten des Index, also bei 0 und 1, und sie soll minimal sein bei Indexwerten von 50. Die U-Form im Zusammenhang zwischen Indexwerten und Bitcoin-Renditen, die wir gefunden haben, deutet ja darauf hin, dass es vorteilhaft wäre, Bitcoin immer dann zu halten, wenn sich der Index in den Extrembereichen befindet. Kurz gesagt benutzen wir also folgende Formel:

Size = abs(fear_and_greed – 50) / 50

Sizing der „linearen“ Strategie

Somit bewegt sich unsere Positionsgröße automatisch zwischen 0 und 1. Null würde bedeuten, wir halten 100% Cash, und eins würde bedeuten, wir halten zu 100% Bitcoin.

Hier haben wir zunächst noch einmal anhand der Einfärbung dargestellt, wann wir jetzt auf Basis der linearen Strategie hohe oder niedrige Bitcoin-Positionen hätten.

R Code (öffnen)

dat <- dat %>% 
    ungroup() %>% 
    mutate(lin_size = abs(fg_1 - 50) / 50) 

ggplot(dat, aes(x = date, y = close, color = lin_size)) +
    geom_line(size = 3) +
    geom_point(size = 3) +
    scale_color_viridis_c() +
    theme_classic(base_size = 25) +
    xlab("") +
    ylab("Bitcoin") +
    ggtitle("Lineare Strategie")Code-Sprache: JavaScript (javascript)

Und so sähe die Kapitalentwicklung aus, wenn man diese lineare Strategie umgesetzt hätte. Transaktionskosten sind hier nicht berücksichtigt, aber nochmal: Es geht ja zunächst einfach nur darum, den Effekt isoliert zu betrachten, bevor wir einen realistischeren Test starten.

R Code (öffnen)

dat_bt <- dat %>% 
    mutate(lin_strat_return = btc_return_1 * lin_size) %>%
    mutate(lin_strat_rev_return = btc_return_1 * (-lin_size + 1)) %>%
    select(date, close, lin_size, lin_strat_return, lin_strat_rev_return) %>% 
    drop_na() %>% 
    mutate(lin_strat_equity = cumprod(1 + lin_strat_return))

btc_ret <- btc %>% 
    mutate(btc_ret = close / dplyr::lag(close) - 1) %>% 
    select(date, btc_ret) %>% 
    drop_na()
btc_ret_xts <- xts(btc_ret$btc_ret, order.by = btc_ret$date)
strat_ret_xts <- xts(dat_bt$lin_strat_return, order.by = dat_bt$date)
strat_ret_rev_xts <- xts(dat_bt$lin_strat_rev_return, order.by = dat_bt$date)
bt_xts <- merge(btc_ret_xts, strat_ret_xts, strat_ret_rev_xts)
bt_xts <- na.omit(bt_xts)
colnames(bt_xts) <- c("Bitcoin", "Lineare Strategie", "Negative Strategie")

charts.PerformanceSummary(bt_xts, geometric = F, main = "Lineare Strategie")
# table.AnnualizedReturns(bt_xts, geometric = F, scale = 365)Code-Sprache: PHP (php)

Interessant zu sehen ist, dass die lineare Strategie auf Basis des Bitcoin Fear and Greed Index zwar niedrigere Renditen als das reine Halten von Bitcoin erzielt, aber eine deutlich höhere Sharpe-Ratio hat (1.18 anstelle von 0.8). Zusätzlich haben wir hier das Negativ der linearen Strategie eingebaut, bei dem also die Positionsgröße in Bitcoin bei mittleren Indexwerten maximal wäre. Wie erwartet, schneidet diese Strategie mit Abstand am schlechtesten ab.

Backtest mit fixem Cutoff für den BTC Fear and Greed Index

In der Praxis würde man ja nicht die lineare Strategie verfolgen, bei der man jeden Tag die Positionsgröße anpassen müsste, sondern vermutlich würde man einen oder mehrere Cutoffs wählen, über oder unter denen man dann Bitcoin halten würde. Andernfalls bliebe man in Cash.

Wir nehmen hier einfach einmal als Beispiel einen Cutoff von 15 und 20 Tage Haltedauer. Das heißt also, bei Indexwerten unter 15 oder über (100 – 15) kaufen wir Bitcoin und halten die Position für 20 Tage. 

Bei diesem Cutoff und dieser Haltedauer wäre die Strategie ca. zwei Drittel der Zeit komplett in Cash gewesen, den Rest der Zeit in Bitcoin.

Das Ergebnis sieht tatsächlich recht ansprechend aus. Die Strategie erzielt im Zeitraum seit 2018 sowohl höhere Renditen als auch eine wesentlich höhere Sharpe-Ratio als das reine Halten von Bitcoin. Aufgrund der wenigen Transaktion sollten hier auch Transaktionskosten nicht ins Gewicht fallen, weshalb wir diese Kosten hier nicht simuliert haben. Das Negativ der Strategie ist hier noch deutlich schlechter als im linearen Test und hat sogar eine negative Rendite.

R Code (öffnen)

dat <- dat %>% 
dat_bt <- dat
dat_bt$signal <- NA

cutoff_long <- 15
cutoff_short <- 100 - 15
holding_period <- 20
for (i in 1:(nrow(dat_bt) - holding_period)) {
    if (dat_bt$fg[i] < cutoff_long | dat_bt$fg[i] > cutoff_short) {
        dat_bt$signal[(i+1):(i+holding_period)] <- 1
    }
}

dat_bt$signal[is.na(dat_bt$signal)] <- 0

dat_bt <- dat_bt %>% 
    ungroup() %>% 
    arrange(date) %>% 
    mutate(fg_ret = ifelse(signal > 0.5, btc_return_1, 0)) %>% 
    mutate(fg_ret_reverse = ifelse(signal < 0.5, btc_return_1, 0)) %>% 
    select(date, fg_ret, fg_ret_reverse) %>% 
    drop_na() %>% 
    mutate(fg_equity = cumprod(1 + fg_ret))

btc_ret <- btc %>% 
    mutate(btc_ret = close / dplyr::lag(close) - 1) %>% 
    select(date, btc_ret) %>% 
    drop_na()
btc_ret_xts <- xts(btc_ret$btc_ret, order.by = btc_ret$date)
strat_ret_xts <- xts(dat_bt$fg_ret, order.by = dat_bt$date)
strat_ret_rev_xts <- xts(dat_bt$fg_ret_reverse, order.by = dat_bt$date)
bt_xts <- merge(btc_ret_xts, strat_ret_xts, strat_ret_rev_xts)
bt_xts <- na.omit(bt_xts)

charts.PerformanceSummary(bt_xts, geometric = F)Code-Sprache: PHP (php)
BitcoinStrategieNegative Strategie
Annualized Return42%49%-7%
Annualized Std. Dev.0.630.400.48
Annualized Sharpe Ratio0.671.24-0.15
Performance-Kennzahlen der Strategien und von Bitcoin. Strategie: Cutoff = 15, Haltedauer = 20 Tage.

Fazit

Wir haben also gesehen, dass der Index in der Vergangenheit scheinbar überraschend nützlich gewesen ist. An dieser Stelle könnte man nun noch testen, ob die optimalen Cutoffs und die optimale Haltedauer über die Jahre hinweg stabil sind. Sollte euch diese Analyse ebenfalls interessieren, lass es uns gern in den Kommentaren wissen, dann liefern wir den entsprechenden Code und die Grafiken noch nach.

Wichtiger wäre es allerdings, hier erst einmal einen Schritt zurück zu machen und zu überlegen, was mit diesen Ergebnissen überhaupt anzufangen ist.

Hauptproblem bleibt, dass es sich hier um einen proprietären Index handelt, dessen Berechnung etwas intransparent ist. Zudem hat sich unsere Hypothese nicht vollständig bestätigt, dass niedrige Werte des Fear and Greed Index zu hohen Bitcoin-Renditen führen, während hohe Indexwerte zu niedrigen Bitcoin-Renditen führen. 

Stattdessen führten auch hohe Indexwerte zu hohen Renditen. Unsere Vermutung ist, dass dieser Effekt ein Artefakt eines Momentum-Faktors ist, der im Fear and Greed Index enthalten sein könnte. Letztlich ist der Bitcoin Fear and Greed Index nämlich relativ nah an klassischer Faktor-Analyse oder einigen Methoden der sogenannten technischen Analyse. 

Anstelle nun also den originalen Index unverändert zu übernehmen, könnte man einen eigenen Index auf Basis derartiger Faktoren oder (Sentiment-) Indikatoren erstellen, die etwas transparenter und nachvollziehbarer sind. 

Außerdem ist die Vermischung mehrerer Faktoren im Bitcoin Fear and Greed Index etwas problematisch, da die Performance der einzelnen Faktoren so nicht getrennt betrachtet werden kann. Zuletzt hatte es den Anschein, dass beispielsweise die Performance des Momentum-Faktors im Kryptomarkt im Verlauf der letzten Jahre etwas abnimmt. 

Zusammenfassend hat uns dennoch überrascht, wie gut der Index funktioniert hätte, sollten die Werte tatsächlich ohne Lookahead-Bias zustande gekommen sein. Eine Anfrage an alternative.me, ob die Indexwerte in der Vergangenheit angepasst wurden und wie man mit dem Entfernen der Umfragedaten umging, blieb leider unbeantwortet.

Christian Thiele
Christian Thiele

M.A. International Economics

Christian interessiert sich seit dem DotCom-Boom Ende der 1990er für die Börse und nutzt hauptsächlich die Statistik-Programmiersprache R.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert