line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Amibroker::AFL::Optimizer;
|
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
23082
|
use 5.006;
|
|
1
|
|
|
|
|
3
|
|
4
|
1
|
|
|
1
|
|
4
|
use strict;
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
18
|
|
5
|
1
|
|
|
1
|
|
4
|
use warnings;
|
|
1
|
|
|
|
|
16
|
|
|
1
|
|
|
|
|
36
|
|
6
|
1
|
|
|
1
|
|
5
|
use File::Path qw(remove_tree);
|
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
82
|
|
7
|
1
|
|
|
1
|
|
746
|
use File::Copy::Recursive;
|
|
1
|
|
|
|
|
9142
|
|
|
1
|
|
|
|
|
44
|
|
8
|
1
|
|
|
1
|
|
832
|
use File::Slurp;
|
|
1
|
|
|
|
|
16095
|
|
|
1
|
|
|
|
|
84
|
|
9
|
1
|
|
|
1
|
|
359
|
use Win32::API;
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
use Carp;
|
11
|
|
|
|
|
|
|
use Amibroker::OLE::Interface;
|
12
|
|
|
|
|
|
|
use Amibroker::OLE::APXCreator;
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.01';
|
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
my $AMIBROKER_SERVICE_RUNS = 1000;
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub new {
|
19
|
|
|
|
|
|
|
my @list = @_;
|
20
|
|
|
|
|
|
|
my $class = shift @list;
|
21
|
|
|
|
|
|
|
push @list, {} unless @list and _is_arg( $list[-1] );
|
22
|
|
|
|
|
|
|
my $self = _check_valid_args(@list);
|
23
|
|
|
|
|
|
|
croak(
|
24
|
|
|
|
|
|
|
'[ERROR] : No \'dbpath\' (Database path) supplied to Amibroker (Required Parameter) '
|
25
|
|
|
|
|
|
|
. "\n" )
|
26
|
|
|
|
|
|
|
unless $self->{dbpath};
|
27
|
|
|
|
|
|
|
croak( '[ERROR] : Invalid \'dbpath\' (Database path) to Amibroker ' . "\n" )
|
28
|
|
|
|
|
|
|
unless -d $self->{dbpath};
|
29
|
|
|
|
|
|
|
croak(
|
30
|
|
|
|
|
|
|
'[ERROR] : No \'destination_path\' path supplied to Amibroker (Required Parameter) '
|
31
|
|
|
|
|
|
|
. "\n" )
|
32
|
|
|
|
|
|
|
unless $self->{destination_path};
|
33
|
|
|
|
|
|
|
croak(
|
34
|
|
|
|
|
|
|
'[ERROR] : No \'timeframe\' supplied to Amibroker (Required Parameter) '
|
35
|
|
|
|
|
|
|
. "\n" )
|
36
|
|
|
|
|
|
|
unless $self->{timeframe};
|
37
|
|
|
|
|
|
|
croak( '[ERROR] : No \'symbol\' supplied to Amibroker (Required Parameter) '
|
38
|
|
|
|
|
|
|
. "\n" )
|
39
|
|
|
|
|
|
|
unless $self->{symbol};
|
40
|
|
|
|
|
|
|
croak(
|
41
|
|
|
|
|
|
|
'[ERROR] : No \'afl_template\' supplied to Amibroker (Required Parameter) '
|
42
|
|
|
|
|
|
|
. "\n" )
|
43
|
|
|
|
|
|
|
unless $self->{afl_template};
|
44
|
|
|
|
|
|
|
croak(
|
45
|
|
|
|
|
|
|
'[ERROR] : No \'lot_size\' supplied to Amibroker, (Required Parameter)'
|
46
|
|
|
|
|
|
|
. "\n" )
|
47
|
|
|
|
|
|
|
unless $self->{lot_size};
|
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
print
|
50
|
|
|
|
|
|
|
'[WARN] : No \'log_path\' supplied to Amibroker : So, No logging '
|
51
|
|
|
|
|
|
|
. "\n"
|
52
|
|
|
|
|
|
|
unless $self->{log_path};
|
53
|
|
|
|
|
|
|
print
|
54
|
|
|
|
|
|
|
'[WARN] : No \'optimizer_name\' supplied to Amibroker : Default is taken '
|
55
|
|
|
|
|
|
|
. "\n"
|
56
|
|
|
|
|
|
|
unless $self->{optimizer_name};
|
57
|
|
|
|
|
|
|
print
|
58
|
|
|
|
|
|
|
'[WARN] : No \'min_win_percent\' supplied to Amibroker : Default 40% is taken '
|
59
|
|
|
|
|
|
|
. "\n"
|
60
|
|
|
|
|
|
|
unless $self->{min_win_percent};
|
61
|
|
|
|
|
|
|
print
|
62
|
|
|
|
|
|
|
'[WARN] : No \'min_profit_percent\' supplied to Amibroker : Default 50% is taken '
|
63
|
|
|
|
|
|
|
. "\n"
|
64
|
|
|
|
|
|
|
unless $self->{min_profit_percent};
|
65
|
|
|
|
|
|
|
print
|
66
|
|
|
|
|
|
|
'[WARN] : No \'min_no_of_trades\' supplied to Amibroker : Default 10 is taken '
|
67
|
|
|
|
|
|
|
. "\n"
|
68
|
|
|
|
|
|
|
unless $self->{min_no_of_trades};
|
69
|
|
|
|
|
|
|
print
|
70
|
|
|
|
|
|
|
'[WARN] : No \'selection_index\' supplied to Amibroker : Default 2 is taken '
|
71
|
|
|
|
|
|
|
. "\n"
|
72
|
|
|
|
|
|
|
unless $self->{selection_index};
|
73
|
|
|
|
|
|
|
print
|
74
|
|
|
|
|
|
|
'[WARN] : No \'profit_from\' supplied to Amibroker : Default 0 is taken '
|
75
|
|
|
|
|
|
|
. "\n"
|
76
|
|
|
|
|
|
|
unless $self->{_profit_from};
|
77
|
|
|
|
|
|
|
print
|
78
|
|
|
|
|
|
|
'[WARN] : No \'profit_to\' supplied to Amibroker : Default 20000 is taken '
|
79
|
|
|
|
|
|
|
. "\n"
|
80
|
|
|
|
|
|
|
unless $self->{profit_to};
|
81
|
|
|
|
|
|
|
print
|
82
|
|
|
|
|
|
|
'[WARN] : No \'profit_incr\' supplied to Amibroker : Default 5000 is taken '
|
83
|
|
|
|
|
|
|
. "\n"
|
84
|
|
|
|
|
|
|
unless $self->{profit_incr};
|
85
|
|
|
|
|
|
|
print
|
86
|
|
|
|
|
|
|
'[WARN] : No \'optimize_time\' supplied to Amibroker : By Default No time optimization is done '
|
87
|
|
|
|
|
|
|
. "\n"
|
88
|
|
|
|
|
|
|
unless $self->{optimize_time};
|
89
|
|
|
|
|
|
|
print
|
90
|
|
|
|
|
|
|
'[WARN] : No \'margin_amt\' supplied to Amibroker : By Default Ignoring margin amount '
|
91
|
|
|
|
|
|
|
. "\n"
|
92
|
|
|
|
|
|
|
unless $self->{margin_amt};
|
93
|
|
|
|
|
|
|
print
|
94
|
|
|
|
|
|
|
'[WARN] : No \'from or to\' dates are supplied to Amibroker : By Default Ignoring From & To Dates'
|
95
|
|
|
|
|
|
|
. "\n"
|
96
|
|
|
|
|
|
|
unless ( $self->{from} || $self->{to} );
|
97
|
|
|
|
|
|
|
bless $self, $class if defined $self;
|
98
|
|
|
|
|
|
|
$self->init();
|
99
|
|
|
|
|
|
|
return $self;
|
100
|
|
|
|
|
|
|
}
|
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub init {
|
103
|
|
|
|
|
|
|
my $self = shift;
|
104
|
|
|
|
|
|
|
$self->{optimizer_name} = 'opt';
|
105
|
|
|
|
|
|
|
$self->{_timestamp} = $self->getLoggingTime();
|
106
|
|
|
|
|
|
|
$self->{_sandbox_path} = $self->getSandboxPath();
|
107
|
|
|
|
|
|
|
$self->{min_win_percent} = 40 unless $self->{min_win_percent};
|
108
|
|
|
|
|
|
|
$self->{min_profit_percent} = 50 unless $self->{min_profit_percent};
|
109
|
|
|
|
|
|
|
$self->{min_no_of_trades} = 10 unless $self->{min_no_of_trades};
|
110
|
|
|
|
|
|
|
$self->{selection_index} = 2 unless $self->{selection_index};
|
111
|
|
|
|
|
|
|
$self->{profit_from} = 0 unless $self->{profit_from};
|
112
|
|
|
|
|
|
|
$self->{profit_to} = 20000 unless $self->{profit_to};
|
113
|
|
|
|
|
|
|
$self->{profit_incr} = 5000 unless $self->{profit_incr};
|
114
|
|
|
|
|
|
|
$self->{optimize_time} = 'NO' unless $self->{optimize_time};
|
115
|
|
|
|
|
|
|
return 1;
|
116
|
|
|
|
|
|
|
}
|
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
sub start_optimizer {
|
119
|
|
|
|
|
|
|
my $self = shift;
|
120
|
|
|
|
|
|
|
my $script = $self->{symbol};
|
121
|
|
|
|
|
|
|
$self->{_BT_Count} = 0;
|
122
|
|
|
|
|
|
|
#
|
123
|
|
|
|
|
|
|
# Step 1 : Start Amibroker engine
|
124
|
|
|
|
|
|
|
#
|
125
|
|
|
|
|
|
|
print "--------------------------------------\n\n";
|
126
|
|
|
|
|
|
|
print "Starting the amibroker engine\n";
|
127
|
|
|
|
|
|
|
my $broker = Amibroker::OLE::Interface->new(
|
128
|
|
|
|
|
|
|
{
|
129
|
|
|
|
|
|
|
verbose => 1,
|
130
|
|
|
|
|
|
|
dbpath => $self->{dbpath}
|
131
|
|
|
|
|
|
|
}
|
132
|
|
|
|
|
|
|
);
|
133
|
|
|
|
|
|
|
$broker->start_amibroker_engine();
|
134
|
|
|
|
|
|
|
$self->{_broker} = $broker;
|
135
|
|
|
|
|
|
|
print "Amibroker Engine Started\n" if ($broker);
|
136
|
|
|
|
|
|
|
#
|
137
|
|
|
|
|
|
|
# Step 2 : Create Sandbox path if it doesnt exists
|
138
|
|
|
|
|
|
|
#
|
139
|
|
|
|
|
|
|
File::Path::make_path( $self->{sandbox}, { verbose => 1 } );
|
140
|
|
|
|
|
|
|
#
|
141
|
|
|
|
|
|
|
# Step 3: a copy of AFL Template file
|
142
|
|
|
|
|
|
|
#
|
143
|
|
|
|
|
|
|
my $copiedFile = $self->copy_file(
|
144
|
|
|
|
|
|
|
$self->{afl_template}, $self->{destination_path},
|
145
|
|
|
|
|
|
|
$self->{timeframe}, $self->{optimizer_name},
|
146
|
|
|
|
|
|
|
$self->{symbol}
|
147
|
|
|
|
|
|
|
);
|
148
|
|
|
|
|
|
|
my $copyAfl = File::Slurp::read_file($copiedFile);
|
149
|
|
|
|
|
|
|
#
|
150
|
|
|
|
|
|
|
# Step 4: Replace STOCK NAME and LOT SIZE for the copied AFL File
|
151
|
|
|
|
|
|
|
#
|
152
|
|
|
|
|
|
|
$copyAfl =~ s/LOT_SIZE\=XXXX\;/LOT_SIZE\=$self->{lot_size}\;/g;
|
153
|
|
|
|
|
|
|
$copyAfl =~ s/STOCK_NAME\=\"XXXX\"\;/STOCK_NAME\=\"$script\"\;/g;
|
154
|
|
|
|
|
|
|
#
|
155
|
|
|
|
|
|
|
# Step 5: Save the Copied AFL file
|
156
|
|
|
|
|
|
|
#
|
157
|
|
|
|
|
|
|
File::Slurp::write_file( $copiedFile, $copyAfl );
|
158
|
|
|
|
|
|
|
#
|
159
|
|
|
|
|
|
|
# Step 6: Run for STRATEGY optimized two variables
|
160
|
|
|
|
|
|
|
#
|
161
|
|
|
|
|
|
|
if ( $copyAfl =~ /optimize/g ) {
|
162
|
|
|
|
|
|
|
my $status1 =
|
163
|
|
|
|
|
|
|
$self->run_optimize_engine( 'BASIC', $broker, $copiedFile, $script );
|
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# No results, so skip optimization for this Afl
|
166
|
|
|
|
|
|
|
# 99 is user-defined number - check for the called function
|
167
|
|
|
|
|
|
|
#
|
168
|
|
|
|
|
|
|
return 1 if ( $status1 == 99 );
|
169
|
|
|
|
|
|
|
}
|
170
|
|
|
|
|
|
|
#
|
171
|
|
|
|
|
|
|
# Step 7: Now get ready for time based optimization
|
172
|
|
|
|
|
|
|
#
|
173
|
|
|
|
|
|
|
if ( $self->{optimize_time} =~ /YES/i ) {
|
174
|
|
|
|
|
|
|
my $copy2Afl = File::Slurp::read_file($copiedFile);
|
175
|
|
|
|
|
|
|
my $firstTime =
|
176
|
|
|
|
|
|
|
"FirstTradeTime = optimize(\"FirstTradeTime\",093000,091500,123000,001000);";
|
177
|
|
|
|
|
|
|
my $lastTime =
|
178
|
|
|
|
|
|
|
"LastTradeTime = optimize(\"LastTradeTime\",151500,140000,153000,001000);";
|
179
|
|
|
|
|
|
|
$copy2Afl =~ s/FirstTradeTime.*/$firstTime/;
|
180
|
|
|
|
|
|
|
$copy2Afl =~ s/LastTradeTime.*/$lastTime/;
|
181
|
|
|
|
|
|
|
File::Slurp::write_file( $copiedFile, $copy2Afl );
|
182
|
|
|
|
|
|
|
#
|
183
|
|
|
|
|
|
|
# Step 8: Run for Time parameters optimized two variables
|
184
|
|
|
|
|
|
|
#
|
185
|
|
|
|
|
|
|
my $status2 =
|
186
|
|
|
|
|
|
|
$self->run_optimize_engine( 'TIME', $broker, $copiedFile, $script );
|
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# No results, so skip optimization for this Afl
|
189
|
|
|
|
|
|
|
# 99 is user-defined number - check for the called function
|
190
|
|
|
|
|
|
|
#
|
191
|
|
|
|
|
|
|
return 1 if ( $status2 == 99 );
|
192
|
|
|
|
|
|
|
}
|
193
|
|
|
|
|
|
|
#
|
194
|
|
|
|
|
|
|
# Step 20: Now insert profit limits to the AFL and save the files
|
195
|
|
|
|
|
|
|
#
|
196
|
|
|
|
|
|
|
$self->save_profit_limit_afls( $script, $copiedFile, $self->{lot_size} );
|
197
|
|
|
|
|
|
|
return 1;
|
198
|
|
|
|
|
|
|
}
|
199
|
|
|
|
|
|
|
#
|
200
|
|
|
|
|
|
|
# Create multiple afl files with different profit limits for backtesting
|
201
|
|
|
|
|
|
|
#
|
202
|
|
|
|
|
|
|
sub save_profit_limit_afls {
|
203
|
|
|
|
|
|
|
my ( $self, $script, $afl, $lotsize ) = @_;
|
204
|
|
|
|
|
|
|
my $copy3Afl = Utils::slurp_afl($afl);
|
205
|
|
|
|
|
|
|
my $destination = $self->{destination_path};
|
206
|
|
|
|
|
|
|
my $optname = $self->{optimizer_name};
|
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
for (
|
209
|
|
|
|
|
|
|
my $i = $self->{profit_from} ;
|
210
|
|
|
|
|
|
|
$i <= $self->{profit_to} ;
|
211
|
|
|
|
|
|
|
$i = $i + $self->{profit_incr}
|
212
|
|
|
|
|
|
|
)
|
213
|
|
|
|
|
|
|
{
|
214
|
|
|
|
|
|
|
my $tempFile =
|
215
|
|
|
|
|
|
|
$destination . '\\'
|
216
|
|
|
|
|
|
|
. $script . '-'
|
217
|
|
|
|
|
|
|
. $self->{timeframe} . '-'
|
218
|
|
|
|
|
|
|
. $optname . '-'
|
219
|
|
|
|
|
|
|
. $i . '.afl';
|
220
|
|
|
|
|
|
|
my $tempcopy = $copy3Afl;
|
221
|
|
|
|
|
|
|
my $value = $i / $lotsize;
|
222
|
|
|
|
|
|
|
my $searchString = "\/\/PROFITPOINTSKEYWORD";
|
223
|
|
|
|
|
|
|
my $replaceString =
|
224
|
|
|
|
|
|
|
"ApplyStop\(stopTypeProfit\,stopModePoint\,$value\,True\,True\)\;";
|
225
|
|
|
|
|
|
|
$tempcopy =~ s/$searchString/$replaceString/;
|
226
|
|
|
|
|
|
|
Utils::Write_to_file( $tempFile, $tempcopy );
|
227
|
|
|
|
|
|
|
}
|
228
|
|
|
|
|
|
|
return 1;
|
229
|
|
|
|
|
|
|
}
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
#
|
232
|
|
|
|
|
|
|
# Main logic of the tool goes here
|
233
|
|
|
|
|
|
|
#
|
234
|
|
|
|
|
|
|
sub run_optimize_engine {
|
235
|
|
|
|
|
|
|
my ( $self, $type, $broker, $copiedFile, $script ) = @_;
|
236
|
|
|
|
|
|
|
$self->service_amibroker()
|
237
|
|
|
|
|
|
|
if ( $self->{_BT_Count} >= $AMIBROKER_SERVICE_RUNS );
|
238
|
|
|
|
|
|
|
#
|
239
|
|
|
|
|
|
|
# Step 10 : Slurp AFL Template file to a string
|
240
|
|
|
|
|
|
|
#
|
241
|
|
|
|
|
|
|
my $slurpAfl = File::Slurp::read_file($copiedFile);
|
242
|
|
|
|
|
|
|
#
|
243
|
|
|
|
|
|
|
# Step 11 : Get the Sandbox and Result file path
|
244
|
|
|
|
|
|
|
#
|
245
|
|
|
|
|
|
|
my $sandbox_xml =
|
246
|
|
|
|
|
|
|
$self->{sandbox} . '/'
|
247
|
|
|
|
|
|
|
. $script . '-'
|
248
|
|
|
|
|
|
|
. $self->{timeframe} . '-'
|
249
|
|
|
|
|
|
|
. $self->{optimizer_name} . '-'
|
250
|
|
|
|
|
|
|
. $type . '.apx';
|
251
|
|
|
|
|
|
|
my $resultFile =
|
252
|
|
|
|
|
|
|
$self->{sandbox} . '/'
|
253
|
|
|
|
|
|
|
. $script . '-'
|
254
|
|
|
|
|
|
|
. $self->{timeframe} . '-'
|
255
|
|
|
|
|
|
|
. $self->{optimizer_name} . '-'
|
256
|
|
|
|
|
|
|
. $type . '.csv';
|
257
|
|
|
|
|
|
|
#
|
258
|
|
|
|
|
|
|
# Step 12 : Create APX file
|
259
|
|
|
|
|
|
|
#
|
260
|
|
|
|
|
|
|
my $range = 0; # by default range_type is all quotes
|
261
|
|
|
|
|
|
|
$range = 3
|
262
|
|
|
|
|
|
|
if ( $self->{from} && $self->{to} )
|
263
|
|
|
|
|
|
|
; # if from & to dates are supplied, then go for From_and_To
|
264
|
|
|
|
|
|
|
my $apxFile = Amibroker::OLE::APXCreator::create_apx_file(
|
265
|
|
|
|
|
|
|
{
|
266
|
|
|
|
|
|
|
apx_file => $sandbox_xml,
|
267
|
|
|
|
|
|
|
afl_file => $slurpAfl,
|
268
|
|
|
|
|
|
|
symbol => $script,
|
269
|
|
|
|
|
|
|
timeframe => $self->{timeframe},
|
270
|
|
|
|
|
|
|
from => $self->{from},
|
271
|
|
|
|
|
|
|
to => $self->{to},
|
272
|
|
|
|
|
|
|
range_type => $range,
|
273
|
|
|
|
|
|
|
apply_to => 1
|
274
|
|
|
|
|
|
|
}
|
275
|
|
|
|
|
|
|
);
|
276
|
|
|
|
|
|
|
#
|
277
|
|
|
|
|
|
|
# Step 13 : Run Amibroker Optimizer
|
278
|
|
|
|
|
|
|
#
|
279
|
|
|
|
|
|
|
my $runStatus = $broker->run_analysis(
|
280
|
|
|
|
|
|
|
{
|
281
|
|
|
|
|
|
|
action => 5,
|
282
|
|
|
|
|
|
|
symbol => $self->{symbol},
|
283
|
|
|
|
|
|
|
apx_file => $apxFile,
|
284
|
|
|
|
|
|
|
result_file => $resultFile
|
285
|
|
|
|
|
|
|
}
|
286
|
|
|
|
|
|
|
);
|
287
|
|
|
|
|
|
|
print "ERROR in Amibroker Engine - Symbol: " . $script . "\n\n"
|
288
|
|
|
|
|
|
|
unless ($runStatus);
|
289
|
|
|
|
|
|
|
#
|
290
|
|
|
|
|
|
|
# Step 14 : Load the result file generated by amibroker to hash
|
291
|
|
|
|
|
|
|
#
|
292
|
|
|
|
|
|
|
my $result_array = $self->load_result_file( $resultFile, $script );
|
293
|
|
|
|
|
|
|
print
|
294
|
|
|
|
|
|
|
"\n***WARN: Result is empty - Amibroker Didnt run for Symbol = $script\n"
|
295
|
|
|
|
|
|
|
unless (@$result_array);
|
296
|
|
|
|
|
|
|
print "Please report to developer - Seems Dates.txt is screwed\n\n"
|
297
|
|
|
|
|
|
|
unless (@$result_array);
|
298
|
|
|
|
|
|
|
$self->save_result_file( $type, $result_array, $script, 'RAW' );
|
299
|
|
|
|
|
|
|
#
|
300
|
|
|
|
|
|
|
# Step 15: Filter Result array
|
301
|
|
|
|
|
|
|
#
|
302
|
|
|
|
|
|
|
my $minprofit = $self->{min_profit_percent};
|
303
|
|
|
|
|
|
|
my $mintrades = $self->{min_no_of_trades};
|
304
|
|
|
|
|
|
|
my $minwinner = $self->{min_win_percent};
|
305
|
|
|
|
|
|
|
my $resultSet =
|
306
|
|
|
|
|
|
|
$self->filter_results( $type, $minprofit, $mintrades, $minwinner,
|
307
|
|
|
|
|
|
|
$result_array );
|
308
|
|
|
|
|
|
|
#
|
309
|
|
|
|
|
|
|
# Step 16: To sure we have sufficient data for selection
|
310
|
|
|
|
|
|
|
#
|
311
|
|
|
|
|
|
|
my $countFlg = 0;
|
312
|
|
|
|
|
|
|
while ($#$resultSet < ( $self->{selection_index} * 3 )
|
313
|
|
|
|
|
|
|
&& $countFlg < ( $self->{selection_index} * 3 ) )
|
314
|
|
|
|
|
|
|
{
|
315
|
|
|
|
|
|
|
$minprofit = $minprofit - 3;
|
316
|
|
|
|
|
|
|
$mintrades = $mintrades - 1;
|
317
|
|
|
|
|
|
|
$minwinner = $minwinner - 1;
|
318
|
|
|
|
|
|
|
$resultSet =
|
319
|
|
|
|
|
|
|
$self->filter_results( $type, $minprofit, $mintrades, $minwinner,
|
320
|
|
|
|
|
|
|
$result_array );
|
321
|
|
|
|
|
|
|
$countFlg++;
|
322
|
|
|
|
|
|
|
}
|
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# Step 17: If result set is less, then ignore that selection itself
|
325
|
|
|
|
|
|
|
#
|
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
if ( $#$resultSet < $self->{selection_index} ) {
|
328
|
|
|
|
|
|
|
my $copyOptAfl_default = File::Slurp::read_file($copiedFile);
|
329
|
|
|
|
|
|
|
my $selected =
|
330
|
|
|
|
|
|
|
$self->get_the_default_values( $resultSet->[0], $copyOptAfl_default );
|
331
|
|
|
|
|
|
|
$self->update_optimized_params(
|
332
|
|
|
|
|
|
|
$selected, # Values to update the afl
|
333
|
|
|
|
|
|
|
$resultSet->[0], # Header info of the result set
|
334
|
|
|
|
|
|
|
$copiedFile, # File path and the physical file
|
335
|
|
|
|
|
|
|
$copyOptAfl_default # File contents that needs modification
|
336
|
|
|
|
|
|
|
);
|
337
|
|
|
|
|
|
|
return 99; # No results number (user-defined)
|
338
|
|
|
|
|
|
|
}
|
339
|
|
|
|
|
|
|
#
|
340
|
|
|
|
|
|
|
# Step 18: Save filtered list to file
|
341
|
|
|
|
|
|
|
#
|
342
|
|
|
|
|
|
|
$self->save_result_file( $type, $resultSet, $script, 'Filtered' );
|
343
|
|
|
|
|
|
|
my ( $selected, $sorted_array ) = $self->sort_and_select($resultSet);
|
344
|
|
|
|
|
|
|
$self->save_result_file( $type, $sorted_array, $script, 'Sorted' );
|
345
|
|
|
|
|
|
|
#
|
346
|
|
|
|
|
|
|
# Step 19: Copy again the AFL file and update the parameters in the file.
|
347
|
|
|
|
|
|
|
#
|
348
|
|
|
|
|
|
|
my $copyOptAfl = File::Slurp::read_file($copiedFile);
|
349
|
|
|
|
|
|
|
$self->update_optimized_params(
|
350
|
|
|
|
|
|
|
$selected, # Values to update the afl
|
351
|
|
|
|
|
|
|
$resultSet->[0], # Header info of the result set
|
352
|
|
|
|
|
|
|
$copiedFile, # File path and the physical file
|
353
|
|
|
|
|
|
|
$copyOptAfl # File contents that needs modification
|
354
|
|
|
|
|
|
|
);
|
355
|
|
|
|
|
|
|
return 1;
|
356
|
|
|
|
|
|
|
}
|
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
#
|
359
|
|
|
|
|
|
|
# Service Amibroker - by restarting it.
|
360
|
|
|
|
|
|
|
# This will clean up the memory that is being holdup by the engine.
|
361
|
|
|
|
|
|
|
#
|
362
|
|
|
|
|
|
|
sub service_amibroker {
|
363
|
|
|
|
|
|
|
my $self = shift;
|
364
|
|
|
|
|
|
|
$self->{_BT_Count} = 0; # reseting the count
|
365
|
|
|
|
|
|
|
my $broker = $self->{_broker};
|
366
|
|
|
|
|
|
|
print "--------------------------------------\n";
|
367
|
|
|
|
|
|
|
print "Restarting Amibroker Engine\n";
|
368
|
|
|
|
|
|
|
$broker->shutdown_amibroker_engine();
|
369
|
|
|
|
|
|
|
sleep(30); #Sleep for half minute and let the amibroker engine relax
|
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
print "Starting the amibroker engine\n";
|
372
|
|
|
|
|
|
|
my $newbroker = Amibroker::OLE::Interface->new(
|
373
|
|
|
|
|
|
|
{
|
374
|
|
|
|
|
|
|
verbose => 1,
|
375
|
|
|
|
|
|
|
dbpath => $self->{dbpath}
|
376
|
|
|
|
|
|
|
}
|
377
|
|
|
|
|
|
|
);
|
378
|
|
|
|
|
|
|
$newbroker->start_amibroker_engine();
|
379
|
|
|
|
|
|
|
$self->{_broker} = $newbroker;
|
380
|
|
|
|
|
|
|
print "Amibroker Engine Re-Started again\n" if ($newbroker);
|
381
|
|
|
|
|
|
|
print "--------------------------------------\n\n";
|
382
|
|
|
|
|
|
|
return 1;
|
383
|
|
|
|
|
|
|
}
|
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
#
|
386
|
|
|
|
|
|
|
# Supports only Two parameters updation
|
387
|
|
|
|
|
|
|
#
|
388
|
|
|
|
|
|
|
sub update_optimized_params {
|
389
|
|
|
|
|
|
|
my ( $self, $selected, $header, $file, $slurpData ) = @_;
|
390
|
|
|
|
|
|
|
my $paramName1 = $header->[3];
|
391
|
|
|
|
|
|
|
my $paramName2 = $header->[4];
|
392
|
|
|
|
|
|
|
my $paramValue1 = $selected->[3];
|
393
|
|
|
|
|
|
|
my $paramValue2 = $selected->[4];
|
394
|
|
|
|
|
|
|
$slurpData =~ s/optimize\(\"$paramName1\".*;/$paramValue1\;/;
|
395
|
|
|
|
|
|
|
$slurpData =~ s/optimize\(\"$paramName2\".*;/$paramValue2\;/;
|
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
File::Slurp::write_file( $file, $slurpData );
|
398
|
|
|
|
|
|
|
return 1;
|
399
|
|
|
|
|
|
|
}
|
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
sub get_the_default_values {
|
402
|
|
|
|
|
|
|
my ( $self, $header, $slurpData ) = @_;
|
403
|
|
|
|
|
|
|
my $paramName1 = $header->[3];
|
404
|
|
|
|
|
|
|
$slurpData =~ /$paramName1.*",([A-Za-z0-9]+)\,/;
|
405
|
|
|
|
|
|
|
my $paramValue1 = $1;
|
406
|
|
|
|
|
|
|
my $paramName2 = $header->[4];
|
407
|
|
|
|
|
|
|
$slurpData =~ /$paramName2.*",([A-Za-z0-9]+)\,/;
|
408
|
|
|
|
|
|
|
my $paramValue2 = $1;
|
409
|
|
|
|
|
|
|
my @selected = ( 0, 0, 0, $paramValue1, $paramValue2 );
|
410
|
|
|
|
|
|
|
return \@selected;
|
411
|
|
|
|
|
|
|
}
|
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
# Sorts the result set and selects only one row.
|
414
|
|
|
|
|
|
|
# Default sort is done for Profit% - You can add more in next release
|
415
|
|
|
|
|
|
|
#
|
416
|
|
|
|
|
|
|
sub sort_and_select {
|
417
|
|
|
|
|
|
|
my ( $self, $result ) = @_;
|
418
|
|
|
|
|
|
|
my $header = shift @$result;
|
419
|
|
|
|
|
|
|
my @sorted = sort { $b->[0] <=> $a->[0] } @$result;
|
420
|
|
|
|
|
|
|
my $selection;
|
421
|
|
|
|
|
|
|
if ( $#sorted < $self->{selection_index} ) {
|
422
|
|
|
|
|
|
|
$selection = $sorted[$#sorted];
|
423
|
|
|
|
|
|
|
}
|
424
|
|
|
|
|
|
|
else {
|
425
|
|
|
|
|
|
|
$selection = $sorted[ $self->{selection_index} - 1 ];
|
426
|
|
|
|
|
|
|
}
|
427
|
|
|
|
|
|
|
unshift @$result, $header;
|
428
|
|
|
|
|
|
|
return ( $selection, \@sorted );
|
429
|
|
|
|
|
|
|
}
|
430
|
|
|
|
|
|
|
#
|
431
|
|
|
|
|
|
|
# To save the results of hash to csv separated file.
|
432
|
|
|
|
|
|
|
#
|
433
|
|
|
|
|
|
|
sub save_result_file {
|
434
|
|
|
|
|
|
|
my ( $self, $type, $result_array, $script, $extn ) = @_;
|
435
|
|
|
|
|
|
|
my $file =
|
436
|
|
|
|
|
|
|
$self->{sandbox} . '\\'
|
437
|
|
|
|
|
|
|
. $script . '-'
|
438
|
|
|
|
|
|
|
. $self->{timeframe} . '-'
|
439
|
|
|
|
|
|
|
. $type . '-'
|
440
|
|
|
|
|
|
|
. $self->{optimizer_name} . '-'
|
441
|
|
|
|
|
|
|
. $extn . '.csv';
|
442
|
|
|
|
|
|
|
File::Path::make_path( $self->{sandbox}, { verbose => 1 } )
|
443
|
|
|
|
|
|
|
unless ( -d $self->{sandbox} );
|
444
|
|
|
|
|
|
|
open( my $SFH, '>', $file )
|
445
|
|
|
|
|
|
|
or croak( '[ERROR] : Could not open file ' . $file . " $!\n" );
|
446
|
|
|
|
|
|
|
$self->dump_data_to_file( $SFH, $result_array );
|
447
|
|
|
|
|
|
|
close $SFH;
|
448
|
|
|
|
|
|
|
return 1;
|
449
|
|
|
|
|
|
|
}
|
450
|
|
|
|
|
|
|
#
|
451
|
|
|
|
|
|
|
# Saving the data to file
|
452
|
|
|
|
|
|
|
#
|
453
|
|
|
|
|
|
|
sub dump_data_to_file {
|
454
|
|
|
|
|
|
|
my ( $self, $FH, $result_array ) = @_;
|
455
|
|
|
|
|
|
|
foreach (@$result_array) {
|
456
|
|
|
|
|
|
|
print $FH join ",", @$_;
|
457
|
|
|
|
|
|
|
print $FH "\n";
|
458
|
|
|
|
|
|
|
}
|
459
|
|
|
|
|
|
|
return 1;
|
460
|
|
|
|
|
|
|
}
|
461
|
|
|
|
|
|
|
#
|
462
|
|
|
|
|
|
|
# Logic to filter the result set
|
463
|
|
|
|
|
|
|
#
|
464
|
|
|
|
|
|
|
sub filter_results {
|
465
|
|
|
|
|
|
|
my ( $self, $type, $minprofit, $mintrades, $minwinner, $result_array ) = @_;
|
466
|
|
|
|
|
|
|
if ( $type eq 'TIME' ) {
|
467
|
|
|
|
|
|
|
$result_array = $self->clear_unwanted_timings( $result_array, 3 )
|
468
|
|
|
|
|
|
|
; # clean FirstTradeTime
|
469
|
|
|
|
|
|
|
$result_array = $self->clear_unwanted_timings( $result_array, 4 )
|
470
|
|
|
|
|
|
|
; # clean LastTradeTime
|
471
|
|
|
|
|
|
|
}
|
472
|
|
|
|
|
|
|
#
|
473
|
|
|
|
|
|
|
# Filter minimum profits % - array index is 0
|
474
|
|
|
|
|
|
|
$result_array = $self->recursive_filter( $result_array, $minprofit, 0 );
|
475
|
|
|
|
|
|
|
#
|
476
|
|
|
|
|
|
|
# Filter minimum number of trades - array index is 1
|
477
|
|
|
|
|
|
|
$result_array = $self->recursive_filter( $result_array, $mintrades, 1 );
|
478
|
|
|
|
|
|
|
#
|
479
|
|
|
|
|
|
|
# Filter minimum winners % - array index is 2
|
480
|
|
|
|
|
|
|
$result_array = $self->recursive_filter( $result_array, $minwinner, 2 );
|
481
|
|
|
|
|
|
|
return $result_array;
|
482
|
|
|
|
|
|
|
}
|
483
|
|
|
|
|
|
|
#
|
484
|
|
|
|
|
|
|
# Optimization results has timings that are from 1 to 100, we want just 1 to 60.
|
485
|
|
|
|
|
|
|
#
|
486
|
|
|
|
|
|
|
sub clear_unwanted_timings {
|
487
|
|
|
|
|
|
|
my ( $self, $result_array, $index ) = @_;
|
488
|
|
|
|
|
|
|
my @temp = @$result_array;
|
489
|
|
|
|
|
|
|
for ( my $i = $#temp ; $i > 0 ; $i-- ) {
|
490
|
|
|
|
|
|
|
unless ( $self->check_if_in_time_range( \@temp, $i, $index ) ) {
|
491
|
|
|
|
|
|
|
splice( @temp, $i, 1 );
|
492
|
|
|
|
|
|
|
}
|
493
|
|
|
|
|
|
|
}
|
494
|
|
|
|
|
|
|
return \@temp;
|
495
|
|
|
|
|
|
|
}
|
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
sub check_if_in_time_range {
|
498
|
|
|
|
|
|
|
my ( $self, $temp, $i, $index ) = @_;
|
499
|
|
|
|
|
|
|
return 1
|
500
|
|
|
|
|
|
|
if ( $temp->[$i]->[$index] >= 91500 && $temp->[$i]->[$index] <= 95500 );
|
501
|
|
|
|
|
|
|
return 1
|
502
|
|
|
|
|
|
|
if ( $temp->[$i]->[$index] >= 100000 && $temp->[$i]->[$index] <= 105500 );
|
503
|
|
|
|
|
|
|
return 1
|
504
|
|
|
|
|
|
|
if ( $temp->[$i]->[$index] >= 110000 && $temp->[$i]->[$index] <= 115500 );
|
505
|
|
|
|
|
|
|
return 1
|
506
|
|
|
|
|
|
|
if ( $temp->[$i]->[$index] >= 120000 && $temp->[$i]->[$index] <= 125500 );
|
507
|
|
|
|
|
|
|
return 1
|
508
|
|
|
|
|
|
|
if ( $temp->[$i]->[$index] >= 130000 && $temp->[$i]->[$index] <= 135500 );
|
509
|
|
|
|
|
|
|
return 1
|
510
|
|
|
|
|
|
|
if ( $temp->[$i]->[$index] >= 140000 && $temp->[$i]->[$index] <= 145500 );
|
511
|
|
|
|
|
|
|
return 1
|
512
|
|
|
|
|
|
|
if ( $temp->[$i]->[$index] >= 150000 && $temp->[$i]->[$index] <= 151000 );
|
513
|
|
|
|
|
|
|
return 0; # return 0 if nothing fits
|
514
|
|
|
|
|
|
|
}
|
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
#
|
517
|
|
|
|
|
|
|
# Call the filtering engine
|
518
|
|
|
|
|
|
|
#
|
519
|
|
|
|
|
|
|
sub recursive_filter {
|
520
|
|
|
|
|
|
|
my ( $self, $result_array, $min_value, $index ) = @_;
|
521
|
|
|
|
|
|
|
my @temp = @$result_array;
|
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
for ( my $i = $#temp ; $i > 0 ; $i-- ) {
|
524
|
|
|
|
|
|
|
splice( @temp, $i, 1 ) if ( $temp[$i]->[$index] < $min_value );
|
525
|
|
|
|
|
|
|
}
|
526
|
|
|
|
|
|
|
return \@temp;
|
527
|
|
|
|
|
|
|
}
|
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
#
|
530
|
|
|
|
|
|
|
# Copy a given file
|
531
|
|
|
|
|
|
|
#
|
532
|
|
|
|
|
|
|
sub copy_file {
|
533
|
|
|
|
|
|
|
my ( $self, $source, $destination, $timeframe, $optname, $script ) = @_;
|
534
|
|
|
|
|
|
|
my $final =
|
535
|
|
|
|
|
|
|
$destination . '\\'
|
536
|
|
|
|
|
|
|
. $script . '-'
|
537
|
|
|
|
|
|
|
. $timeframe . '-'
|
538
|
|
|
|
|
|
|
. $optname . '.afl';
|
539
|
|
|
|
|
|
|
File::Copy::copy( $source, $final )
|
540
|
|
|
|
|
|
|
or croak( '[ERROR] : Copy failed: ' . "$!" );
|
541
|
|
|
|
|
|
|
return $final;
|
542
|
|
|
|
|
|
|
}
|
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
#
|
545
|
|
|
|
|
|
|
# Clean up the sandbox for allowing fresh optimize to run
|
546
|
|
|
|
|
|
|
#
|
547
|
|
|
|
|
|
|
sub clear_sandbox {
|
548
|
|
|
|
|
|
|
my $self = shift;
|
549
|
|
|
|
|
|
|
File::Path::remove_tree( $self->{sandbox}, { verbose => 1 } );
|
550
|
|
|
|
|
|
|
print "Sandbox cleaned up\n";
|
551
|
|
|
|
|
|
|
return 1;
|
552
|
|
|
|
|
|
|
}
|
553
|
|
|
|
|
|
|
#
|
554
|
|
|
|
|
|
|
# Keep a copy of sandbox in the logs foler for later debugging
|
555
|
|
|
|
|
|
|
#
|
556
|
|
|
|
|
|
|
sub copy_sandbox {
|
557
|
|
|
|
|
|
|
my $self = shift;
|
558
|
|
|
|
|
|
|
if ( $self->{log_path} ) {
|
559
|
|
|
|
|
|
|
my $logPath = $self->{log_path} . '/' . $self->{_timestamp};
|
560
|
|
|
|
|
|
|
File::Copy::Recursive::dircopy( $self->{sandbox}, $logPath )
|
561
|
|
|
|
|
|
|
or print("WARN: Could not copy Sandbox log: $!\n");
|
562
|
|
|
|
|
|
|
print "Sandbox copy completed\n";
|
563
|
|
|
|
|
|
|
}
|
564
|
|
|
|
|
|
|
return 1;
|
565
|
|
|
|
|
|
|
}
|
566
|
|
|
|
|
|
|
#
|
567
|
|
|
|
|
|
|
# Load the result file generated by Amibroker engine
|
568
|
|
|
|
|
|
|
#
|
569
|
|
|
|
|
|
|
sub load_result_file {
|
570
|
|
|
|
|
|
|
my ( $self, $resultFile ) = @_;
|
571
|
|
|
|
|
|
|
my @raw = File::Slurp::read_file($resultFile);
|
572
|
|
|
|
|
|
|
my @result;
|
573
|
|
|
|
|
|
|
foreach (@raw) {
|
574
|
|
|
|
|
|
|
chomp($_);
|
575
|
|
|
|
|
|
|
my @temp = split( /,/, $_ );
|
576
|
|
|
|
|
|
|
splice @temp, 0, 2;
|
577
|
|
|
|
|
|
|
splice @temp, 1, 18;
|
578
|
|
|
|
|
|
|
splice @temp, 2, 4;
|
579
|
|
|
|
|
|
|
splice @temp, 3, 10;
|
580
|
|
|
|
|
|
|
push( @result, \@temp );
|
581
|
|
|
|
|
|
|
}
|
582
|
|
|
|
|
|
|
return \@result;
|
583
|
|
|
|
|
|
|
}
|
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
#
|
586
|
|
|
|
|
|
|
# create a timestamp for log files
|
587
|
|
|
|
|
|
|
#
|
588
|
|
|
|
|
|
|
sub getLoggingTime {
|
589
|
|
|
|
|
|
|
my $self = shift;
|
590
|
|
|
|
|
|
|
my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
|
591
|
|
|
|
|
|
|
localtime(time);
|
592
|
|
|
|
|
|
|
my $nice_timestamp = sprintf(
|
593
|
|
|
|
|
|
|
"%04d%02d%02d-%02d%02d%02d",
|
594
|
|
|
|
|
|
|
$year + 1900,
|
595
|
|
|
|
|
|
|
$mon + 1, $mday, $hour, $min, $sec
|
596
|
|
|
|
|
|
|
);
|
597
|
|
|
|
|
|
|
return $nice_timestamp;
|
598
|
|
|
|
|
|
|
}
|
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
sub getSandboxPath {
|
601
|
|
|
|
|
|
|
my $self = shift;
|
602
|
|
|
|
|
|
|
my $GetTempAPI =
|
603
|
|
|
|
|
|
|
Win32::API->new( 'kernel32', 'GetTempPath', [ 'N', 'P' ], 'N' );
|
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
my $lpBuffer = " " x 80;
|
606
|
|
|
|
|
|
|
my $length = $GetTempAPI->Call( 80, $lpBuffer );
|
607
|
|
|
|
|
|
|
my $tempPath = substr( $lpBuffer, 0, $length );
|
608
|
|
|
|
|
|
|
|
609
|
|
|
|
|
|
|
$tempPath .= 'sandbox-' . $self->{_timestamp};
|
610
|
|
|
|
|
|
|
print "Temp directory: $tempPath\n";
|
611
|
|
|
|
|
|
|
return $tempPath;
|
612
|
|
|
|
|
|
|
}
|
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
sub _is_arg {
|
615
|
|
|
|
|
|
|
my ($arg) = @_;
|
616
|
|
|
|
|
|
|
return ( ref $arg eq 'HASH' );
|
617
|
|
|
|
|
|
|
}
|
618
|
|
|
|
|
|
|
|
619
|
|
|
|
|
|
|
sub _check_valid_args {
|
620
|
|
|
|
|
|
|
my @list = @_;
|
621
|
|
|
|
|
|
|
my %args_permitted = map { $_ => 1 } (
|
622
|
|
|
|
|
|
|
qw|
|
623
|
|
|
|
|
|
|
dbpath
|
624
|
|
|
|
|
|
|
destination_path
|
625
|
|
|
|
|
|
|
timeframe
|
626
|
|
|
|
|
|
|
symbol
|
627
|
|
|
|
|
|
|
afl_template
|
628
|
|
|
|
|
|
|
lot_size
|
629
|
|
|
|
|
|
|
log_path
|
630
|
|
|
|
|
|
|
optimizer_name
|
631
|
|
|
|
|
|
|
min_win_percent
|
632
|
|
|
|
|
|
|
min_profit_percent
|
633
|
|
|
|
|
|
|
min_no_of_trades
|
634
|
|
|
|
|
|
|
selection_index
|
635
|
|
|
|
|
|
|
profit_from
|
636
|
|
|
|
|
|
|
profit_to
|
637
|
|
|
|
|
|
|
profit_incr
|
638
|
|
|
|
|
|
|
optimize_time
|
639
|
|
|
|
|
|
|
margin_amt
|
640
|
|
|
|
|
|
|
from
|
641
|
|
|
|
|
|
|
to
|
642
|
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
);
|
644
|
|
|
|
|
|
|
my @bad_args = ();
|
645
|
|
|
|
|
|
|
my $arg = pop @list;
|
646
|
|
|
|
|
|
|
for my $k ( sort keys %{$arg} ) {
|
647
|
|
|
|
|
|
|
push @bad_args, $k unless $args_permitted{$k};
|
648
|
|
|
|
|
|
|
}
|
649
|
|
|
|
|
|
|
croak("Unrecognized option(s) passed to Amibroker OLE: @bad_args")
|
650
|
|
|
|
|
|
|
if @bad_args;
|
651
|
|
|
|
|
|
|
return $arg;
|
652
|
|
|
|
|
|
|
}
|
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
#
|
655
|
|
|
|
|
|
|
# Explicitly call the destructor
|
656
|
|
|
|
|
|
|
#
|
657
|
|
|
|
|
|
|
sub DESTROY {
|
658
|
|
|
|
|
|
|
my $self = shift;
|
659
|
|
|
|
|
|
|
my $broker = $self->{_broker};
|
660
|
|
|
|
|
|
|
$broker->shutdown_amibroker_engine() if ( $self->{_broker} );
|
661
|
|
|
|
|
|
|
$self->copy_sandbox();
|
662
|
|
|
|
|
|
|
$self->clear_sandbox();
|
663
|
|
|
|
|
|
|
return 1;
|
664
|
|
|
|
|
|
|
}
|
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
1; # End of Amibroker::AFL::Optimizer
|
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
__END__
|