Revision | 779c588998898345a1895b59ad2e6817ff2d43ee (tree) |
---|---|
Zeit | 2023-02-15 06:37:05 |
Autor | kazuhiro_kondow <simauma.circus@gmai...> |
Commiter | kazuhiro_kondow |
loggerのLogファイルがローテーションしない事象を修正
macd判定が誤る事象について
毎分計算し直すと前回結果が異なると判明
内部に前回結果を退避して改善を試みる
転換点判定を何度も判定しないようにフラグ設定
@@ -20,4 +20,5 @@ __pycache__/ | ||
20 | 20 | *.json |
21 | 21 | *.log* |
22 | 22 | Docs/.~lock.memo.ods# |
23 | -*.spec | |
\ No newline at end of file | ||
23 | +*.spec | |
24 | +*.csv | |
\ No newline at end of file |
@@ -0,0 +1,75 @@ | ||
1 | +import numpy as np | |
2 | +from numpy import linalg as LA | |
3 | + | |
4 | + | |
5 | +# テクニカル分析 | |
6 | +TECHNICAL_UNCHANGED = 0 | |
7 | +TECHNICAL_GOLDEN_CROSS = 1 | |
8 | +TECHNICAL_DEAD_CROSS = 2 | |
9 | +TECHNICAL_UPTREND = 3 | |
10 | +TECHNICAL_DOWNTREND = 4 | |
11 | + | |
12 | + | |
13 | +def crossover(series1: np.ndarray, series2: np.ndarray) -> bool: | |
14 | + return series1[-2] < series2[-2] and series1[-1] > series2[-1] | |
15 | + | |
16 | + | |
17 | +def vector_angle(u: np.ndarray, v: np.ndarray): | |
18 | + i = np.inner(u, v) | |
19 | + n = LA.norm(u) * LA.norm(v) | |
20 | + c = i / n | |
21 | + return np.rad2deg(np.arccos(np.clip(c, -1.0, 1.0))) | |
22 | + | |
23 | + | |
24 | +# 確認したい動作 | |
25 | +# 2023-02-14 06:14:02,857 13268 MainThread exchange_rate_info.py:197 set_technical_indicators [DEBUG]: num[-2]: MACD: -0.018727, MACDSignal: -0.017417 < | |
26 | +# 2023-02-14 06:14:02,858 13268 MainThread exchange_rate_info.py:197 set_technical_indicators [DEBUG]: num[-1]: MACD: -0.018409, MACDSignal: -0.017507 < | |
27 | +# 2023-02-14 06:14:02,860 13268 MainThread exchange_rate_info.py:202 set_technical_indicators [DEBUG]: vector angle:0.6382709010386011 | |
28 | +# 2023-02-14 06:15:02,500 13268 MainThread exchange_rate_info.py:197 set_technical_indicators [DEBUG]: num[-2]: MACD: -0.01741, MACDSignal: -0.017416 > | |
29 | +# 2023-02-14 06:15:02,502 13268 MainThread exchange_rate_info.py:197 set_technical_indicators [DEBUG]: num[-1]: MACD: -0.016186, MACDSignal: -0.017305 > | |
30 | +# 2023-02-14 06:15:02,504 13268 MainThread exchange_rate_info.py:202 set_technical_indicators [DEBUG]: vector angle:1.9033600682385448 | |
31 | +# 2023-02-14 06:15:02,505 13268 MainThread exchange_rate_info.py:239 set_technical_indicators [INFO]: Technical Indicator: Downtrand. | |
32 | +# 結果としてはmacd_value_verification.pyで確認した通り、判定時に使用したmacdの値の扱いを考え直す必要がわかった | |
33 | + | |
34 | +macd = [-0.016186, -0.01741] | |
35 | +macdsignal = [-0.017305, -0.017416] | |
36 | + | |
37 | +v_angle = vector_angle(macd[-2:], macdsignal[-2:]) | |
38 | +angle = 1.54 | |
39 | +turning_point: bool | None = None | |
40 | + | |
41 | +if crossover(macd, macdsignal) and \ | |
42 | + v_angle > angle and \ | |
43 | + macd[-1] < 0.0: | |
44 | + print('Cross up.') | |
45 | + if turning_point is not True: | |
46 | + # GoldenCross 上昇転換サイン | |
47 | + technical_indicator = TECHNICAL_GOLDEN_CROSS | |
48 | + turning_point = True | |
49 | + msg = 'Technical Indicator: GoldenCros' | |
50 | + else: | |
51 | + print('Already judged goldencros') | |
52 | +elif crossover(macdsignal, macd) and \ | |
53 | + v_angle > angle and \ | |
54 | + macd[-1] > 0.0: | |
55 | + print('Cross down.') | |
56 | + if turning_point is not False: | |
57 | + # DeadCross 降下転換サイン | |
58 | + technical_indicator = TECHNICAL_DEAD_CROSS | |
59 | + turning_point = False | |
60 | + msg = 'Technical Indicator: DeadCros' | |
61 | + else: | |
62 | + print('Already judged deadcros') | |
63 | +elif macd[-1] > 0.0: | |
64 | + # 上昇トレンド | |
65 | + technical_indicator = TECHNICAL_UPTREND | |
66 | + msg = 'Technical Indicator: Uptrend.' | |
67 | +elif macd[-1] < 0.0: | |
68 | + # 降下トレンド | |
69 | + technical_indicator = TECHNICAL_DOWNTREND | |
70 | + msg = 'Technical Indicator: Downtrand.' | |
71 | +else: | |
72 | + # いずれにも該当しない | |
73 | + technical_indicator = TECHNICAL_UNCHANGED | |
74 | + msg = 'Technical Indicator: Unchanged.' | |
75 | +print(msg) |
@@ -0,0 +1,97 @@ | ||
1 | +# 毎回算出するMACDの値は直近の値の影響を強く受ける設計なので | |
2 | +# 再計算のたびに値が変わる | |
3 | + | |
4 | +# 2023 - 02 - 14 06: 10: 02, 406 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.020016, MACDSignal: -0.016616 | |
5 | +# 2023 - 02 - 14 06: 10: 02, 407 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.020261, MACDSignal: -0.016947 | |
6 | +# 2023 - 02 - 14 06: 11: 02, 049 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.01968, MACDSignal: -0.016894 | |
7 | +# 2023 - 02 - 14 06: 11: 02, 050 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.019248, MACDSignal: -0.017108 | |
8 | +# 2023 - 02 - 14 06: 12: 02, 583 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.019278, MACDSignal: -0.017111 | |
9 | +# 2023 - 02 - 14 06: 12: 02, 583 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.01886, MACDSignal: -0.01727 | |
10 | +# 2023 - 02 - 14 06: 13: 02, 208 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.019039, MACDSignal: -0.017286 | |
11 | +# 2023 - 02 - 14 06: 13: 02, 210 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.018787, MACDSignal: -0.017423 | |
12 | + | |
13 | +# 2023 - 02 - 14 06: 14: 02, 857 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.018727, MACDSignal: -0.017417 > | |
14 | +# 2023 - 02 - 14 06: 14: 02, 858 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.018409, MACDSignal: -0.017507 > | |
15 | +# mt5上ではグラフが交差した部分は直近2データが変わっている、本来なら前回最新が次回2個目のデータと近似するはず・・・ | |
16 | +# 2023 - 02 - 14 06: 15: 02, 500 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.01741, MACDSignal: -0.017416 < | |
17 | +# 2023 - 02 - 14 06: 15: 02, 502 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.016186, MACDSignal: -0.017305 < | |
18 | + | |
19 | +# 2023 - 02 - 14 06: 16: 02, 159 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.016171, MACDSignal: -0.017303 | |
20 | +# 2023 - 02 - 14 06: 16: 02, 161 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.015187, MACDSignal: -0.017111 | |
21 | +# 2023 - 02 - 14 06: 17: 02, 856 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.015545, MACDSignal: -0.017143 | |
22 | +# 2023 - 02 - 14 06: 17: 02, 858 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.014928, MACDSignal: -0.016942 | |
23 | +# 2023 - 02 - 14 06: 18: 02, 545 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.014794, MACDSignal: -0.01693 | |
24 | +# 2023 - 02 - 14 06: 18: 02, 546 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.01408, MACDSignal: -0.016671 | |
25 | +# 2023 - 02 - 14 06: 19: 02, 168 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.013811, MACDSignal: -0.016646 | |
26 | +# 2023 - 02 - 14 06: 19: 02, 169 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.012886, MACDSignal: -0.016304 | |
27 | +# 2023 - 02 - 14 06: 20: 02, 712 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-2]: MACD: -0.013065, MACDSignal: -0.016321 | |
28 | +# 2023 - 02 - 14 06: 20: 02, 713 13268 MainThread exchange_rate_info.py: 197 set_technical_indicators[DEBUG]: num[-1]: MACD: -0.012373, MACDSignal: -0.015962 | |
29 | +import MetaTrader5 as mt5 | |
30 | +import talib as ta | |
31 | +import datetime as dt | |
32 | +import pytz | |
33 | +import pandas as pd | |
34 | + | |
35 | + | |
36 | +# 短期EMAの期間 | |
37 | +n1 = 39 | |
38 | +# 長期EMAの期間 | |
39 | +n2 = 56 | |
40 | +# シグナル(MACDのSMA)の期間 | |
41 | +ns = 21 | |
42 | + | |
43 | +# 日本のtimezone | |
44 | +tz_local = pytz.timezone('Etc/GMT-9') | |
45 | +# OANDAサーバのtimezone | |
46 | +tz_oanda = pytz.timezone('Etc/GMT-2') | |
47 | + | |
48 | +cp = 'USDJPY' | |
49 | +mt5.initialize() | |
50 | + | |
51 | +# 終値履歴を取得 | |
52 | +rates = mt5.copy_rates_from_pos( | |
53 | + cp, | |
54 | + mt5.TIMEFRAME_M1, | |
55 | + 0, | |
56 | + 100000 | |
57 | +) | |
58 | + | |
59 | +df = pd.DataFrame(rates) | |
60 | + | |
61 | +# timeカラムをdatetime型に変換 | |
62 | +df.time = pd.to_datetime(df['time'], unit='s') | |
63 | +# timeカラムをindexに時系列インデックスとして割り当て | |
64 | +df.index = pd.DatetimeIndex(df.time, name='Date') | |
65 | +# インデックスのTimeZoneをUTCに設定 | |
66 | +df.index = df.index.tz_localize(dt.timezone.utc) | |
67 | +# インデックスのTimeZoneをLovalに変換 | |
68 | +df.index = df.index.tz_convert(tz_local) | |
69 | + | |
70 | +# >>> df[-2:] | |
71 | +# time open high low close tick_volume spread real_volume | |
72 | +# Date | |
73 | +# 2023-02-15 00:41:00+09:00 2023-02-14 15:41:00 132.383 132.404 132.242 132.249 709 3 0 | |
74 | +# 2023-02-15 00:42:00+09:00 2023-02-14 15:42:00 132.253 132.344 132.215 132.296 672 3 0 | |
75 | +# | |
76 | +# mt5のmacd表示 | |
77 | +# 15:41:00 15:42:00 | |
78 | +# macd : -0.0049 -0.0019 | |
79 | +# signal : -0.0107 -0.0102 | |
80 | +# | |
81 | +# この値に近似するデータ数を探索する | |
82 | + | |
83 | +closes = df['close'] | |
84 | + | |
85 | +# MACDを算出 | |
86 | +macd, macdsignal, _ = ta.MACD( | |
87 | + closes, | |
88 | + fastperiod=n1, | |
89 | + slowperiod=n2, | |
90 | + signalperiod=ns | |
91 | +) | |
92 | +print(macd[-2:]) | |
93 | +print(macdsignal[-2:]) | |
94 | + | |
95 | +# 100 macd:0.012177 signal:-0.011877 | |
96 | +# 150 macd:0.012937 signal:-0.010785 | |
97 | +# 200 macd:0.013341 signal:-0.010236 | |
\ No newline at end of file |
@@ -18,7 +18,7 @@ args=(sys.stdout,) | ||
18 | 18 | class=logging.handlers.TimedRotatingFileHandler |
19 | 19 | level=DEBUG |
20 | 20 | formatter=simple |
21 | -args=('./mt5-operator.log', 'D', 10, 10, 'utf-8') | |
21 | +args=('./mt5-operator.log', 'MIDNIGHT', 10, 10, 'utf-8') | |
22 | 22 | |
23 | 23 | [formatters] |
24 | 24 | keys=simple |
@@ -11,7 +11,7 @@ TradeContractSize = 100000.0 | ||
11 | 11 | # 約定の最小ボリューム |
12 | 12 | VolumeMin = 0.1 |
13 | 13 | # 約定の最大ボリューム |
14 | -VolumeMax = 10.0 | |
14 | +VolumeMax = 0.1 | |
15 | 15 | # 約定実行のための最小限のボリューム変化のステップ |
16 | 16 | VolumeStep = 0.01 |
17 | 17 |
@@ -38,15 +38,15 @@ OpenMergin = 0.007 | ||
38 | 38 | # 利益確定(円) |
39 | 39 | ProfitTaking = 1.5 |
40 | 40 | # 損切(円) |
41 | -LossCut = 0.2 | |
41 | +LossCut = 0.25 | |
42 | 42 | |
43 | 43 | # テクニカル分析 |
44 | 44 | [Technical] |
45 | 45 | # 短期EMAの期間 |
46 | -FastEMAperiod = 39 | |
46 | +FastEMAperiod = 35 | |
47 | 47 | # 長期EMAの期間 |
48 | -SlowEMAperiod = 56 | |
48 | +SlowEMAperiod = 53 | |
49 | 49 | # シグナルの期間 |
50 | -Signalperiod = 21 | |
50 | +Signalperiod = 18 | |
51 | 51 | # 交差角度閾値 |
52 | -CrossingAngleThreshold = 0.684 | |
52 | +CrossingAngleThreshold = 1.54 |
@@ -46,6 +46,8 @@ class Setting(object): | ||
46 | 46 | self.__orderable_lot: float = 0.0 |
47 | 47 | # テクニカル指標 |
48 | 48 | self.__technical_indicator: int = 0 |
49 | + # テクニカル転換点 | |
50 | + self.__turning_point: bool | None = None | |
49 | 51 | |
50 | 52 | @property |
51 | 53 | def get_instance(self): |
@@ -136,3 +138,16 @@ class Setting(object): | ||
136 | 138 | @technical_indicator.setter |
137 | 139 | def technical_indicator(self, value: int): |
138 | 140 | self.__technical_indicator = value |
141 | + | |
142 | + @property | |
143 | + def turning_point(self) -> bool | None: | |
144 | + """テクニカル転換点 | |
145 | + True: GoldenCross | |
146 | + False: DeadCross | |
147 | + None: Unset | |
148 | + """ | |
149 | + return self.__turning_point | |
150 | + | |
151 | + @turning_point.setter | |
152 | + def turning_point(self, value: bool): | |
153 | + self.__turning_point = value |
@@ -41,6 +41,8 @@ class ExchangeRateInfo(): | ||
41 | 41 | except Exception as e: |
42 | 42 | logger.error(e) |
43 | 43 | raise |
44 | + self.__previous_macd: float | None = None | |
45 | + self.__previous_signal: float | None = None | |
44 | 46 | |
45 | 47 | def __get_latest_rate(self) -> tuple: |
46 | 48 | """最新レート取得 |
@@ -79,7 +81,10 @@ class ExchangeRateInfo(): | ||
79 | 81 | return series1[-2] < series2[-2] and series1[-1] > series2[-1] |
80 | 82 | |
81 | 83 | def __vector_angle(self, u: np.ndarray, v: np.ndarray): |
82 | - if np.isnan(u) or np.isnan(v): | |
84 | + if np.isnan(u[-1]) or \ | |
85 | + np.isnan(u[-2]) or \ | |
86 | + np.isnan(v[-1]) or \ | |
87 | + np.isnan(v[-2]): | |
83 | 88 | raise ValueError('Nan is included in the value.') |
84 | 89 | i = np.inner(u, v) |
85 | 90 | n = LA.norm(u) * LA.norm(v) |
@@ -167,7 +172,8 @@ class ExchangeRateInfo(): | ||
167 | 172 | |
168 | 173 | # MACDテクニカル分析に必要なデータ件数 |
169 | 174 | # mt5アプリの表示と数値の乖離があるので件数を増やしてみる |
170 | - count = (self.__n2 + self.__ns) * 20 | |
175 | + # count = (self.__n2 + self.__n1) * 20 | |
176 | + count = self.__ns * 10 | |
171 | 177 | |
172 | 178 | # 終値履歴を取得 |
173 | 179 | closes = self.__get_history_closes( |
@@ -186,6 +192,18 @@ class ExchangeRateInfo(): | ||
186 | 192 | macd = np.around(macd, 6) |
187 | 193 | macdsignal = np.around(macdsignal, 6) |
188 | 194 | |
195 | + # 前回結果が未設定なら判定しない | |
196 | + if self.__previous_macd is None: | |
197 | + self.__previous_macd = macd[-1] | |
198 | + self.__previous_signal = macdsignal[-1] | |
199 | + s.technical_indicator = DEF.TECHNICAL_UNCHANGED | |
200 | + msg = 'Previous MACD result not set.' | |
201 | + logger.info(msg) | |
202 | + return | |
203 | + else: | |
204 | + macd[-2] = self.__previous_macd | |
205 | + macdsignal[-2] = self.__previous_signal | |
206 | + | |
189 | 207 | # for debug |
190 | 208 | for i in range(-2, 0, 1): |
191 | 209 | msg = ( |
@@ -195,24 +213,50 @@ class ExchangeRateInfo(): | ||
195 | 213 | |
196 | 214 | # 指標判定 |
197 | 215 | v_angle = self.__vector_angle(macd[-2:], macdsignal[-2:]) |
216 | + msg = f'vector angle:{v_angle}' | |
217 | + logger.debug(msg) | |
198 | 218 | msg = f'macd vector angle:{v_angle}' |
219 | + | |
199 | 220 | if self.__crossover(macd, macdsignal) and \ |
200 | - v_angle > self.__angle: | |
201 | - # GoldenCross 上昇転換サイン | |
202 | - s.technical_indicator = DEF.TECHNICAL_GOLDEN_CROSS | |
203 | - msg = 'Technical Indicator: GoldenCross.' | |
221 | + v_angle > self.__angle and \ | |
222 | + macd[-1] < 0.0: | |
223 | + logger.debug('Cross up.') | |
224 | + if s.turning_point is not True: | |
225 | + # GoldenCross 上昇転換サイン | |
226 | + s.technical_indicator = DEF.TECHNICAL_GOLDEN_CROSS | |
227 | + s.turning_point = True | |
228 | + msg = 'Technical Indicator: GoldenCross.' | |
229 | + else: | |
230 | + logger.debug('Already judged goldencross.') | |
204 | 231 | elif self.__crossover(macdsignal, macd) and \ |
205 | - self.__vector_angle(macd[-2:], macdsignal[-2:]) > \ | |
206 | - self.__angle: | |
207 | - # DeadCross 降下転換サイン | |
208 | - s.technical_indicator = DEF.TECHNICAL_DEAD_CROSS | |
209 | - msg = 'Technical Indicator: DeadCross.' | |
232 | + v_angle > self.__angle and \ | |
233 | + macd[-1] > 0.0: | |
234 | + logger.debug('Cross down.') | |
235 | + if s.turning_point is not False: | |
236 | + # DeadCross 降下転換サイン | |
237 | + s.technical_indicator = DEF.TECHNICAL_DEAD_CROSS | |
238 | + s.turning_point = False | |
239 | + msg = 'Technical Indicator: DeadCross.' | |
240 | + else: | |
241 | + logger.debug('Already judged deadcross.') | |
242 | + elif macd[-1] > 0.0: | |
243 | + # 上昇トレンド | |
244 | + s.technical_indicator = DEF.TECHNICAL_UPTREND | |
245 | + msg = 'Technical Indicator: Uptrend.' | |
246 | + elif macd[-1] < 0.0: | |
247 | + # 降下トレンド | |
248 | + s.technical_indicator = DEF.TECHNICAL_DOWNTREND | |
249 | + msg = 'Technical Indicator: Downtrand.' | |
210 | 250 | else: |
211 | 251 | # いずれにも該当しない |
212 | 252 | s.technical_indicator = DEF.TECHNICAL_UNCHANGED |
213 | 253 | msg = 'Technical Indicator: Unchanged.' |
214 | 254 | logger.info(msg) |
215 | 255 | |
256 | + # 今回MACD結果を退避 | |
257 | + self.__previous_macd = macd[-1] | |
258 | + self.__previous_signal = macdsignal[-1] | |
259 | + | |
216 | 260 | except Exception as e: |
217 | 261 | logger.error(e) |
218 | 262 | raise |