三月的日幣季節性
近期在前大型銀行交易員 Brent Donnelly 的文章讀到日幣在三月有季節性的跌幅。 一些網路文章亦提到各種貨幣對的季節性,例如 這篇文章 與 這篇文章。
Brent 的論點如下:
每年三月中旬時 USDJPY 會先下跌,之後上漲至 4 月初,接著又下跌至四月中。作者並且回測這個理論 (從 2010 至 2021),發現的確有這個現象。
其給出的理由是: 日本的會計年度在 03/31 結束,因此 3 月中開始,大量在外國設址的日本企業會將外幣轉為日幣 (以讓財務表現更好看),推升日幣買壓,使 USDJPY 下跌,Brent 大大用的詞是 “repatriation”,中文是遣返,也有 “將外幣兌換成本國貨幣”的意思。 4 月一到來 (新的會計年度開始),這些企業又將日幣轉為外幣以方便做生意,使得 USDJPY 上漲,至於 4 月初到 4 月中的持續上漲,Brent 表示這是企業發給員工的獎金,至於這個現象是否可靠? 作者 Brent 有在日本野村銀行工作過,他說保證有這樣的現象。
實測結論
我實際用 EURJPY 與 USDJPY 測,發現使用這個策略,也就是在 4 月初做多 EURJPY (持有期間三個交易日),從 1999 年到 2021 年,這策略的勝率約 52% (樣本數為 46)。若改用 USDJPY,則從 2017 年至 2021 年,勝率約 55% (樣本數為 11)。
至於為何 EURJPY 我用 22 年的數據,USDJPY 卻只用近 6 年? 因為 Fred 提供的 USDJPY 資料從 2017 年開始,Dbnomics 又沒有提供 USDJPY 的資料,所以只能將就點囉。
EURJPY 回測 (1999-2021)
引入相關套件 (以 dbnomics
套件取得 EURJPY 資料)
1
2
3
4
5
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from dbnomics import fetch_series, fetch_series_by_api_link
自套件取得資料並計算報酬
1
2
3
4
5
6
7
8
9
10
eurjpy = fetch_series('BDF', 'EXR', 'EXR.D.JPY.EUR.SP00.A')
df = pd.DataFrame()
cols = ['period', 'value']
for i in cols:
df[i] = eurjpy[i]
df.columns = ['Time', 'EURJPY']
df.dropna(inplace=True)
df.index = range(0, df.shape[0])
1
2
3
4
5
6
7
8
9
10
period_ret_dict = dict()
for i in range(0, df.shape[0], 3):
period_start = (df.loc[i, 'Time']).date()
end_pos = i + 3
tmp_df = pd.DataFrame(df.loc[i:end_pos])
tmp_df['Ret'] = tmp_df['EURJPY'].pct_change()
tmp_df.dropna(inplace=True)
cum_ret_periods = (tmp_df['Ret']+1).cumprod() - 1
# this is a pandas Series
period_ret_dict[period_start] = cum_ret_periods.tail(1)
畫出持有三個交易日的報酬情況
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
tmp_l = list()
for i in period_ret_dict.values():
tmp_l.append(float(i.values))
s = pd.Series(tmp_l, index=period_ret_dict.keys())
count = 0
pos_ret = 0
count_s = pd.Series()
for i in range(len(s)):
if s.index[i].month == 4 and s.index[i].day < 10:
count += 1
if s[i] > 0:
pos_ret += 1
count_s[s.index[i]] = s[i]
count = 0
pos_ret = 0
count_s = pd.Series()
for i in range(len(s)):
if s.index[i].month == 4 and s.index[i].day < 10:
count += 1
if s[i] > 0:
pos_ret += 1
count_s[s.index[i]] = s[i]
#print(s.index[i], s[i])
win_rate = float("{:.3f}".format(pos_ret/count))
print(f'報酬 > 0 % 次數: {pos_ret} out of {count} 次')
print(f'四月初多 EURJPY 的勝率為: {win_rate*100} %')
1
2
3
4
5
6
7
8
import matplotlib.dates as mdates
plt.style.use('ggplot')
plt.xlabel('Time')
plt.ylabel('Return')
plt.title('EURJPY 3 trading day return (Early April)')
count_s.plot(kind='bar')
plt.gca().xaxis.set_major_locator(mdates.DayLocator(interval=5))
plt.gcf().autofmt_xdate()
在 4 月初做多 EURJPY 的報酬 (1999-2021)
USDJPY 回測 (2017-2021)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from pandas_datareader import data
df = data.DataReader('DEXJPUS', 'fred')
df['Time'] = df.index
df.index = range(0, df.shape[0])
df.rename(columns={'DEXJPUS':'USDJPY'}, inplace=True)
period_ret_dict = dict()
for i in range(0, df.shape[0], 3):
period_start = (df.loc[i, 'Time']).date()
end_pos = i + 3
tmp_df = pd.DataFrame(df.loc[i:end_pos])
tmp_df['Ret'] = tmp_df['USDJPY'].pct_change()
tmp_df.dropna(inplace=True)
cum_ret_periods = (tmp_df['Ret']+1).cumprod() - 1
period_ret_dict[period_start] = cum_ret_periods.tail(1)
tmp_l = list()
for i in period_ret_dict.values():
tmp_l.append(float(i.values))
s = pd.Series(tmp_l, index=period_ret_dict.keys())
count = 0
pos_ret = 0
count_s = pd.Series()
for i in range(len(s)):
if s.index[i].month == 4 and s.index[i].day < 10:
count += 1
if s[i] > 0:
pos_ret += 1
if s[i] < 0:
print(s.index[i])
print(s[i])
count_s[s.index[i]] = s[i]
win_rate = float("{:.3f}".format(pos_ret/count))
print(f'報酬 > 0 % 次數: {pos_ret} out of {count} 次')
print(f'四月初多 USDJPY 的勝率為: {win_rate*100} %')
import matplotlib.dates as mdates
count_s.plot(kind='bar')
plt.xlabel('Time')
plt.ylabel('Return')
plt.title('USDJPY 3 trading day return (Early April)')
plt.gcf().autofmt_xdate()