-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.m
137 lines (118 loc) · 4.39 KB
/
main.m
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
%% Section 1: Importing Dependencies and Data
% Add the genetic programming module to MATLAB path
addpath(genpath('./gp'));
% Add the utils path to MATLAB path
addpath('./utils');
% Add the data path
if ~isfolder('./data')
mkdir('data');
end
addpath('./data');
%% Section 2: Data Preprocessing
% Define the product
% For the correct product name, visit: https://finance.yahoo.com/
product = '^DJI';
filename = "./data/" + product + "_D1.csv";
% Download data from the Yahoo Finance (if not exists)
if ~isfile(filename)
downloadData(product);
% Calculate the indicators (speedup training)
% NOTE: You must have the financial toolbox installed.
calculateIndicators(product);
end
% Use a random seed
rng shuffle
% Read the data
data = readtable(filename);
% Define the transaction cost in the form of Ask and Bid
commission = 0.0025; % A transaction cost of 0.25%
data.Ask = data{:, 'Open'} / (1 - commission);
data.Bid = data{:, 'Open'};
% Split the data into training and test set
% Testing period: 2020 - 2021 (2 years)
[splitMonth, splitDay, splitYear] = deal(1, 1, 2020);
[testSet, trainingSet, trainBeginIdx] = splitDataByDate(data, ...
splitYear, splitMonth, splitDay);
% Training period: 2010 - 2019 (10 years)
[splitMonth, splitDay, splitYear] = deal(1, 1, 2010);
try
[trainingSet, ~, ~] = splitDataByDate(trainingSet, ...
splitYear, splitMonth, splitDay);
catch
end
%% Section 3: Define the template for GP
template = Template();
template.set("Root", { ...
IfThenElse(), ...
});
% We can set the lower and upper bound to the same value. This ensures the
% right-hand side of such Variable will not contain a value.
template.set("Operator.BinaryOperator.BinaryRelationalOperator.lhs", { ...
Variable("RSI", 'bounded', 'double', 0, 100), ...
Variable("EMA5", 'none', 'double'), ...
Variable("EMA20", 'none', 'double'), ...
Variable("MACD", 'bounded', 'double', 0, 0), ...
Variable("MACDSignal", 'none', 'double'), ...
Variable("Bid", 'none', 'double'), ...
Variable("Return5", 'bounded', 'double', ...
min(trainingSet{:, 'Return5'}), max(trainingSet{:, 'Return5'})), ...
Variable("Return20", 'bounded', 'double', ...
min(trainingSet{:, 'Return20'}), max(trainingSet{:, 'Return20'})), ...
});
% We can place more prototypes of a certain type to increase the
% probability of it being chosen.
% Values only appear on the right-hand side.
template.set("Operator.BinaryOperator.BinaryRelationalOperator.rhs", { ...
Value(), Value(), Value(), Value(), Value(), Value(), ...
Variable("EMA5", 'none', 'double'), ...
Variable("EMA20", 'none', 'double'), ...
Variable("MACD", 'bounded', 'double', 0, 0), ...
Variable("MACDSignal", 'none', 'double'), ...
});
template.set("Operator.BinaryOperator.BinaryLogicalOperator.lhs", { ...
GreaterEqual(), LessEqual(), And(), Or()...
});
template.set("Operator.BinaryOperator.BinaryLogicalOperator.rhs", { ...
GreaterEqual(), LessEqual(), And(), Or() ...
});
template.set("Statement.IfThenElse.ifNode", { ...
GreaterEqual(), LessEqual(), And(), Or() ...
});
% 1 for sell, 2 for hold, and 3 for buy
template.set("Statement.IfThenElse.thenNode", { ...
IfThenElse(), IfThenElse(), EnumeratedSignal({1, 2, 3})});
template.set("Statement.IfThenElse.elseNode", { ...
IfThenElse(), IfThenElse(), EnumeratedSignal({1, 2, 3})});
%% Section 4: Define the parameters for GP
% Note: The parameters are not optimized.
opt.populationSize = 25;
opt.maxHeight = 6;
opt.generations = 20;
opt.selectionSchema = 'tournament';
opt.tournamentSize = 3;
opt.eliteSize = 8;
opt.crossoverFraction = 0.8;
opt.mutateAfterCrossover = true;
opt.reevaluateElites = false;
opt.mutationProb = 0.05;
opt.terminalMutationProb = 0.15;
%% Section 5: Setup and optimize the GP Model
% Instantiate the GP model
myGPModel = MyGPModel();
% Register the options
myGPModel.register(opt);
% Populate the GPModel with individuals
% GPModel's `populate` will call the constructor specified
myGPModel.populate();
% Initialize every individual in the population
myGPModel.init(template, opt.maxHeight);
% Optimize the model using genetic algorithm
tic;
myGPModel.run(trainingSet);
toc;
%% Section 6: Evaluation
% Show the best performing strategy in pseudocode
myGPModel.pseudocode(1);
% Test the strategy on the test set
r = backtest(myGPModel.best, testSet);
fprintf("Return on test set: %s\n", string(r));