-
Notifications
You must be signed in to change notification settings - Fork 5
/
ma_classification_test.py
182 lines (138 loc) · 7.1 KB
/
ma_classification_test.py
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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
"""Demonstration of MA/price cross strategy combined with AI estimation of fake signals.
The author is Zmicier Gotowka
Distributed under Fcore License 1.1 (see license.md)
"""
# TODO LOW Remove gaps between docstrings and imports
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from tools.ma_classifier import MAClassifier
from backtest.ma_classification import MAClassification
from backtest.ma import MA
from backtest.base import BackTestError
from backtest.stock import StockData
from backtest.reporting import Report
from tools.base import ToolError
from data.fdata import FdataError
from data.yf import YF
import plotly.graph_objects as go
import sys
# Variables for testing
symbol = 'SPY'
period = 50 # Period for MA calculation
change_period = 2 # Number of cycles to consider the trend as changed if there was no signal
change_percent = 2 # Change of price in percent to consider the trend as changed if there was no signal
true_ratio = 0.004 # Ratio of ma/quote change to consider it as a true signal. It should be achieved withing cycles_num to be considered as true.
cycle_num = 2 # Number of cycles to wait for the true_ratio value. If true_ratio is not reached withing these cycles, the signal is considered as false.
min_width = 2500 # Minimum width for charting
height = 250 # Height of each subchart in reporting
if __name__ == "__main__":
# Get a separate data for learning and testing.
# Get quotes for learning
try:
warning = "WARNING! Using yfinance data for the demonstration.\n" +\
"Always keep yfinance up to date ( pip install yfinance --upgrade ) and use quotes obtained from this " +\
"datasource only for demonstation purposes!\n"
print(warning)
rows_learn = YF(symbol=symbol, first_date="2000-1-1", last_date="2021-1-1").get()
except FdataError as e:
sys.exit(e)
length_learn = len(rows_learn)
print(f"The total number of quotes used for {symbol} is {length_learn}.\n")
# Get quotes for testing
try:
rows = YF(symbol=symbol, first_date="2021-1-2", last_date="2023-4-1").get()
except FdataError as e:
sys.exit(e)
length_test = len(rows)
print(f"The total number of quotes used for {symbol} is {length_test}.\n")
# Train the models
classifier = MAClassifier(period,
data_to_learn=[rows_learn],
true_ratio=true_ratio,
cycle_num=cycle_num,
model_buy=LinearDiscriminantAnalysis(),
model_sell=LinearDiscriminantAnalysis())
try:
classifier.learn()
accuracy_buy_learn, accuracy_sell_learn, total_accuracy_learn = classifier.get_learn_accuracy()
f1_buy_learn, f1_sell_learn, total_f1_learn = classifier.get_learn_f1()
except ToolError as e:
sys.exit(f"Can't train MA classification models: {e}")
print('\nBuy train accuracy:{: .2f}%'.format(accuracy_buy_learn * 100))
print('Sell train accuracy:{: .2f}%'.format(accuracy_sell_learn * 100))
print('Total train accuracy:{: .2f}%'.format(total_accuracy_learn * 100))
print(f"\nBuy train f1 score: {round(f1_buy_learn, 4)}")
print(f"Sell train f1 score: {round(f1_sell_learn, 4)}")
print(f"Total train f1 score: {round(total_f1_learn, 4)}")
# Perform a backtest
quotes = StockData(rows=rows,
title=symbol,
spread=0.1,
trend_change_period=change_period,
trend_change_percent=change_percent
)
try:
classification = MAClassification(data=[quotes],
commission=2.5,
initial_deposit=10000,
periodic_deposit=500,
deposit_interval=30,
inflation=2.5,
period=period,
margin_rec=0.9,
margin_req=1,
classifier=classifier
)
classification.calculate()
results_cls = classification.get_results()
accuracy_buy_est, accuracy_sell_est, total_accuracy_est = classifier.get_est_accuracy()
f1_buy_est, f1_sell_est, total_f1_est = classifier.get_est_f1()
except BackTestError as e:
sys.exit(f"Can't perform backtesting: {e}")
print('\nBuy estimation accuracy:{: .2f}%'.format(accuracy_buy_est * 100))
print('Sell estimation accuracy:{: .2f}%'.format(accuracy_sell_est * 100))
print('Total estimation accuracy:{: .2f}%'.format(total_accuracy_est * 100))
print(f"\nBuy estimation f1 score: {round(f1_buy_est, 4)}")
print(f"Sell estimation f1 score: {round(f1_sell_est, 4)}")
print(f"Total estimation f1 score: {round(total_f1_est, 4)}")
print(f"\nThe actual/estimated signals:\n{classifier.get_df_signals_to_compare().to_string()}\n")
# Compare with regular MA-cross strategy
# Create the 'regular' MA-Cross result for comparison
ma = MA(data=[quotes],
commission=2.5,
initial_deposit=10000,
periodic_deposit=500,
deposit_interval=30,
inflation=2.5,
period=period,
margin_rec=0.9,
margin_req=1
)
try:
ma.calculate()
results_cmp = ma.get_results()
except BackTestError as e:
sys.exit(f"Can't perform backtesting calculation: {e}")
#################
# Create a report
#################
report = Report(data=results_cls, width=max(length_test, min_width), margin=True)
# Add a chart with quotes
fig_quotes = report.add_quotes_chart(title="MA/Quote Cross + AI Backtesting Example")
# Append MA values to the quotes chart
fig_quotes.add_trace(go.Scatter(x=results_cls.DateTime, y=classification.exec().get_vals()['ma'], mode='lines', name="MA", line=dict(color="green")))
# Add strategy comparison to the second chart
fig_cmp = report.add_quotes_chart(title="Regular MA/Quote cross Example for Comparison", data=results_cmp, height=height)
# Append MA values to the comparison chart
fig_cmp.add_trace(go.Scatter(x=results_cmp.DateTime, y=ma.exec().get_vals()['ma'], mode='lines', name="MA", line=dict(color="green")))
# Add a chart to represent portfolio performance
fig_portf = report.add_portfolio_chart(height=height)
# Add second strategy results for comparison
fig_portf.add_trace(go.Scatter(x=results_cmp.DateTime, y=results_cmp.TotalValue, mode='lines', name="MA Cross Results"))
# Add chart a with expenses
report.add_expenses_chart(height=height)
# Add annotations with strategy results
report.add_annotations(title="MA Classifier performance:")
report.add_annotations(data=results_cmp, title="Regular MA/Price Crossover performance:")
# Show image
new_file = report.show_image()
print(f"{new_file} is written.")