We obtain preliminary results investigating the relation between lunar phases and cryptocurrency returns. Our findings show that trading guided by moon phases — namely, buying at a full moon phase and selling at the next new moon phase — shows significantly higher returns than other baseline methods. Further, we show an even stronger sinusoidal relationship between the moon phase in which we begin two-week trading (approximate length of half a moon cycle) and cryptocurrency market returns. We show that highest returns are achieved when we buy at a full moon phase and sell at a new moon phase, implying these phases map to local bottoms and tops, something that has long been hypothesized.
import bisect
import glob
import numpy as np
import pandas as pd
import pylunar
import re
import seaborn as sns
import statsmodels.formula.api as smf
from datetime import datetime, timedelta
from tqdm import tqdm
from matplotlib import pyplot as plt
pd.options.mode.chained_assignment = None
pd.options.display.float_format = '{:,.3f}'.format
pd.set_option("display.precision", 4)
SECONDS_IN_DAY = 86400
We consider the top 30 cryptocurrency tokens, by marketcap, listed on CoinGecko on Aug. 14, 2022, not including stablecoins or wrapped tokens. The data was gathered using the CoinGecko API. The script used to call the API is in this directory ./collect_data.py
.
DF_MAIN = pd.concat([pd.read_csv(fname) for fname in glob.glob("./data/CoinGecko_*.csv")])
We remove rows where either marketcap is zero or total volume is zero. This usually indicates to very early data, near a tokens listing.
# clean ticker symbol
DF_MAIN['ticker'] = [tkr.split('|')[0] for tkr in DF_MAIN['ticker']]
# remove rows where market cap or volume is 0
for col in ['market_caps', 'total_volumes']:
print('Removing {} rows where `{}` == 0'.format(
(DF_MAIN[col] == 0).sum(), col
))
DF_MAIN = DF_MAIN[DF_MAIN[col] != 0]
# the data *should* contain consecutive days
# sometimes this data is missing, so we put NaNs in place
# this makes later computations much simpler
def fill_missing(df):
df.sort_values('unixtime', inplace=True)
ts_start = df.iloc[0].unixtime
ts_end = df.iloc[-1].unixtime
tss = range(ts_start, ts_end, SECONDS_IN_DAY)
missing = set(tss) - set(df.unixtime)
data = []
for ts in missing:
data.append([
ts,
datetime.utcfromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S'),
df.iloc[0].ticker, np.nan, np.nan, np.nan
])
return pd.DataFrame(data, columns=df.columns)
DF_MAIN = pd.concat([
DF_MAIN.groupby('ticker').apply(fill_missing).reset_index(drop=True),
DF_MAIN
]).reset_index(drop=True)
# get latest marketcaps and sort tickers by that,
# preseving unixtime sorting
custom_dict = {
v: k
for (k, v) in enumerate(
DF_MAIN.groupby('ticker').last().total_volumes.sort_values(ascending=False).index
)
}
DF_MAIN = DF_MAIN.sort_values(
by='ticker',
key=lambda x: x.map(custom_dict)
).groupby('ticker', sort=False).apply(
lambda x: x.sort_values('unixtime')
).reset_index(drop=True)
Removing 443 rows where `market_caps` == 0 Removing 637 rows where `total_volumes` == 0
DF_MAIN
unixtime | date | ticker | prices | market_caps | total_volumes | |
---|---|---|---|---|---|---|
0 | 1388102400 | 2013-12-26 19:00:00 | BTC | 734.270 | 8,944,473,292.000 | 62,881,800.000 |
1 | 1388188800 | 2013-12-27 19:00:00 | BTC | 738.810 | 9,002,769,255.000 | 28,121,600.000 |
2 | 1388275200 | 2013-12-28 19:00:00 | BTC | 726.470 | 8,855,251,580.000 | 27,018,300.000 |
3 | 1388361600 | 2013-12-29 19:00:00 | BTC | 760.520 | 9,270,681,761.000 | 24,717,100.000 |
4 | 1388448000 | 2013-12-30 19:00:00 | BTC | 755.160 | 9,205,343,763.000 | 21,903,500.000 |
... | ... | ... | ... | ... | ... | ... |
47850 | 1658966400 | 2022-07-27 20:00:00 | LEO | 5.178 | 4,843,044,935.433 | 1,707,115.191 |
47851 | 1659052800 | 2022-07-28 20:00:00 | LEO | 5.321 | 4,965,723,084.313 | 1,898,325.579 |
47852 | 1659139200 | 2022-07-29 20:00:00 | LEO | 5.181 | 4,848,771,860.852 | 2,088,763.614 |
47853 | 1659225600 | 2022-07-30 20:00:00 | LEO | 5.056 | 4,716,690,886.729 | 1,955,159.439 |
47854 | 1659312000 | 2022-07-31 20:00:00 | LEO | 5.022 | 4,693,204,526.821 | 2,124,827.097 |
47855 rows × 6 columns
mi = pylunar.MoonInfo((51, 30, 36), (0, 7, 5)) # London, UK
def moon_phase_data(df):
def _moon_phase(date_string):
# example data string:
# "2012-06-19 14:08:00"
mi.update(tuple(map(int, re.split("-|\s+|:", date_string))))
return mi.fractional_phase()
moon_phases = df['date'].apply(_moon_phase)
s = (moon_phases.shift(1) > moon_phases) & (moon_phases.shift(-1) > moon_phases)
new_moon_idxs = s.index[s]
def _moon_cycle(idx):
return bisect.bisect(new_moon_idxs, idx)
def _day_after_new_moon(idx):
nearest_new_moon = new_moon_idxs[_moon_cycle(idx) - 1]
result = idx - nearest_new_moon
if result >= 0:
return result
return 30 - (new_moon_idxs[0] - idx)
s1 = df.apply(lambda row: _moon_cycle(row.name),axis=1)
s2 = df.apply(lambda row: _day_after_new_moon(row.name), axis=1)
return pd.DataFrame({'moon_cycle': s1, 'days_after_new_moon': s2})
if 'moon_cycle' not in DF_MAIN.columns:
DF_MAIN = DF_MAIN.join(DF_MAIN.groupby('ticker').apply(moon_phase_data))
DF_MAIN.head(10)
unixtime | date | ticker | prices | market_caps | total_volumes | moon_cycle | days_after_new_moon | |
---|---|---|---|---|---|---|---|---|
0 | 1388102400 | 2013-12-26 19:00:00 | BTC | 734.270 | 8,944,473,292.000 | 62,881,800.000 | 0 | 24 |
1 | 1388188800 | 2013-12-27 19:00:00 | BTC | 738.810 | 9,002,769,255.000 | 28,121,600.000 | 0 | 25 |
2 | 1388275200 | 2013-12-28 19:00:00 | BTC | 726.470 | 8,855,251,580.000 | 27,018,300.000 | 0 | 26 |
3 | 1388361600 | 2013-12-29 19:00:00 | BTC | 760.520 | 9,270,681,761.000 | 24,717,100.000 | 0 | 27 |
4 | 1388448000 | 2013-12-30 19:00:00 | BTC | 755.160 | 9,205,343,763.000 | 21,903,500.000 | 0 | 28 |
5 | 1388534400 | 2013-12-31 19:00:00 | BTC | 767.740 | 9,358,693,020.000 | 23,448,600.000 | 0 | 29 |
6 | 1388620800 | 2014-01-01 19:00:00 | BTC | 772.530 | 9,417,082,760.000 | 16,837,800.000 | 1 | 0 |
7 | 1388707200 | 2014-01-02 19:00:00 | BTC | 825.470 | 10,062,417,390.000 | 54,171,500.000 | 1 | 1 |
8 | 1388793600 | 2014-01-03 19:00:00 | BTC | 849.140 | 10,350,952,914.000 | 36,344,700.000 | 1 | 2 |
9 | 1388880000 | 2014-01-04 19:00:00 | BTC | 919.410 | 11,207,538,944.000 | 62,414,600.000 | 1 | 3 |
def cumulative_return(x):
return (x.iloc[-1] - x.iloc[0]) / x.iloc[0]
def mean_monthly_log_return(x):
df = pd.DataFrame({0: x, 1: x.shift(-30)})
# use mask to mark the NaNs
# so we can compute log over series
a = df.to_numpy(dtype=float)
a = np.ma.array(a, mask=np.isnan(a))
return np.mean(np.log(a[:, 1] / a[:, 0]))
def std_monthly_log_return(x):
df = pd.DataFrame({0: x, 1: x.shift(-30)})
a = df.to_numpy(dtype=float)
a = np.ma.array(a, mask=np.isnan(a))
return np.std(np.log(a[:, 1] / a[:, 0]))
df = DF_MAIN.groupby("ticker").agg(
start_date=('date', lambda x: x.values[0][:10]),
no_obs=('date', lambda x: len(x)),
cumulative_return=('prices', cumulative_return),
mean_monthly_log_return=('prices', mean_monthly_log_return),
std_monthly_log_return=('prices', std_monthly_log_return),
).sort_values('start_date')
df.style.format(formatter={
"cumulative_return": "{:,.0%}",
"mean_monthly_log_return": "{:.3f}",
"std_monthly_log_return": "{:.2f}"
})
start_date | no_obs | cumulative_return | mean_monthly_log_return | std_monthly_log_return | |
---|---|---|---|---|---|
ticker | |||||
XRP | 2013-12-26 | 3142 | 1,340% | 0.027 | 0.45 |
LTC | 2013-12-26 | 3142 | 167% | 0.008 | 0.33 |
BTC | 2013-12-26 | 3144 | 3,084% | 0.032 | 0.23 |
DOGE | 2013-12-26 | 3142 | 12,462% | 0.048 | 0.47 |
XMR | 2014-05-20 | 2997 | 6,166% | 0.043 | 0.37 |
XLM | 2014-08-05 | 2920 | 4,334% | 0.040 | 0.46 |
ETH | 2015-08-07 | 2553 | 126,296% | 0.083 | 0.39 |
ETC | 2016-07-24 | 2201 | 5,833% | 0.033 | 0.40 |
BCH | 2017-08-01 | 1828 | -82% | -0.021 | 0.40 |
BNB | 2017-09-15 | 1783 | 264,621% | 0.127 | 0.53 |
ADA | 2017-10-17 | 1751 | 1,831% | 0.052 | 0.51 |
TRX | 2017-11-08 | 1729 | 2,795% | 0.061 | 0.53 |
LINK | 2017-11-08 | 1729 | 3,306% | 0.063 | 0.42 |
OKB | 2018-06-20 | 1503 | 270% | 0.026 | 0.35 |
VET | 2018-07-26 | 1467 | 9% | 0.011 | 0.40 |
ATOM | 2019-03-15 | 1235 | 67% | 0.022 | 0.36 |
CRO | 2019-03-16 | 1234 | 64% | 0.018 | 0.36 |
MATIC | 2019-04-26 | 1193 | 16,903% | 0.117 | 0.51 |
LEO | 2019-05-22 | 1167 | 348% | 0.032 | 0.15 |
ALGO | 2019-06-21 | 1137 | -89% | -0.033 | 0.37 |
FTT | 2019-08-08 | 1089 | 1,248% | 0.085 | 0.29 |
SOL | 2020-04-10 | 843 | 4,336% | 0.150 | 0.52 |
DOT | 2020-08-22 | 709 | 94% | 0.015 | 0.40 |
UNI | 2020-09-16 | 684 | 144% | 0.024 | 0.42 |
AVAX | 2020-09-21 | 679 | 350% | 0.074 | 0.56 |
FIL | 2020-10-14 | 656 | -84% | -0.083 | 0.44 |
NEAR | 2020-11-22 | 617 | 294% | 0.066 | 0.48 |
STETH | 2020-12-23 | 586 | 175% | 0.017 | 0.30 |
FLOW | 2021-01-26 | 552 | -72% | -0.115 | 0.43 |
SHIB | 2021-05-15 | 443 | -25% | 0.015 | 0.55 |
We first look at 14-day trading cycles. That is, we consider the returns when we buy a token and then sell it 14 days later. Note that 14 days is the approximate length of half a moon cycle — there are ~29.5 days between consecutive new moons and there are ~14 days between a new moon and the next full moon. Here, our x-axis is the moon phase of the purchase date, represented as the number of days after the most recent new moon (0 - 28 days); i.e. 0 represents a new moon date and 28 is also approximately a new moon date.
Note that the majority of plots show a peak near the middle (approx. x = 14
) which corresponds to trades where buys are executed at full moons (approx. 14 days after a new moon) and sells are executed at the following new moon. This is precisely what has been hypothesized elsewhere, namely that full moons indicate local bottoms and new moons indicate local tops.
Further, we observe what appears to a sinusoidal relationship between our returns and the distance of our purchase date from the nearest new moon. This is tested and confirmed further down in the notebook.
Also: We also plot the mean daily log returns for these in the appendix.
def _n_day_cumulative_returns(df, n):
df = pd.DataFrame({0: df.prices, 1: df.prices.shift(-n)})
#print(df)
a = df.to_numpy(dtype=float)
#print(a[:, 1] - a[:, 0])
return pd.Series((a[:, 1] - a[:, 0]) / a[:, 0])
def _n_day_mean_daily_log_returns(df, n):
df2 = pd.DataFrame({0: df.prices, 1: df.prices.shift(-1)})
a = df2.to_numpy(dtype=float)
a = np.ma.array(a, mask=np.isnan(a))
daily_log = pd.Series(np.log(a[:, 1] / a[:, 0]), name='daily_log')
return daily_log.index.to_series().apply(
lambda x: np.mean(daily_log.iloc[x:x+n])
)
for n in [14, 30, 60]:
for fn in [_n_day_cumulative_returns, _n_day_mean_daily_log_returns]:
key = '{}{}'.format(n, fn.__name__[2:])
DF_MAIN[key] = DF_MAIN.groupby('ticker', sort=False).apply(lambda x: fn(x, n)).values
g = sns.FacetGrid(DF_MAIN, col="ticker", col_wrap=5, height=3, aspect=1, xlim=(0, 28), sharey=False)
g.set_axis_labels('')
g.map(sns.lineplot, "days_after_new_moon", "14_day_cumulative_returns", color='maroon');
g.map(lambda y, **kw: plt.axhline(y.mean(), color="k", linewidth=1), "14_day_cumulative_returns");
#g.set(title='Cumulative Returns on 14-day trades vs moon cycle (mean return marked as horizontal line)')
g.set_axis_labels("days_after_new_moon");
As a bit of a visual baseline, we look at the same plots but with 29-day trading cycles; that is, we look at returns for trades where buy and sells occur at equal and consecutive phases of the moon. E.g. 0 on the x-axis represents cumulative returns where the buy and sell occur at consecutive new moons (29 days apart).
Note that hardly any of the graphs display a sinusoidal pattern (like those above) and, in fact, are mostly flat. Again, this is formally tested later on. The visual difference between 14-day and 30-day cycle plots is even more stricking when looking at mean daily log returns (see Appendix).
g = sns.FacetGrid(DF_MAIN, col="ticker", col_wrap=5, height=3, aspect=1, xlim=(0, 28), sharey=False)
g.map(sns.lineplot, "days_after_new_moon", "30_day_cumulative_returns", color='maroon');
A sinusoidal model is estimated to test for the cyclical pattern of the lunar effect. According to our model, the lunar effect reaches its peak at the time of full moon (approx 14 days after a new moon) and declines as it nears the time of new moon, following a cosine curve with a period of 29.5 days (the approximate length of a lunar cycle).
The following regression is estimated for each cryptocurrency $t$:
$$R_{t} = \alpha + \beta \cos \left(\dfrac{2 \pi d_{t}}{29.5} \right) + e_{t} + \text{CalendarControls}_t$$where $\text{CalendarControls}$ controls for: the month, the day of the month, and the day of the week for the buy and sell dates, and their respective interactions. These controls are added beause some skeptics may argue that significant results — showing a correlation between moon cycles and cryptocurrency markets — has something to do with option expiries or something else that correlates with monthly/weekly cycles (even though the moon phases aren't synced with calendar weeks/months...).
The results indicate a significant cyclical lunar pattern for returns of 14-day trades. Furthermore, almost all significant results for 14-day trades have a negative coefficient implying a maximum return occurs when we purchase at a full moon and sell at the following new moon.
Note: We use 30-day and 60-day cycles as baselines because 14-day cycles do show a sinusoidal effect, meaning selecting baslines of, say, 7-day cycles would be affected by the 14-day cycle. Put another way: $n$-day trading cycles, where $n$ is a multiple of 30, means that tokens are bought and sold on the same moon phase, and thus are unaffected by the effect of buying/selling on different moon phases.
# augment data with controls
# augment data with week/month data
# day_of_week, buy
DF_MAIN["day_of_week_buy"] = DF_MAIN["unixtime"].apply(lambda x: datetime.utcfromtimestamp(x).weekday())
# day of week, sell
DF_MAIN["day_of_week_sell"] = DF_MAIN["unixtime"].apply(lambda x: (datetime.utcfromtimestamp(x) + timedelta(days=14)).weekday())
# month, buy
DF_MAIN["month_buy"] = DF_MAIN["unixtime"].apply(lambda x: datetime.utcfromtimestamp(x).month)
# month, sell
DF_MAIN["month_sell"] = DF_MAIN["unixtime"].apply(lambda x: (datetime.utcfromtimestamp(x) + timedelta(days=14)).month)
# day_of_month, buy
DF_MAIN["day_of_month_buy"] = DF_MAIN["unixtime"].apply(lambda x: datetime.utcfromtimestamp(x).day)
# day_of_month, sell
DF_MAIN["day_of_month_sell"] = DF_MAIN["unixtime"].apply(lambda x: (datetime.utcfromtimestamp(x) + timedelta(days=14)).day)
def _run_experiment(df):
result = []
df['cos_trans'] = np.cos(2 * np.pi * df['days_after_new_moon'] / 29.53)
for n in [14, 30, 60]:
for fn in [_n_day_cumulative_returns, _n_day_mean_daily_log_returns]:
key = '{}{}'.format(n, fn.__name__[2:])
df[key] = fn(df, n).values
formula = (
'Q("{}")'
' ~ cos_trans'
' + C(day_of_week_buy)'
' + C(month_buy) + C(month_buy) + C(month_buy) * C(month_sell)'
' + C(day_of_month_buy) + C(day_of_month_sell) + C(day_of_month_buy) * C(day_of_month_sell)'
).format(key)
reg = smf.ols(formula=formula, data=df).fit()
result.extend([
reg.params.cos_trans,
reg.tvalues.cos_trans,
reg.pvalues.cos_trans
])
return pd.Series(result)
DF_RESULT = DF_MAIN.groupby('ticker', sort=False).apply(_run_experiment)
tickers = DF_MAIN.groupby('ticker').last().total_volumes.sort_values(ascending=False).index
DF_RESULT.index=pd.Index(tickers, name='Ticker:')
DF_RESULT.columns = pd.MultiIndex.from_product([
['14 Day', '30 Day', '60 Day'],
['Cumulative', 'Mean Daily Log'],
['coef', 't', 'P>|t|']
], names=['Window:', 'Return:', 'Metric:']
)
#DF_RESULT
# TODO: hide this code; it's just styling stuff ...
###
multiindex = pd.MultiIndex.from_product([
['14 Day', '30 Day', '60 Day'],
['Cumulative', 'Mean Daily Log'],
['coef', 't', 'P>|t|']
])
s = DF_RESULT.style.format(formatter={
t:(
"{:.1f}" if t[2] == 't'
else "{:.4f}" if t[2] == 'P>|t|'
else "{:.4f}" if t[1] == 'Mean Daily Log'
else "{:.2f}"
)
for t in multiindex
})
s.columns = pd.MultiIndex.from_product([
['14 Day', '30 Day', '60 Day'],
['Cumulative', 'Mean Daily Log'],
['coef', 't', 'P>|t|']
], names=['Window:', 'Return:', ''])
s.set_table_styles([
{'selector': '.index_name', 'props': 'font-weight:normal; font-weight: normal;'},
{'selector': 'th.row_heading', 'props': 'font-weight:bold; text-align: center;'},
{'selector': 'th.col_heading', 'props': 'text-align: center;'},
{'selector': 'th.col_heading.level0', 'props': 'font-size: 1.5em; border-bottom: 1px solid darkgrey;'},
{'selector': 'th.col_heading.level1', 'props': 'font-size: 1.2em; border-bottom: 1px solid darkgrey;'},
{'selector': 'th.col_heading.level2', 'props': 'font-size: 1.2em; border-bottom: 1px solid darkgrey;'},
{'selector': 'td', 'props': 'text-align: center; font-weight: normal;'},
{'selector': 'th:not(.index_name)', 'props': 'background-color: black; color: white;'}
])
s.set_table_styles({
('30 Day', 'Cumulative', 'coef'): [
{'selector': 'th', 'props': 'border-left: 2px solid white'},
{'selector': 'td', 'props': 'border-left: 2px solid black'}
],
('60 Day', 'Cumulative', 'coef'): [
{'selector': 'th', 'props': 'border-left: 2px solid white'},
{'selector': 'td', 'props': 'border-left: 2px solid black'}
],
('14 Day', 'Mean Daily Log', 'coef'): [
{'selector': 'td', 'props': 'border-left: 1px solid black'}
],
('30 Day', 'Mean Daily Log', 'coef'): [
{'selector': 'td', 'props': 'border-left: 1px solid black'}
],
('60 Day', 'Mean Daily Log', 'coef'): [
{'selector': 'td', 'props': 'border-left: 1px solid black'}
]
}, overwrite=False, axis=0)
def highlight_pvalues(s):
def _color(pvalue):
if pvalue < 0.001:
return "FCF947"
if pvalue < 0.01:
return "FDFA75"
if pvalue < 0.05:
return "FEFDBA"
if pvalue < 0.1:
return "FFFEE8"
else:
return ""
props = []
for x in ['14 Day', '30 Day', '60 Day']:
for y in ['Cumulative', 'Mean Daily Log']:
pvalue = s[x, y, 'P>|t|']
props.extend(['background-color:#{}'.format(_color(pvalue))] * 3)
return props
s.apply(highlight_pvalues, axis=1)
Window: | 14 Day | 30 Day | 60 Day | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Return: | Cumulative | Mean Daily Log | Cumulative | Mean Daily Log | Cumulative | Mean Daily Log | ||||||||||||
Metric: | coef | t | P>|t| | coef | t | P>|t| | coef | t | P>|t| | coef | t | P>|t| | coef | t | P>|t| | coef | t | P>|t| |
Ticker: | ||||||||||||||||||
BTC | 0.00 | 0.3 | 0.7573 | 0.0002 | 0.8 | 0.4400 | 0.00 | 0.6 | 0.5324 | 0.0001 | 0.3 | 0.7306 | 0.00 | 0.2 | 0.8171 | -0.0000 | -0.2 | 0.8029 |
ETH | -0.02 | -2.8 | 0.0057 | -0.0012 | -2.6 | 0.0107 | 0.01 | 0.4 | 0.6768 | -0.0001 | -0.3 | 0.7370 | 0.01 | 0.3 | 0.7464 | -0.0003 | -1.0 | 0.3042 |
XRP | 0.02 | 1.4 | 0.1759 | 0.0005 | 0.9 | 0.3746 | -0.03 | -1.3 | 0.2086 | 0.0000 | 0.1 | 0.9452 | -0.11 | -1.3 | 0.1976 | -0.0001 | -0.2 | 0.8070 |
ETC | -0.05 | -4.7 | 0.0000 | -0.0018 | -3.3 | 0.0011 | 0.03 | 1.7 | 0.0935 | 0.0001 | 0.4 | 0.7214 | 0.04 | 1.2 | 0.2115 | -0.0001 | -0.3 | 0.7451 |
FIL | -0.06 | -3.8 | 0.0002 | -0.0015 | -1.1 | 0.2561 | -0.01 | -0.3 | 0.7749 | 0.0033 | 3.1 | 0.0023 | -0.04 | -0.5 | 0.6149 | 0.0039 | 3.7 | 0.0003 |
BNB | 0.21 | 2.7 | 0.0070 | 0.0022 | 2.4 | 0.0167 | 0.10 | 1.1 | 0.2560 | 0.0001 | 0.2 | 0.8202 | 0.80 | 2.1 | 0.0336 | 0.0006 | 1.5 | 0.1248 |
SOL | -0.08 | -4.8 | 0.0000 | -0.0053 | -5.7 | 0.0000 | 0.01 | 0.3 | 0.7611 | 0.0002 | 0.3 | 0.7741 | -0.03 | -0.4 | 0.7261 | 0.0001 | 0.1 | 0.8928 |
MATIC | 0.02 | 0.8 | 0.4146 | -0.0000 | -0.0 | 0.9967 | 0.06 | 1.6 | 0.1020 | 0.0011 | 1.7 | 0.0869 | 0.06 | 0.7 | 0.5005 | 0.0006 | 1.2 | 0.2446 |
DOT | -0.05 | -4.0 | 0.0001 | -0.0042 | -4.8 | 0.0000 | 0.03 | 1.0 | 0.3040 | 0.0006 | 0.9 | 0.3495 | 0.07 | 1.2 | 0.2506 | 0.0007 | 1.3 | 0.1999 |
BCH | -0.03 | -2.7 | 0.0065 | -0.0016 | -2.5 | 0.0132 | 0.01 | 0.9 | 0.3925 | -0.0001 | -0.3 | 0.7701 | 0.04 | 1.4 | 0.1559 | -0.0002 | -0.5 | 0.5884 |
ADA | 0.02 | 1.4 | 0.1712 | 0.0009 | 1.2 | 0.2168 | 0.06 | 1.1 | 0.2560 | 0.0000 | 0.1 | 0.9331 | -0.03 | -0.2 | 0.8419 | 0.0000 | 0.1 | 0.9057 |
LINK | -0.01 | -1.4 | 0.1562 | -0.0008 | -1.2 | 0.2335 | 0.02 | 0.8 | 0.4102 | 0.0005 | 1.0 | 0.3101 | 0.05 | 1.6 | 0.1058 | 0.0001 | 0.2 | 0.8419 |
LTC | -0.00 | -0.4 | 0.7029 | -0.0000 | -0.1 | 0.9347 | 0.01 | 1.0 | 0.3240 | 0.0001 | 0.5 | 0.6205 | 0.01 | 0.3 | 0.7376 | -0.0000 | -0.2 | 0.8286 |
AVAX | -0.18 | -7.0 | 0.0000 | -0.0105 | -8.8 | 0.0000 | 0.05 | 1.2 | 0.2335 | -0.0010 | -1.1 | 0.2543 | 0.21 | 1.7 | 0.0882 | -0.0007 | -1.1 | 0.2774 |
TRX | -0.12 | -3.4 | 0.0007 | -0.0007 | -0.9 | 0.3848 | -0.30 | -2.1 | 0.0352 | 0.0003 | 0.5 | 0.6096 | 0.13 | 0.8 | 0.4467 | 0.0002 | 0.7 | 0.5007 |
DOGE | -0.03 | -2.3 | 0.0220 | -0.0009 | -1.6 | 0.1049 | 0.02 | 0.8 | 0.4119 | 0.0000 | 0.1 | 0.9120 | 0.07 | 1.3 | 0.1873 | -0.0001 | -0.3 | 0.7794 |
SHIB | -0.10 | -2.6 | 0.0108 | -0.0052 | -3.0 | 0.0025 | -0.03 | -0.3 | 0.7397 | -0.0004 | -0.4 | 0.6644 | -0.01 | -0.1 | 0.9480 | 0.0001 | 0.3 | 0.7634 |
ATOM | -0.05 | -4.4 | 0.0000 | -0.0030 | -4.4 | 0.0000 | 0.01 | 0.9 | 0.3837 | 0.0003 | 0.7 | 0.5028 | 0.02 | 0.7 | 0.5004 | -0.0000 | -0.1 | 0.9565 |
UNI | -0.08 | -4.7 | 0.0000 | -0.0062 | -6.2 | 0.0000 | -0.02 | -0.5 | 0.5971 | -0.0003 | -0.5 | 0.6528 | -0.00 | -0.0 | 0.9694 | -0.0004 | -0.7 | 0.5077 |
NEAR | -0.09 | -5.3 | 0.0000 | -0.0061 | -5.7 | 0.0000 | 0.04 | 1.5 | 0.1438 | -0.0004 | -0.6 | 0.5434 | 0.11 | 2.0 | 0.0464 | -0.0005 | -0.9 | 0.3709 |
XLM | 0.04 | 2.3 | 0.0237 | 0.0002 | 0.4 | 0.6975 | -0.01 | -0.3 | 0.7525 | 0.0001 | 0.2 | 0.8316 | -0.09 | -1.2 | 0.2238 | -0.0001 | -0.3 | 0.7505 |
XMR | -0.02 | -1.9 | 0.0620 | -0.0004 | -1.0 | 0.3389 | 0.01 | 0.5 | 0.6208 | 0.0000 | 0.1 | 0.9576 | 0.01 | 0.6 | 0.5189 | -0.0001 | -0.5 | 0.6070 |
VET | -0.01 | -0.5 | 0.6057 | -0.0002 | -0.3 | 0.7577 | 0.03 | 2.0 | 0.0410 | 0.0011 | 2.3 | 0.0234 | 0.05 | 1.6 | 0.1057 | 0.0006 | 1.6 | 0.1166 |
ALGO | -0.06 | -5.1 | 0.0000 | -0.0031 | -4.3 | 0.0000 | 0.03 | 1.8 | 0.0649 | 0.0005 | 1.0 | 0.3256 | 0.04 | 1.3 | 0.1819 | 0.0002 | 0.4 | 0.6581 |
FLOW | -0.12 | -6.7 | 0.0000 | -0.0080 | -7.6 | 0.0000 | -0.07 | -2.1 | 0.0374 | -0.0003 | -0.4 | 0.6976 | -0.11 | -2.6 | 0.0108 | -0.0000 | -0.1 | 0.9419 |
FTT | -0.04 | -5.2 | 0.0000 | -0.0029 | -5.8 | 0.0000 | 0.02 | 1.1 | 0.2819 | 0.0004 | 1.1 | 0.2523 | 0.03 | 0.7 | 0.4576 | 0.0002 | 0.5 | 0.5898 |
CRO | 0.01 | 1.0 | 0.3383 | -0.0001 | -0.1 | 0.9111 | 0.01 | 0.6 | 0.5361 | 0.0006 | 1.2 | 0.2164 | 0.01 | 0.2 | 0.8760 | 0.0002 | 0.6 | 0.5499 |
OKB | -0.00 | -0.2 | 0.8502 | -0.0002 | -0.3 | 0.7897 | 0.01 | 0.9 | 0.3934 | 0.0005 | 1.2 | 0.2369 | 0.04 | 2.2 | 0.0307 | 0.0004 | 1.6 | 0.1173 |
STETH | -0.12 | -8.9 | 0.0000 | -0.0079 | -9.6 | 0.0000 | 0.00 | 0.1 | 0.9401 | -0.0008 | -1.5 | 0.1343 | -0.01 | -0.3 | 0.7604 | -0.0006 | -1.4 | 0.1570 |
LEO | 0.00 | 0.3 | 0.7711 | -0.0002 | -0.7 | 0.5069 | 0.01 | 1.0 | 0.3334 | 0.0001 | 0.3 | 0.7411 | 0.00 | 0.6 | 0.5761 | -0.0000 | -0.4 | 0.7192 |
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14,5))
ax1.tick_params(labelrotation=90)
ax2.tick_params(labelrotation=90)
fig.suptitle('Coefficients of our sinusoidal model on 14-day trades')
df = DF_RESULT[('14 Day', 'Cumulative', 'coef')]
g1 = sns.barplot(x=df.index, y=df.values, color='maroon', ax=ax1);
g1.set_xticklabels([tkr.split('|')[0] for tkr in DF_RESULT.index]);
g1.set(title='Cumulative');
df = DF_RESULT[('14 Day', 'Mean Daily Log', 'coef')]
g2 = sns.barplot(x=df.index, y=df.values, color='maroon', ax=ax2);
g2.set_xticklabels([tkr.split('|')[0] for tkr in DF_RESULT.index]);
g2.set(title='Mean Daily Log');
#ax2.plot(x, -y)
g = sns.FacetGrid(
DF_MAIN, col="ticker", col_wrap=5, height=3, aspect=1,
xlim=(0, 28), ylim=(-0.02, 0.02), sharey=False
)
g.map(sns.lineplot, "days_after_new_moon", "14_day_mean_daily_log_returns", color='maroon');
g.map(lambda y, **kw: plt.axhline(y.mean(), color="k", linewidth=1), '14_day_mean_daily_log_returns');
g.set_axis_labels("days_after_new_moon");
g = sns.FacetGrid(
DF_MAIN, col="ticker", col_wrap=5, height=3, aspect=1,
xlim=(0, 28), ylim=(-0.02, 0.02), sharey=True
)
g.map(sns.lineplot, "days_after_new_moon", "30_day_mean_daily_log_returns", color='maroon');
g = sns.FacetGrid(
DF_MAIN, col="ticker", col_wrap=5, height=3, aspect=1,
xlim=(0, 28), ylim=(-0.02, 0.02), sharey=False
)
g.map(sns.lineplot, "days_after_new_moon", "60_day_mean_daily_log_returns", color='maroon');