| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Finance::IG; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# use 5.010000; I cannot get this to work, trying to say it should run with perl 5.10 or greater and should be fine with 5.32 |
|
4
|
|
|
|
|
|
|
# but get message ! Installing the dependencies failed: Your Perl (5.032001) is not in the range '5.10' |
|
5
|
1
|
|
|
1
|
|
73651
|
use strict; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
33
|
|
|
6
|
1
|
|
|
1
|
|
5
|
no strict 'refs'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
23
|
|
|
7
|
1
|
|
|
1
|
|
17
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
58
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=encoding utf8 |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Finance::IG - - Module for doing useful stuff with IG Markets REST API. |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
16
|
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
This is very much a first draft, but will enable you to get simple arrays of positions, print them out possily some simple trading. |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
Its proof of concept in perl beyond anything else, extend it as you need to. |
|
20
|
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
I have only used it for spreadbet accounts, it would be simple to extend to CFD's but I dont have CFD data or an interest in CFD's so have not done this. |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
You will need an API key to use this module, available free from IG Markets. |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 VERSION |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
Version 0.102 |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
=cut |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $VERSION = '0.102'; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
use Finance::IG; |
|
37
|
|
|
|
|
|
|
use strict; |
|
38
|
|
|
|
|
|
|
no strict 'refs'; |
|
39
|
|
|
|
|
|
|
use warnings; |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
my $ig=iFinance::IG->new( |
|
42
|
|
|
|
|
|
|
username=> "demome", |
|
43
|
|
|
|
|
|
|
password=> "mypassword", |
|
44
|
|
|
|
|
|
|
apikey=> "4398232394029341776153276512736icab", |
|
45
|
|
|
|
|
|
|
isdemo=>0, |
|
46
|
|
|
|
|
|
|
); |
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
my $p=$ig->positions(); # Get a list of positions |
|
49
|
|
|
|
|
|
|
$p=$ig->agg($p,$sortlist); # Aggregate them, so one item per instrument. |
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
my $format="%-41sinstrumentName %+6.2fsize %-9.2flevel ". |
|
52
|
|
|
|
|
|
|
"%-9.2fbid £%-8.2fprofit %5.1fprofitpc%% £%10.2fatrisk\n", |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
$ig->printpos("stdout" , [], $format); |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
for my $position (@$p) |
|
57
|
|
|
|
|
|
|
{ |
|
58
|
|
|
|
|
|
|
$ig-> printpos("stdout" ,$position,$format); |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head1 UTILITIES |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
The utility igdisp.pl is installed with this module and may be used to list your positions on IG. A help message can be obtained with igdisp.pl -h |
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
This is a list of currently implemented methods |
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 new |
|
70
|
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Normal parameters, as above. |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
col=>1 |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Causes Finance::IG to try to use Term::Chrome to do some simple coloration of output. |
|
76
|
|
|
|
|
|
|
If Term::Chrome is not installed, it will be silently ignored. See printpos. |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=head2 login |
|
79
|
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Originally needed to be called once after new and before other calls. Now this is done automatically, |
|
81
|
|
|
|
|
|
|
so you do not need to use this or be aware of it. Look for a 401 error if your password is |
|
82
|
|
|
|
|
|
|
wrong. |
|
83
|
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
No Parameters. |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=head2 printpos print out a formatted hash as one line |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Parameters |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
file - can be a file handle or the string stdout or the glob *STDOUT |
|
92
|
|
|
|
|
|
|
A position of other shallow hash, |
|
93
|
|
|
|
|
|
|
A format string. The format string is similar to a printf format string, for example %s says print out a string |
|
94
|
|
|
|
|
|
|
however, the name of the item to be printed follows the letter, eg %sinstrumentName print the string instrument name. |
|
95
|
|
|
|
|
|
|
optional up |
|
96
|
|
|
|
|
|
|
optional down |
|
97
|
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
A title line can be printed by either passing an array ref instead of a position, in which case the array ref can contain |
|
99
|
|
|
|
|
|
|
the titles to print. If the array is empty then the titles will be generated from the format string. |
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
up and down can be provided and represent negative and posite limits on dbid element by default. |
|
102
|
|
|
|
|
|
|
Alternatively, provide up only and make it a subroutine ref. |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
The subroutime takes parameter a position, and should return escape characters (from Term::Chrome to colorise the line. |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head2 transactions - retrieve transactions history |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
transactions(++$page,Time::Piece->strptime("2020-01-01","%Y-%m-%d-%H.%M"),scalar localtime) |
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
Parameters |
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
Paging number, start at 1 |
|
113
|
|
|
|
|
|
|
Start time, can be a string or a Time::Piece |
|
114
|
|
|
|
|
|
|
Endtime |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
return a reference to an array of transactions for that time span. Each transaction is a hash of data. |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
=cut |
|
119
|
|
|
|
|
|
|
|
|
120
|
1
|
|
|
1
|
|
635
|
use Moose; |
|
|
1
|
|
|
|
|
483214
|
|
|
|
1
|
|
|
|
|
8
|
|
|
121
|
1
|
|
|
1
|
|
8386
|
use JSON; |
|
|
1
|
|
|
|
|
11161
|
|
|
|
1
|
|
|
|
|
5
|
|
|
122
|
1
|
|
|
1
|
|
695
|
use REST::Client; |
|
|
1
|
|
|
|
|
46198
|
|
|
|
1
|
|
|
|
|
42
|
|
|
123
|
|
|
|
|
|
|
#use Data::Dump qw(dump); # used in some commented out debug statements |
|
124
|
|
|
|
|
|
|
#use Scalar::Util; |
|
125
|
1
|
|
|
1
|
|
604
|
use Time::Piece; |
|
|
1
|
|
|
|
|
8084
|
|
|
|
1
|
|
|
|
|
5
|
|
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
BEGIN { |
|
128
|
1
|
50
|
|
1
|
|
213
|
if (eval("require Term::Chrome")) |
|
129
|
|
|
|
|
|
|
{ |
|
130
|
0
|
|
|
|
|
0
|
Term::Chrome->import(); |
|
131
|
|
|
|
|
|
|
} |
|
132
|
|
|
|
|
|
|
else |
|
133
|
|
|
|
|
|
|
{ |
|
134
|
1
|
|
|
0
|
1
|
5
|
map { eval ("sub $_ {}") } qw(Red Blue Bold Reset Underline Green color); # need these to avoid compile time errors. |
|
|
7
|
|
|
0
|
1
|
3491
|
|
|
|
|
|
|
0
|
1
|
|
|
|
|
|
|
|
0
|
1
|
|
|
|
|
|
|
|
0
|
1
|
|
|
|
|
|
|
|
0
|
1
|
|
|
|
|
|
|
|
0
|
1
|
|
|
|
135
|
|
|
|
|
|
|
} |
|
136
|
|
|
|
|
|
|
} |
|
137
|
|
|
|
|
|
|
has 'apikey' => ( |
|
138
|
|
|
|
|
|
|
is=>'ro', |
|
139
|
|
|
|
|
|
|
isa=>'Str', |
|
140
|
|
|
|
|
|
|
required=>1, |
|
141
|
|
|
|
|
|
|
); |
|
142
|
|
|
|
|
|
|
has 'username' => ( |
|
143
|
|
|
|
|
|
|
is=>'ro', |
|
144
|
|
|
|
|
|
|
isa=>'Str', |
|
145
|
|
|
|
|
|
|
required=>1, |
|
146
|
|
|
|
|
|
|
); |
|
147
|
|
|
|
|
|
|
has 'password' => ( |
|
148
|
|
|
|
|
|
|
is=>'ro', |
|
149
|
|
|
|
|
|
|
isa=>'Str', |
|
150
|
|
|
|
|
|
|
required=>1, |
|
151
|
|
|
|
|
|
|
); |
|
152
|
|
|
|
|
|
|
has 'isdemo' => ( |
|
153
|
|
|
|
|
|
|
is=>'ro', |
|
154
|
|
|
|
|
|
|
isa=>'Bool', |
|
155
|
|
|
|
|
|
|
required=>1, |
|
156
|
|
|
|
|
|
|
); |
|
157
|
|
|
|
|
|
|
has 'CST' => ( |
|
158
|
|
|
|
|
|
|
is=>'rw', |
|
159
|
|
|
|
|
|
|
isa=>'Str', |
|
160
|
|
|
|
|
|
|
); |
|
161
|
|
|
|
|
|
|
has 'XSECURITYTOKEN' => ( |
|
162
|
|
|
|
|
|
|
is=>'rw', |
|
163
|
|
|
|
|
|
|
isa=>'Str', |
|
164
|
|
|
|
|
|
|
); |
|
165
|
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
has 'XSTTIME' => ( |
|
167
|
|
|
|
|
|
|
is=>'rw', |
|
168
|
|
|
|
|
|
|
isa=>'Int', |
|
169
|
|
|
|
|
|
|
); |
|
170
|
|
|
|
|
|
|
has 'col' => ( # set to 1 to use Term::Chrome for coloration. |
|
171
|
|
|
|
|
|
|
is=>'rw', |
|
172
|
|
|
|
|
|
|
isa=>'Bool', |
|
173
|
|
|
|
|
|
|
default=>0, |
|
174
|
|
|
|
|
|
|
); |
|
175
|
|
|
|
|
|
|
has 'uds' => ( |
|
176
|
|
|
|
|
|
|
is=>'rw', |
|
177
|
|
|
|
|
|
|
isa=>'Str', |
|
178
|
|
|
|
|
|
|
default=>'', |
|
179
|
|
|
|
|
|
|
); |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
around 'new' => sub { |
|
182
|
|
|
|
|
|
|
my $orig = shift; |
|
183
|
|
|
|
|
|
|
my $self = shift; |
|
184
|
|
|
|
|
|
|
my $r; |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
$r=$self->$orig(@_); |
|
187
|
|
|
|
|
|
|
$r->login; |
|
188
|
|
|
|
|
|
|
return $r; |
|
189
|
|
|
|
|
|
|
}; |
|
190
|
|
|
|
|
|
|
sub _url |
|
191
|
|
|
|
|
|
|
{ |
|
192
|
0
|
|
|
0
|
|
|
my ($self) = @_; |
|
193
|
0
|
0
|
|
|
|
|
return 'https://demo-api.ig.com/gateway/deal' if ( $self->isdemo); |
|
194
|
0
|
|
|
|
|
|
return 'https://api.ig.com/gateway/deal'; |
|
195
|
|
|
|
|
|
|
} |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
########################################################################## |
|
199
|
|
|
|
|
|
|
=head2 login - loginto the account. |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Parameters - none |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
login to the object, using the parameters provided to new. |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
You should call this just once per object after calling new. |
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=cut |
|
208
|
|
|
|
|
|
|
########################################################################## |
|
209
|
|
|
|
|
|
|
sub login { |
|
210
|
0
|
|
|
0
|
1
|
|
my ($self) = @_; |
|
211
|
0
|
|
|
|
|
|
my $headers = |
|
212
|
|
|
|
|
|
|
{ |
|
213
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
214
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
215
|
|
|
|
|
|
|
VERSION => 2, |
|
216
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey |
|
217
|
|
|
|
|
|
|
}; |
|
218
|
0
|
|
|
|
|
|
my $data = { |
|
219
|
|
|
|
|
|
|
identifier => $self->username, |
|
220
|
|
|
|
|
|
|
password => $self->password, |
|
221
|
|
|
|
|
|
|
}; |
|
222
|
|
|
|
|
|
|
# my $jdata = encode_json($data); |
|
223
|
0
|
|
|
|
|
|
my $jdata=JSON->new->canonical->encode($data); |
|
224
|
|
|
|
|
|
|
|
|
225
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
226
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
|
$client->POST ( |
|
230
|
|
|
|
|
|
|
'/session', |
|
231
|
|
|
|
|
|
|
$jdata, |
|
232
|
|
|
|
|
|
|
$headers |
|
233
|
|
|
|
|
|
|
); |
|
234
|
0
|
|
|
|
|
|
my $code=$client->responseCode(); |
|
235
|
0
|
0
|
|
|
|
|
die "response code from login $code" if ($code!=200); |
|
236
|
0
|
|
0
|
|
|
|
$self->CST($client->responseHeader('CST') // die "No CST header in login response"); |
|
237
|
0
|
|
0
|
|
|
|
$self->XSECURITYTOKEN($client->responseHeader('X-SECURITY-TOKEN') // die "No X-SECURITY-TOKEN in login response header"); |
|
238
|
0
|
|
|
|
|
|
$self->XSTTIME(time()); |
|
239
|
0
|
|
|
|
|
|
return; |
|
240
|
|
|
|
|
|
|
} |
|
241
|
|
|
|
|
|
|
########################################################################## |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=head2 flatten |
|
244
|
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
Parameters |
|
246
|
|
|
|
|
|
|
1 Ref to array of hashes to flatten or a ref to a hash to flatten |
|
247
|
|
|
|
|
|
|
2 ref to an array of items to flatten, or just a single item name. |
|
248
|
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
Typical use of this is for a position that as it comes back from IG contains a market and a position |
|
250
|
|
|
|
|
|
|
byut we would prefer all items at the top level. This would moves all the keys of position and market up one level and |
|
251
|
|
|
|
|
|
|
would remove the keys market and position. |
|
252
|
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
$self->flatten($hash, [qw(market position)]); |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=cut |
|
256
|
|
|
|
|
|
|
########################################################################## |
|
257
|
|
|
|
|
|
|
sub flatten |
|
258
|
|
|
|
|
|
|
{ |
|
259
|
0
|
|
|
0
|
1
|
|
my ($self)=shift; |
|
260
|
0
|
|
|
|
|
|
my ($hash)=shift; |
|
261
|
0
|
|
|
|
|
|
my ($toflatten)=shift; |
|
262
|
|
|
|
|
|
|
|
|
263
|
0
|
0
|
|
|
|
|
$hash=[$hash] if (ref($hash) ne 'ARRAY'); |
|
264
|
0
|
0
|
|
|
|
|
$toflatten=[$toflatten] if (ref($toflatten) ne 'ARRAY'); |
|
265
|
|
|
|
|
|
|
|
|
266
|
0
|
|
|
|
|
|
for my $h (@$hash) |
|
267
|
|
|
|
|
|
|
{ |
|
268
|
0
|
|
|
|
|
|
for my $key (@$toflatten) |
|
269
|
|
|
|
|
|
|
{ |
|
270
|
0
|
0
|
|
|
|
|
if (exists($h->{$key})) |
|
271
|
|
|
|
|
|
|
{ |
|
272
|
0
|
0
|
|
|
|
|
if (defined($h->{$key})) |
|
273
|
|
|
|
|
|
|
{ |
|
274
|
0
|
0
|
|
|
|
|
die "key $key to flatten is not a hash" if (ref($h->{$key}) ne 'HASH'); |
|
275
|
0
|
|
|
|
|
|
for my $subkey (keys %{$h->{$key}}) |
|
|
0
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
{ |
|
277
|
0
|
0
|
|
|
|
|
die "subkey exists $subkey" if (exists($h->{$subkey})); |
|
278
|
0
|
|
|
|
|
|
$h->{$subkey}=$h->{$key}->{$subkey}; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
} |
|
281
|
0
|
|
|
|
|
|
delete $h->{$key}; |
|
282
|
|
|
|
|
|
|
} |
|
283
|
|
|
|
|
|
|
} |
|
284
|
|
|
|
|
|
|
} |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
sub transactions |
|
287
|
|
|
|
|
|
|
{ |
|
288
|
|
|
|
|
|
|
|
|
289
|
0
|
|
|
0
|
1
|
|
my ($self) = shift; |
|
290
|
0
|
|
|
|
|
|
my ($pageNumber)=shift; |
|
291
|
0
|
|
|
|
|
|
my ($from) =shift; |
|
292
|
0
|
|
|
|
|
|
my ($to)=shift; |
|
293
|
|
|
|
|
|
|
|
|
294
|
0
|
|
|
|
|
|
my $pageSize=50; |
|
295
|
|
|
|
|
|
|
|
|
296
|
0
|
|
0
|
|
|
|
$from//=''; |
|
297
|
0
|
|
0
|
|
|
|
$to//=''; |
|
298
|
|
|
|
|
|
|
|
|
299
|
0
|
0
|
|
|
|
|
if (ref($to) eq 'Time::Piece') |
|
300
|
|
|
|
|
|
|
{ |
|
301
|
0
|
|
|
|
|
|
$to=$to->strftime("%Y-%m-%dT%H:%M:%S"); |
|
302
|
|
|
|
|
|
|
} |
|
303
|
0
|
0
|
|
|
|
|
if (ref($from) eq 'Time::Piece') |
|
304
|
|
|
|
|
|
|
{ |
|
305
|
0
|
|
|
|
|
|
$from=$from->strftime("%Y-%m-%dT%H:%M:%S"); |
|
306
|
|
|
|
|
|
|
} |
|
307
|
0
|
0
|
|
|
|
|
$to=~m/^[-0-9T:]*$/ or die "Invalid date format for 'to' $to, is a ".ref(\$to); |
|
308
|
0
|
0
|
|
|
|
|
$from=~m/^[-0-9T:]*$/ or die "Invalid date format for 'from' $from"; |
|
309
|
|
|
|
|
|
|
|
|
310
|
0
|
|
|
|
|
|
my $headers = { |
|
311
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
312
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
313
|
|
|
|
|
|
|
VERSION => 2, |
|
314
|
|
|
|
|
|
|
CST=>$self->CST, |
|
315
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
316
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
317
|
|
|
|
|
|
|
}; |
|
318
|
|
|
|
|
|
|
#my $jheaders = encode_json($headers); |
|
319
|
0
|
|
|
|
|
|
my $jheaders=JSON->new->canonical->encode($headers); |
|
320
|
|
|
|
|
|
|
|
|
321
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
322
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
323
|
|
|
|
|
|
|
|
|
324
|
0
|
0
|
|
|
|
|
$from and $from="from=$from"; |
|
325
|
0
|
0
|
|
|
|
|
$to and $to="to=$to"; |
|
326
|
0
|
|
|
|
|
|
my $rpage=$pageNumber; # requested page number as integer, 1 is first |
|
327
|
0
|
0
|
|
|
|
|
$pageNumber and $pageNumber="pageNumber=$pageNumber"; |
|
328
|
0
|
0
|
|
|
|
|
$pageSize and $pageSize="pageSize=$pageSize"; |
|
329
|
|
|
|
|
|
|
|
|
330
|
0
|
0
|
|
|
|
|
my $url=join '', map { $_?'&'.$_:'' } ($from,$to,$pageNumber,$pageSize); |
|
|
0
|
|
|
|
|
|
|
|
331
|
0
|
|
|
|
|
|
$url=~s/^&//; |
|
332
|
0
|
0
|
|
|
|
|
$url='?'.$url if ($url); |
|
333
|
|
|
|
|
|
|
|
|
334
|
0
|
|
|
|
|
|
$url='/history/transactions'.$url; |
|
335
|
0
|
|
|
|
|
|
$client->GET ( |
|
336
|
|
|
|
|
|
|
$url, |
|
337
|
|
|
|
|
|
|
$headers |
|
338
|
|
|
|
|
|
|
); |
|
339
|
|
|
|
|
|
|
|
|
340
|
0
|
|
|
|
|
|
my $code=$client->responseCode(); |
|
341
|
0
|
0
|
|
|
|
|
if ($code==200) |
|
342
|
|
|
|
|
|
|
{ |
|
343
|
0
|
|
|
|
|
|
my $resp=decode_json($client->responseContent()); |
|
344
|
|
|
|
|
|
|
# $resp=$self->flatten($resp,[qw/transactions metadata/]); |
|
345
|
|
|
|
|
|
|
#die encode_json($resp); |
|
346
|
|
|
|
|
|
|
|
|
347
|
0
|
|
|
|
|
|
my @activities=@{$resp->{transactions}}; |
|
|
0
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# pncerint encode_json( $resp->{metadata} ); |
|
349
|
|
|
|
|
|
|
# {"pageData":{"totalPages":11,"pageNumber":11,"pageSize":50},"size":534}***** 34 |
|
350
|
0
|
0
|
|
|
|
|
return undef if ($rpage > $resp->{metadata}->{pageData}->{pageNumber}); |
|
351
|
|
|
|
|
|
|
# return undef if (@activities==0); |
|
352
|
0
|
|
|
|
|
|
return \@activities; |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
else |
|
355
|
|
|
|
|
|
|
{ |
|
356
|
0
|
|
|
|
|
|
print "failed $code: ".$client->responseContent()."\n"; |
|
357
|
0
|
|
|
|
|
|
return undef; |
|
358
|
|
|
|
|
|
|
} |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
# example from/ to sting format: |
|
362
|
|
|
|
|
|
|
# 2020-10-28 |
|
363
|
|
|
|
|
|
|
# 2020-10-28T15:30 |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# keys in retirn, when called with detailed=1 |
|
366
|
|
|
|
|
|
|
# type, goodTillDate, actions(ARRAY) , epic, direction, level, channel, marketName, date, dealReference, guaranteedStop, stopLevel, size, currency, stopDistance, trailingStep, status, trailingStopDistance, limitLevel, description, dealId, period, limitDistance |
|
367
|
|
|
|
|
|
|
# without: |
|
368
|
|
|
|
|
|
|
# period, details, date, dealId, epic, description, channel, status, type |
|
369
|
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub history |
|
371
|
|
|
|
|
|
|
{ |
|
372
|
0
|
|
|
0
|
1
|
|
my ($self) = shift; |
|
373
|
0
|
|
|
|
|
|
my ($detailed)=shift; ## undef, not detailed, 1 for detailed. |
|
374
|
0
|
|
|
|
|
|
my ($pageNumber)=shift; |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
|
|
377
|
0
|
|
|
|
|
|
my ($from) = shift; |
|
378
|
0
|
|
|
|
|
|
my ($to) = shift; |
|
379
|
|
|
|
|
|
|
|
|
380
|
0
|
|
0
|
|
|
|
$pageNumber//=''; |
|
381
|
0
|
|
|
|
|
|
my $pageSize=50; |
|
382
|
|
|
|
|
|
|
|
|
383
|
0
|
|
0
|
|
|
|
$from//=''; |
|
384
|
0
|
|
0
|
|
|
|
$to//=''; |
|
385
|
|
|
|
|
|
|
|
|
386
|
0
|
0
|
|
|
|
|
if (ref($to) eq 'Time::Piece') |
|
387
|
|
|
|
|
|
|
{ |
|
388
|
0
|
|
|
|
|
|
$to=$to->strftime("%Y-%m-%dT%H:%M:%S"); |
|
389
|
|
|
|
|
|
|
} |
|
390
|
0
|
0
|
|
|
|
|
if (ref($from) eq 'Time::Piece') |
|
391
|
|
|
|
|
|
|
{ |
|
392
|
0
|
|
|
|
|
|
$from=$from->strftime("%Y-%m-%dT%H:%M:%S"); |
|
393
|
|
|
|
|
|
|
} |
|
394
|
|
|
|
|
|
|
|
|
395
|
0
|
0
|
|
|
|
|
$to=~m/^[-0-9T:]*$/ or die "Invalid date format for 'to' $to"; |
|
396
|
0
|
0
|
|
|
|
|
$from=~m/^[-0-9T:]*$/ or die "Invalid date format for 'from' $from"; |
|
397
|
|
|
|
|
|
|
|
|
398
|
0
|
|
|
|
|
|
my $headers = { |
|
399
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
400
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
401
|
|
|
|
|
|
|
VERSION => 2, |
|
402
|
|
|
|
|
|
|
CST=>$self->CST, |
|
403
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
404
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
405
|
|
|
|
|
|
|
}; |
|
406
|
|
|
|
|
|
|
#my $jheaders = encode_json($headers); |
|
407
|
0
|
|
|
|
|
|
my $jheaders=JSON->new->canonical->encode($headers); |
|
408
|
|
|
|
|
|
|
|
|
409
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
410
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
411
|
0
|
0
|
|
|
|
|
$from="from=$from" if ($from ne ''); |
|
412
|
0
|
0
|
|
|
|
|
$to="to=$to" if ($to ne ''); |
|
413
|
0
|
0
|
|
|
|
|
if ($detailed) |
|
414
|
|
|
|
|
|
|
{ |
|
415
|
0
|
|
|
|
|
|
$detailed="detailed=true" |
|
416
|
|
|
|
|
|
|
} |
|
417
|
|
|
|
|
|
|
else |
|
418
|
|
|
|
|
|
|
{ |
|
419
|
0
|
|
|
|
|
|
$detailed=''; |
|
420
|
|
|
|
|
|
|
} |
|
421
|
|
|
|
|
|
|
|
|
422
|
0
|
0
|
|
|
|
|
$pageNumber="pageNumber=$pageNumber" if ($pageNumber); |
|
423
|
0
|
|
0
|
|
|
|
$pageSize//=''; |
|
424
|
0
|
0
|
|
|
|
|
$pageSize="pageSize=$pageSize" if ($pageSize); |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
# my $sep='?'; |
|
427
|
|
|
|
|
|
|
# map { $_ eq '' or $_=$sep.$_ and $sep='&'} ($from,$to,$detailed,$pageNumber,$pageSize); |
|
428
|
|
|
|
|
|
|
|
|
429
|
0
|
0
|
|
|
|
|
my $url=join '', map { $_?'&'.$_:'' } ($from,$to,$detailed,$pageNumber,$pageSize); |
|
|
0
|
|
|
|
|
|
|
|
430
|
0
|
|
|
|
|
|
$url=~s/^&//; |
|
431
|
0
|
0
|
|
|
|
|
$url='?'.$url if ($url); |
|
432
|
|
|
|
|
|
|
|
|
433
|
0
|
|
|
|
|
|
$url='/history/activity'.$url; |
|
434
|
|
|
|
|
|
|
|
|
435
|
|
|
|
|
|
|
# die $url; |
|
436
|
|
|
|
|
|
|
|
|
437
|
0
|
|
|
|
|
|
$client->GET ( |
|
438
|
|
|
|
|
|
|
$url, |
|
439
|
|
|
|
|
|
|
$headers |
|
440
|
|
|
|
|
|
|
); |
|
441
|
|
|
|
|
|
|
|
|
442
|
0
|
|
|
|
|
|
my $code=$client->responseCode(); |
|
443
|
|
|
|
|
|
|
|
|
444
|
0
|
0
|
|
|
|
|
if ($code==200) |
|
445
|
|
|
|
|
|
|
{ |
|
446
|
0
|
|
|
|
|
|
my $resp=decode_json($client->responseContent()); |
|
447
|
0
|
|
|
|
|
|
my @activities=@{$resp->{activities}}; |
|
|
0
|
|
|
|
|
|
|
|
448
|
0
|
0
|
|
|
|
|
return undef if (@activities==0); |
|
449
|
0
|
|
|
|
|
|
$self->flatten(\@activities,'details'); |
|
450
|
0
|
|
|
|
|
|
return \@activities; |
|
451
|
|
|
|
|
|
|
} |
|
452
|
|
|
|
|
|
|
else |
|
453
|
|
|
|
|
|
|
{ |
|
454
|
0
|
|
|
|
|
|
print "failed $code: ".$client->responseContent()."\n"; |
|
455
|
0
|
|
|
|
|
|
return undef; |
|
456
|
|
|
|
|
|
|
} |
|
457
|
|
|
|
|
|
|
} |
|
458
|
|
|
|
|
|
|
# example response: |
|
459
|
|
|
|
|
|
|
#{"metadata":{"paging":{"size":50,"next":"/history/activity?version=3&from=2020-10-28T00:00:00&to=2020-10-29T16:41:45&detailed=false&pageSize=50"}} |
|
460
|
|
|
|
|
|
|
# "activities": [.... ] |
|
461
|
|
|
|
|
|
|
# } |
|
462
|
|
|
|
|
|
|
# each activity looks like: |
|
463
|
|
|
|
|
|
|
#{ |
|
464
|
|
|
|
|
|
|
# details=>null, |
|
465
|
|
|
|
|
|
|
# dealId=>"DIAAAAFRS39HJAK", |
|
466
|
|
|
|
|
|
|
# period=>"DFB", |
|
467
|
|
|
|
|
|
|
# type=>"POSITION", |
|
468
|
|
|
|
|
|
|
# epic=>"UA.D.ATVI.DAILY.IP", |
|
469
|
|
|
|
|
|
|
# description=>"Position partially closed=> J6GK8WA9", |
|
470
|
|
|
|
|
|
|
# date=>"2020-10-29T17:47:46", |
|
471
|
|
|
|
|
|
|
# status=>"ACCEPTED", |
|
472
|
|
|
|
|
|
|
# channel=>"SYSTEM" |
|
473
|
|
|
|
|
|
|
# |
|
474
|
|
|
|
|
|
|
# or with detail |
|
475
|
|
|
|
|
|
|
# {"activities": |
|
476
|
|
|
|
|
|
|
# [ |
|
477
|
|
|
|
|
|
|
# [ |
|
478
|
|
|
|
|
|
|
# {"date":"2020-11-19T18:41:04", |
|
479
|
|
|
|
|
|
|
# "epic":"UC.D.MU.DAILY.IP", |
|
480
|
|
|
|
|
|
|
# "period":"DFB", |
|
481
|
|
|
|
|
|
|
# "dealId":"DIAAAAFVXZV5LA5", |
|
482
|
|
|
|
|
|
|
# "channel":"WEB", |
|
483
|
|
|
|
|
|
|
# "type":"POSITION", |
|
484
|
|
|
|
|
|
|
# "status":"ACCEPTED", |
|
485
|
|
|
|
|
|
|
# "description":"Position opened: VXZV5LA5", |
|
486
|
|
|
|
|
|
|
# "details": |
|
487
|
|
|
|
|
|
|
# { |
|
488
|
|
|
|
|
|
|
# "dealReference":"6XQESB1EQGWY4FR2", |
|
489
|
|
|
|
|
|
|
# "actions": |
|
490
|
|
|
|
|
|
|
# [ |
|
491
|
|
|
|
|
|
|
# {"actionType":"POSITION_OPENED", |
|
492
|
|
|
|
|
|
|
# "affectedDealId":"DIAAAAFVXZV5LA5" |
|
493
|
|
|
|
|
|
|
# } |
|
494
|
|
|
|
|
|
|
# ], |
|
495
|
|
|
|
|
|
|
# "marketName":"Micron Technology Inc (All Sessions)", |
|
496
|
|
|
|
|
|
|
# "goodTillDate":null, |
|
497
|
|
|
|
|
|
|
# "currency":"GBP", |
|
498
|
|
|
|
|
|
|
# "size":0.4, |
|
499
|
|
|
|
|
|
|
# "direction":"BUY", |
|
500
|
|
|
|
|
|
|
# "level":6123, |
|
501
|
|
|
|
|
|
|
# "stopLevel":null, |
|
502
|
|
|
|
|
|
|
# "stopDistance":null, |
|
503
|
|
|
|
|
|
|
# "guaranteedStop":false, |
|
504
|
|
|
|
|
|
|
# "trailingStopDistance":null, |
|
505
|
|
|
|
|
|
|
# "trailingStep":null, |
|
506
|
|
|
|
|
|
|
# "limitLevel":null, |
|
507
|
|
|
|
|
|
|
# "limitDistance":null |
|
508
|
|
|
|
|
|
|
# } |
|
509
|
|
|
|
|
|
|
# }, |
|
510
|
|
|
|
|
|
|
# {"date":"2020-11-17T11:33:52", |
|
511
|
|
|
|
|
|
|
#"epic":"KA.D.FSTA.DAILY.IP", |
|
512
|
|
|
|
|
|
|
#"period":"DFB", |
|
513
|
|
|
|
|
|
|
#"dealId":"DIAAAAFVEFD4GAG", |
|
514
|
|
|
|
|
|
|
#"channel":"WEB", |
|
515
|
|
|
|
|
|
|
#"type":"POSITION", |
|
516
|
|
|
|
|
|
|
#"status":"ACCEPTED", |
|
517
|
|
|
|
|
|
|
#"description":"Position/s closed: HH93GXAZ", |
|
518
|
|
|
|
|
|
|
#"details":{"dealReference":"6XQESB1EQAZNR6V3", |
|
519
|
|
|
|
|
|
|
#"actions":[{"actionType":"POSITION_CLOSED", |
|
520
|
|
|
|
|
|
|
#"affectedDealId":"DIAAAAFHH93GXAZ"}], |
|
521
|
|
|
|
|
|
|
#"marketName":"Fuller Smith & Turner", |
|
522
|
|
|
|
|
|
|
#"goodTillDate":null, |
|
523
|
|
|
|
|
|
|
#"currency":"GBP", |
|
524
|
|
|
|
|
|
|
#"size":1, |
|
525
|
|
|
|
|
|
|
#"direction":"SELL", |
|
526
|
|
|
|
|
|
|
#"level":726.2, |
|
527
|
|
|
|
|
|
|
#"stopLevel":null, |
|
528
|
|
|
|
|
|
|
#"stopDistance":null, |
|
529
|
|
|
|
|
|
|
#"guaranteedStop":false, |
|
530
|
|
|
|
|
|
|
#"trailingStopDistance":null, |
|
531
|
|
|
|
|
|
|
#"trailingStep":null, |
|
532
|
|
|
|
|
|
|
#"limitLevel":null, |
|
533
|
|
|
|
|
|
|
#"limitDistance":null}}, |
|
534
|
|
|
|
|
|
|
#} |
|
535
|
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
# with detailed=1 |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
#{ |
|
539
|
|
|
|
|
|
|
# "activities": [ |
|
540
|
|
|
|
|
|
|
# { |
|
541
|
|
|
|
|
|
|
# "date": "2020-11-19T18:41:04", |
|
542
|
|
|
|
|
|
|
# "epic": "UC.D.MU.DAILY.IP", |
|
543
|
|
|
|
|
|
|
# "period": "DFB", |
|
544
|
|
|
|
|
|
|
# "dealId": "DIAAAAFVXZV5LA5", |
|
545
|
|
|
|
|
|
|
# "channel": "WEB", |
|
546
|
|
|
|
|
|
|
# "type": "POSITION", |
|
547
|
|
|
|
|
|
|
# "status": "ACCEPTED", |
|
548
|
|
|
|
|
|
|
# "description": "Position opened: VXZV5LA5", |
|
549
|
|
|
|
|
|
|
# "details": { |
|
550
|
|
|
|
|
|
|
# "dealReference": "6XQESB1EQGWY4FR2", |
|
551
|
|
|
|
|
|
|
# "actions": [ |
|
552
|
|
|
|
|
|
|
# { |
|
553
|
|
|
|
|
|
|
# "actionType": "POSITION_OPENED", |
|
554
|
|
|
|
|
|
|
# "affectedDealId": "DIAAAAFVXZV5LA5" |
|
555
|
|
|
|
|
|
|
# } |
|
556
|
|
|
|
|
|
|
# ], |
|
557
|
|
|
|
|
|
|
# "marketName": "Micron Technology Inc (All Sessions)", |
|
558
|
|
|
|
|
|
|
# "goodTillDate": null, |
|
559
|
|
|
|
|
|
|
# "currency": "GBP", |
|
560
|
|
|
|
|
|
|
# "size": 0.4, |
|
561
|
|
|
|
|
|
|
# "direction": "BUY", |
|
562
|
|
|
|
|
|
|
# "level": 6123, |
|
563
|
|
|
|
|
|
|
# "stopLevel": null, |
|
564
|
|
|
|
|
|
|
# "stopDistance": null, |
|
565
|
|
|
|
|
|
|
# "guaranteedStop": false, |
|
566
|
|
|
|
|
|
|
# "trailingStopDistance": null, |
|
567
|
|
|
|
|
|
|
# "trailingStep": null, |
|
568
|
|
|
|
|
|
|
# "limitLevel": null, |
|
569
|
|
|
|
|
|
|
# "limitDistance": null |
|
570
|
|
|
|
|
|
|
# } |
|
571
|
|
|
|
|
|
|
# }, |
|
572
|
|
|
|
|
|
|
# { |
|
573
|
|
|
|
|
|
|
# "date": "2020-11-17T11:33:52", |
|
574
|
|
|
|
|
|
|
# "epic": "KA.D.FSTA.DAILY.IP", |
|
575
|
|
|
|
|
|
|
# "period": "DFB", |
|
576
|
|
|
|
|
|
|
# "dealId": "DIAAAAFVEFD4GAG", |
|
577
|
|
|
|
|
|
|
# "channel": "WEB", |
|
578
|
|
|
|
|
|
|
# "type": "POSITION", |
|
579
|
|
|
|
|
|
|
# "status": "ACCEPTED", |
|
580
|
|
|
|
|
|
|
# "description": "Position/s closed: HH93GXAZ", |
|
581
|
|
|
|
|
|
|
# "details": { |
|
582
|
|
|
|
|
|
|
# "dealReference": "6XQESB1EQAZNR6V3", |
|
583
|
|
|
|
|
|
|
# "actions": [ |
|
584
|
|
|
|
|
|
|
# { |
|
585
|
|
|
|
|
|
|
# "actionType": "POSITION_CLOSED", |
|
586
|
|
|
|
|
|
|
# "affectedDealId": "DIAAAAFHH93GXAZ" |
|
587
|
|
|
|
|
|
|
# } |
|
588
|
|
|
|
|
|
|
# ], |
|
589
|
|
|
|
|
|
|
# "marketName": "Fuller Smith & Turner", |
|
590
|
|
|
|
|
|
|
# "goodTillDate": null, |
|
591
|
|
|
|
|
|
|
# "currency": "GBP", |
|
592
|
|
|
|
|
|
|
# "size": 1, |
|
593
|
|
|
|
|
|
|
# "direction": "SELL", |
|
594
|
|
|
|
|
|
|
# "level": 726.2, |
|
595
|
|
|
|
|
|
|
# "stopLevel": null, |
|
596
|
|
|
|
|
|
|
# "stopDistance": null, |
|
597
|
|
|
|
|
|
|
# "guaranteedStop": false, |
|
598
|
|
|
|
|
|
|
# "trailingStopDistance": null, |
|
599
|
|
|
|
|
|
|
# "trailingStep": null, |
|
600
|
|
|
|
|
|
|
# "limitLevel": null, |
|
601
|
|
|
|
|
|
|
# "limitDistance": null |
|
602
|
|
|
|
|
|
|
# } |
|
603
|
|
|
|
|
|
|
# }, |
|
604
|
|
|
|
|
|
|
# { |
|
605
|
|
|
|
|
|
|
# "date": "2020-11-17T11:33:09", |
|
606
|
|
|
|
|
|
|
# "epic": "KA.D.FSTA.DAILY.IP", |
|
607
|
|
|
|
|
|
|
# "period": "DFB", |
|
608
|
|
|
|
|
|
|
# "dealId": "DIAAAAFVEFBBKA4", |
|
609
|
|
|
|
|
|
|
# "channel": "WEB", |
|
610
|
|
|
|
|
|
|
# "type": "POSITION", |
|
611
|
|
|
|
|
|
|
# "status": "ACCEPTED", |
|
612
|
|
|
|
|
|
|
# "description": "Position opened: VEFBBKA4", |
|
613
|
|
|
|
|
|
|
# "details": { |
|
614
|
|
|
|
|
|
|
# "dealReference": "6XQESB1EQAZKR1V2", |
|
615
|
|
|
|
|
|
|
# "actions": [ |
|
616
|
|
|
|
|
|
|
# { |
|
617
|
|
|
|
|
|
|
# "actionType": "POSITION_OPENED", |
|
618
|
|
|
|
|
|
|
# "affectedDealId": "DIAAAAFVEFBBKA4" |
|
619
|
|
|
|
|
|
|
# } |
|
620
|
|
|
|
|
|
|
# ], |
|
621
|
|
|
|
|
|
|
# "marketName": "Fuller Smith & Turner", |
|
622
|
|
|
|
|
|
|
# "goodTillDate": null, |
|
623
|
|
|
|
|
|
|
# "currency": "GBP", |
|
624
|
|
|
|
|
|
|
# "size": 2, |
|
625
|
|
|
|
|
|
|
# "direction": "BUY", |
|
626
|
|
|
|
|
|
|
# "level": 779.9, |
|
627
|
|
|
|
|
|
|
# "stopLevel": null, |
|
628
|
|
|
|
|
|
|
# "stopDistance": null, |
|
629
|
|
|
|
|
|
|
# "guaranteedStop": false, |
|
630
|
|
|
|
|
|
|
# "trailingStopDistance": null, |
|
631
|
|
|
|
|
|
|
# "trailingStep": null, |
|
632
|
|
|
|
|
|
|
# "limitLevel": null, |
|
633
|
|
|
|
|
|
|
# "limitDistance": null |
|
634
|
|
|
|
|
|
|
# } |
|
635
|
|
|
|
|
|
|
# }, |
|
636
|
|
|
|
|
|
|
# { |
|
637
|
|
|
|
|
|
|
# "date": "2020-11-16T17:17:29", |
|
638
|
|
|
|
|
|
|
# "epic": "UD.D.WIXUS.DAILY.IP", |
|
639
|
|
|
|
|
|
|
# "period": "DFB", |
|
640
|
|
|
|
|
|
|
# "dealId": "DIAAAAFU94TQRAR", |
|
641
|
|
|
|
|
|
|
# "channel": "WEB", |
|
642
|
|
|
|
|
|
|
# "type": "POSITION", |
|
643
|
|
|
|
|
|
|
# "status": "ACCEPTED", |
|
644
|
|
|
|
|
|
|
# "description": "Position opened: U94TQRAR", |
|
645
|
|
|
|
|
|
|
# "details": { |
|
646
|
|
|
|
|
|
|
# "dealReference": "6XQESB1EQ90XNSR2", |
|
647
|
|
|
|
|
|
|
# "actions": [ |
|
648
|
|
|
|
|
|
|
# { |
|
649
|
|
|
|
|
|
|
# "actionType": "POSITION_OPENED", |
|
650
|
|
|
|
|
|
|
# "affectedDealId": "DIAAAAFU94TQRAR" |
|
651
|
|
|
|
|
|
|
# } |
|
652
|
|
|
|
|
|
|
# ], |
|
653
|
|
|
|
|
|
|
# "marketName": "Wix.com Ltd", |
|
654
|
|
|
|
|
|
|
# "goodTillDate": null, |
|
655
|
|
|
|
|
|
|
# "currency": "GBP", |
|
656
|
|
|
|
|
|
|
# "size": 0.31, |
|
657
|
|
|
|
|
|
|
# "direction": "BUY", |
|
658
|
|
|
|
|
|
|
# "level": 24142, |
|
659
|
|
|
|
|
|
|
# "stopLevel": null, |
|
660
|
|
|
|
|
|
|
# "stopDistance": null, |
|
661
|
|
|
|
|
|
|
# "guaranteedStop": false, |
|
662
|
|
|
|
|
|
|
# "trailingStopDistance": null, |
|
663
|
|
|
|
|
|
|
# "trailingStep": null, |
|
664
|
|
|
|
|
|
|
# "limitLevel": null, |
|
665
|
|
|
|
|
|
|
# "limitDistance": null |
|
666
|
|
|
|
|
|
|
# } |
|
667
|
|
|
|
|
|
|
# }, |
|
668
|
|
|
|
|
|
|
# { |
|
669
|
|
|
|
|
|
|
# "date": "2020-11-16T17:08:33", |
|
670
|
|
|
|
|
|
|
# "epic": "UD.D.ZMUS.DAILY.IP", |
|
671
|
|
|
|
|
|
|
# "period": "DFB", |
|
672
|
|
|
|
|
|
|
# "dealId": "DIAAAAFU924B7A3", |
|
673
|
|
|
|
|
|
|
# etc.... |
|
674
|
|
|
|
|
|
|
########################################################################## |
|
675
|
|
|
|
|
|
|
# |
|
676
|
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
=head2 accounts - retrieve a list of accounts |
|
678
|
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
Parameters - none |
|
680
|
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
Return value - Array ref containing hashes of accounts. |
|
682
|
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=cut |
|
684
|
|
|
|
|
|
|
########################################################################## |
|
685
|
|
|
|
|
|
|
sub accounts |
|
686
|
|
|
|
|
|
|
{ |
|
687
|
0
|
|
|
0
|
1
|
|
my ($self) = shift; |
|
688
|
|
|
|
|
|
|
|
|
689
|
0
|
|
|
|
|
|
my $headers = { |
|
690
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
691
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
692
|
|
|
|
|
|
|
VERSION => 1, |
|
693
|
|
|
|
|
|
|
CST=>$self->CST, |
|
694
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
695
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
696
|
|
|
|
|
|
|
}; |
|
697
|
|
|
|
|
|
|
#my $jheaders = encode_json($headers); |
|
698
|
0
|
|
|
|
|
|
my $jheaders=JSON->new->canonical->encode($headers); |
|
699
|
|
|
|
|
|
|
|
|
700
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
701
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
702
|
0
|
|
|
|
|
|
my $r=$client->GET ( '/accounts', $headers); |
|
703
|
|
|
|
|
|
|
|
|
704
|
0
|
|
|
|
|
|
my $resp=decode_json($client->responseContent()); |
|
705
|
|
|
|
|
|
|
|
|
706
|
0
|
|
|
|
|
|
my $accounts=[]; |
|
707
|
0
|
|
|
|
|
|
@$accounts=@{$resp->{accounts}}; |
|
|
0
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
|
|
709
|
0
|
|
|
|
|
|
return $accounts; |
|
710
|
|
|
|
|
|
|
|
|
711
|
|
|
|
|
|
|
} |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
# Typical return data: |
|
714
|
|
|
|
|
|
|
#[ |
|
715
|
|
|
|
|
|
|
# {"accountId":"...", |
|
716
|
|
|
|
|
|
|
# "status":"ENABLED", |
|
717
|
|
|
|
|
|
|
# "canTransferFrom":true, |
|
718
|
|
|
|
|
|
|
# "preferred":true, |
|
719
|
|
|
|
|
|
|
# "accountAlias":null, |
|
720
|
|
|
|
|
|
|
# "accountType":"SPREADBET", |
|
721
|
|
|
|
|
|
|
# "accountName":"Spread bet", |
|
722
|
|
|
|
|
|
|
# "balance":{ |
|
723
|
|
|
|
|
|
|
# "deposit":89051.36, |
|
724
|
|
|
|
|
|
|
# "balance":152475.8, |
|
725
|
|
|
|
|
|
|
# "available":85942.65, |
|
726
|
|
|
|
|
|
|
# "profitLoss":22518.21 |
|
727
|
|
|
|
|
|
|
# }, |
|
728
|
|
|
|
|
|
|
# "canTransferTo":true, |
|
729
|
|
|
|
|
|
|
# "currency":"GBP" |
|
730
|
|
|
|
|
|
|
# }, |
|
731
|
|
|
|
|
|
|
# {"accountId":"...", |
|
732
|
|
|
|
|
|
|
# "status":"ENABLED", |
|
733
|
|
|
|
|
|
|
# "canTransferFrom":true, |
|
734
|
|
|
|
|
|
|
# "preferred":false, |
|
735
|
|
|
|
|
|
|
# "accountAlias":null, |
|
736
|
|
|
|
|
|
|
# "accountType":"CFD", |
|
737
|
|
|
|
|
|
|
# "accountName":"CFD", |
|
738
|
|
|
|
|
|
|
# "balance":{ |
|
739
|
|
|
|
|
|
|
# "available":0, |
|
740
|
|
|
|
|
|
|
# "profitLoss":0, |
|
741
|
|
|
|
|
|
|
# "balance":0, |
|
742
|
|
|
|
|
|
|
# "deposit":0 |
|
743
|
|
|
|
|
|
|
# }, |
|
744
|
|
|
|
|
|
|
# "canTransferTo":true, |
|
745
|
|
|
|
|
|
|
# "currency":"GBP" |
|
746
|
|
|
|
|
|
|
# } |
|
747
|
|
|
|
|
|
|
#] |
|
748
|
|
|
|
|
|
|
########################################################################## |
|
749
|
|
|
|
|
|
|
# |
|
750
|
|
|
|
|
|
|
# Return a ref to an array of positions. Each position is |
|
751
|
|
|
|
|
|
|
# a variable structure deep hash |
|
752
|
|
|
|
|
|
|
# |
|
753
|
|
|
|
|
|
|
########################################################################## |
|
754
|
|
|
|
|
|
|
sub positions |
|
755
|
|
|
|
|
|
|
{ |
|
756
|
0
|
|
|
0
|
1
|
|
my ($self) = shift; |
|
757
|
|
|
|
|
|
|
|
|
758
|
0
|
|
|
|
|
|
my $headers = { |
|
759
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
760
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
761
|
|
|
|
|
|
|
VERSION => 2, |
|
762
|
|
|
|
|
|
|
# 'IG-ACCOUNT-ID'=> $accountid, |
|
763
|
|
|
|
|
|
|
CST=>$self->CST, |
|
764
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
765
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
766
|
|
|
|
|
|
|
}; |
|
767
|
|
|
|
|
|
|
#my $jheaders=JSON->new->canonical->encode($headers); # for debug |
|
768
|
|
|
|
|
|
|
|
|
769
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
770
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
771
|
|
|
|
|
|
|
#my $r; |
|
772
|
|
|
|
|
|
|
# $headers->{VERSION}=2; |
|
773
|
|
|
|
|
|
|
#$r=$client->GET ( |
|
774
|
0
|
|
|
|
|
|
$client->GET ( '/positions', |
|
775
|
|
|
|
|
|
|
$headers |
|
776
|
|
|
|
|
|
|
); |
|
777
|
0
|
|
|
|
|
|
my $resp=decode_json($client->responseContent()); |
|
778
|
|
|
|
|
|
|
|
|
779
|
0
|
|
|
|
|
|
my $positions=[]; |
|
780
|
0
|
|
|
|
|
|
@$positions=@{$resp->{positions}}; |
|
|
0
|
|
|
|
|
|
|
|
781
|
|
|
|
|
|
|
|
|
782
|
0
|
|
|
|
|
|
return $positions; |
|
783
|
|
|
|
|
|
|
} |
|
784
|
|
|
|
|
|
|
# example of the structure of a position |
|
785
|
|
|
|
|
|
|
# Regeneron Pharmaceuticals Inc, 0.06 |
|
786
|
|
|
|
|
|
|
# { |
|
787
|
|
|
|
|
|
|
# "position" : { |
|
788
|
|
|
|
|
|
|
# "trailingStopDistance" : null, |
|
789
|
|
|
|
|
|
|
# "size" : 0.06, |
|
790
|
|
|
|
|
|
|
# "limitedRiskPremium" : null, |
|
791
|
|
|
|
|
|
|
# "stopLevel" : 50128, |
|
792
|
|
|
|
|
|
|
# "direction" : "BUY", |
|
793
|
|
|
|
|
|
|
# "level" : 50303, |
|
794
|
|
|
|
|
|
|
# "dealReference" : "6XQESB1E506WW334", |
|
795
|
|
|
|
|
|
|
# "controlledRisk" : false, |
|
796
|
|
|
|
|
|
|
# "currency" : "GBP", |
|
797
|
|
|
|
|
|
|
# "contractSize" : 1, |
|
798
|
|
|
|
|
|
|
# "createdDateUTC" : "2020-04-03T14:26:07", |
|
799
|
|
|
|
|
|
|
# "trailingStep" : null, |
|
800
|
|
|
|
|
|
|
# "createdDate" : "2020/04/03 15:26:07:000", |
|
801
|
|
|
|
|
|
|
# "limitLevel" : null, |
|
802
|
|
|
|
|
|
|
# "dealId" : "DIAAAAEL2T7AEAS" |
|
803
|
|
|
|
|
|
|
# }, |
|
804
|
|
|
|
|
|
|
# "market" : { |
|
805
|
|
|
|
|
|
|
# "lotSize" : 1, |
|
806
|
|
|
|
|
|
|
# "marketStatus" : "EDITS_ONLY", |
|
807
|
|
|
|
|
|
|
# "instrumentType" : "SHARES", |
|
808
|
|
|
|
|
|
|
# "expiry" : "DFB", |
|
809
|
|
|
|
|
|
|
# "streamingPricesAvailable" : false, |
|
810
|
|
|
|
|
|
|
# "instrumentName" : "Regeneron Pharmaceuticals Inc", |
|
811
|
|
|
|
|
|
|
# "offer" : 60261, |
|
812
|
|
|
|
|
|
|
# "delayTime" : 0, |
|
813
|
|
|
|
|
|
|
# "updateTime" : "20:59:56", |
|
814
|
|
|
|
|
|
|
# "high" : 61455, |
|
815
|
|
|
|
|
|
|
# "percentageChange" : -2.01, |
|
816
|
|
|
|
|
|
|
# "netChange" : -1236, |
|
817
|
|
|
|
|
|
|
# "low" : 59886, |
|
818
|
|
|
|
|
|
|
# "bid" : 60261, |
|
819
|
|
|
|
|
|
|
# "updateTimeUTC" : "19:59:56", |
|
820
|
|
|
|
|
|
|
# "scalingFactor" : 1, |
|
821
|
|
|
|
|
|
|
# "epic" : "UC.D.REGN.DAILY.IP" |
|
822
|
|
|
|
|
|
|
# } |
|
823
|
|
|
|
|
|
|
# } |
|
824
|
|
|
|
|
|
|
##################################################################### |
|
825
|
|
|
|
|
|
|
# Aggregate an array of positions into an array of unique |
|
826
|
|
|
|
|
|
|
# positions with 1 element per instrument, Items will be combined |
|
827
|
|
|
|
|
|
|
# where more than one position is combined, in a field dependent way. |
|
828
|
|
|
|
|
|
|
# for exeample sizes will be added as will be profit |
|
829
|
|
|
|
|
|
|
# a reference to an array is expected and a reference to a new array |
|
830
|
|
|
|
|
|
|
# returned. |
|
831
|
|
|
|
|
|
|
##################################################################### |
|
832
|
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
=head2 agg - aggregate positions into a flattened 1 element per instrument form. |
|
834
|
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
Parameters |
|
836
|
|
|
|
|
|
|
|
|
837
|
|
|
|
|
|
|
1 Reference to an array of positions |
|
838
|
|
|
|
|
|
|
2 (Optional) Ref to an array of keys to sort on |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
agg does three things actually. First, it joins together multiple positions of the same instrument, |
|
841
|
|
|
|
|
|
|
generating sensible values for things like profit/loss and size |
|
842
|
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
Second, it performs some flattening of the deep structure for a position which comes from IG. |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
Third it sorts the result. The default sort order I use is -profitpc instrumentName, but |
|
846
|
|
|
|
|
|
|
you can provide a 2rd parameter, a reference to an array of items to sort by. |
|
847
|
|
|
|
|
|
|
Each item can optionally be preceeded by - to reverse the prder. If the first item equates equal, then |
|
848
|
|
|
|
|
|
|
the next item is used. |
|
849
|
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
=cut |
|
851
|
|
|
|
|
|
|
##################################################################### |
|
852
|
|
|
|
|
|
|
sub agg |
|
853
|
|
|
|
|
|
|
{ |
|
854
|
0
|
|
|
0
|
1
|
|
my ($self,$positions,$sortlist)=@_; |
|
855
|
0
|
|
|
|
|
|
my %totals; # aggregated totals as arrays of individuals. |
|
856
|
|
|
|
|
|
|
|
|
857
|
0
|
|
|
|
|
|
$self->flatten($positions, [qw/market position/]); |
|
858
|
0
|
|
|
|
|
|
for my $position (@$positions) |
|
859
|
|
|
|
|
|
|
{ |
|
860
|
|
|
|
|
|
|
|
|
861
|
0
|
|
|
|
|
|
my $json = JSON->new; |
|
862
|
0
|
0
|
|
|
|
|
$position->{size}=-abs($position->{size}) if ($position->{direction} eq 'SELL'); |
|
863
|
|
|
|
|
|
|
# $position->{size}= -abs($position->{size}) if ($position->{direction}//'' ne 'BUY'); |
|
864
|
0
|
|
|
|
|
|
$position->{profit}=($self->fetch($position,'bid')-$self->fetch($position,'level'))*$self->fetch($position,'size'); |
|
865
|
|
|
|
|
|
|
|
|
866
|
0
|
0
|
|
|
|
|
$position->{held}=Time::Piece->strptime($position->{createdDateUTC},"%Y-%m-%dT%H:%M:%S") or die "strptime failed for ".$position->{createdDateOnly}; |
|
867
|
0
|
|
|
|
|
|
$position->{held}=(gmtime()-$position->{held})/(24*3600); |
|
868
|
|
|
|
|
|
|
|
|
869
|
0
|
|
0
|
|
|
|
my $ra=($totals{$position->{instrumentName}}||=[]); |
|
870
|
0
|
|
|
|
|
|
push(@$ra,$position); |
|
871
|
|
|
|
|
|
|
|
|
872
|
|
|
|
|
|
|
} |
|
873
|
|
|
|
|
|
|
|
|
874
|
|
|
|
|
|
|
# totals is a hash on instrument name each element is a pointer to an array of positions for the same instrument. |
|
875
|
|
|
|
|
|
|
|
|
876
|
0
|
|
|
|
|
|
my $aggregated=[]; |
|
877
|
0
|
|
|
|
|
|
for my $total (values %totals) |
|
878
|
|
|
|
|
|
|
{ # for one particular name |
|
879
|
0
|
|
|
|
|
|
my $position={}; # initialise the new aggregate position |
|
880
|
|
|
|
|
|
|
|
|
881
|
0
|
|
|
|
|
|
$position->{profit}=0; |
|
882
|
0
|
|
|
|
|
|
$position->{size}=0; |
|
883
|
0
|
|
|
|
|
|
$position->{held}=0; |
|
884
|
0
|
|
|
|
|
|
$position->{stopLevel}=[]; |
|
885
|
0
|
|
|
|
|
|
$position->{createdDate}=[]; |
|
886
|
0
|
|
|
|
|
|
$position->{createdDateUTC}=[]; |
|
887
|
|
|
|
|
|
|
|
|
888
|
0
|
|
|
|
|
|
for my $subtotal ( @$total) # go through all the positions for that one name |
|
889
|
|
|
|
|
|
|
{ |
|
890
|
0
|
|
0
|
|
|
|
$position->{instrumentName}//=$subtotal->{instrumentName}; |
|
891
|
0
|
|
|
|
|
|
$position->{size}+=$subtotal->{size}; |
|
892
|
0
|
|
|
|
|
|
my $h; |
|
893
|
0
|
0
|
|
|
|
|
$h=Time::Piece->strptime($subtotal->{createdDateUTC},"%Y-%m-%dT%H:%M:%S") or die "strptime failed for ".$subtotal->{createdDateOnly}; |
|
894
|
0
|
|
|
|
|
|
$h=(gmtime()-$h)/(24*3600); |
|
895
|
0
|
|
|
|
|
|
$h=int($h*10)/10; |
|
896
|
0
|
|
|
|
|
|
$subtotal->{held}=$h; |
|
897
|
0
|
|
|
|
|
|
$position->{held}+=$subtotal->{held}*$subtotal->{size}; # this is a size-weighted average. Needs division by total size. |
|
898
|
0
|
|
0
|
|
|
|
$position->{bid}//=$subtotal->{bid}; |
|
899
|
0
|
|
|
|
|
|
$position->{profit}+=$subtotal->{profit} ; |
|
900
|
0
|
|
0
|
|
|
|
$position->{epic}//=$subtotal->{epic}; |
|
901
|
|
|
|
|
|
|
|
|
902
|
0
|
|
0
|
|
|
|
$position->{currency}//=$subtotal->{currency}; |
|
903
|
0
|
|
0
|
|
|
|
$position->{marketStatus}//=$subtotal->{marketStatus}; |
|
904
|
|
|
|
|
|
|
|
|
905
|
0
|
0
|
|
|
|
|
push(@{$position->{stopLevel}},$subtotal->{stopLevel}) if $subtotal->{stopLevel}; |
|
|
0
|
|
|
|
|
|
|
|
906
|
0
|
|
|
|
|
|
push(@{$position->{createdDate}},$subtotal->{createdDate}); |
|
|
0
|
|
|
|
|
|
|
|
907
|
0
|
|
|
|
|
|
push(@{$position->{createdDateUTC}},$subtotal->{createdDateUTC}); |
|
|
0
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
} |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
# now we have various housekeeping to do in some cases, eg where an average is calculated as a sum above, we divide by the number to get a true mean. |
|
911
|
|
|
|
|
|
|
########### |
|
912
|
|
|
|
|
|
|
|
|
913
|
0
|
0
|
|
|
|
|
$position->{held}=sprintf("%0.1f",$position->{held}/$position->{size}); $position->{held}.=" av" if (@$total>1); |
|
|
0
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
|
|
915
|
|
|
|
|
|
|
|
|
916
|
0
|
|
|
|
|
|
$position->{level}=$position->{bid}-$position->{profit}/$position->{size}; # open level for multiple positions |
|
917
|
|
|
|
|
|
|
|
|
918
|
0
|
0
|
|
|
|
|
$position->{profitpc}=int(0.5+1000*$position->{profit}/($position->{level}*abs($position->{size})))/10 if ($position->{level}>0); |
|
919
|
|
|
|
|
|
|
|
|
920
|
0
|
|
|
|
|
|
$position->{atrisk}=$position->{bid}*$position->{size}; |
|
921
|
|
|
|
|
|
|
|
|
922
|
0
|
|
|
|
|
|
$position->{createdDate}=$self->sortrange($position->{createdDate}); |
|
923
|
0
|
|
|
|
|
|
$position->{createdDateUTC}=$self->sortrange($position->{createdDateUTC}); |
|
924
|
0
|
|
|
|
|
|
$position->{createdDateOnly}=$position->{createdDate}; |
|
925
|
0
|
|
|
|
|
|
$position->{createdDateOnly}=~s/T[^-]+//g; |
|
926
|
|
|
|
|
|
|
|
|
927
|
0
|
0
|
|
|
|
|
$position->{slpc}=join(',',map { $_?(int(1000.0*$_/$position->{bid})/10):''} @{$position->{stopLevel}}); |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
928
|
0
|
|
|
|
|
|
$position->{stopLevel}=join(',',@{$position->{stopLevel}}); |
|
|
0
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
########### |
|
931
|
|
|
|
|
|
|
# end of aggregated operations |
|
932
|
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
|
|
934
|
0
|
|
|
|
|
|
push(@$aggregated,$position); |
|
935
|
|
|
|
|
|
|
} |
|
936
|
|
|
|
|
|
|
|
|
937
|
|
|
|
|
|
|
# @$aggregated=sort { $b->{profitpc}<=>$a->{profitpc} } @$aggregated; |
|
938
|
0
|
|
0
|
|
|
|
$sortlist//=[qw(-profitpc instrumentName)]; # default sort |
|
939
|
0
|
|
|
|
|
|
$self->sorter($sortlist,$aggregated); |
|
940
|
0
|
|
|
|
|
|
return $aggregated; |
|
941
|
|
|
|
|
|
|
|
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
# like agg, but do not do actual aggregation. |
|
944
|
|
|
|
|
|
|
# so we sort, add certain extra characteristics but thats all. |
|
945
|
|
|
|
|
|
|
########################################################################## |
|
946
|
|
|
|
|
|
|
# |
|
947
|
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
=head2 nonagg - like agg but do not do actual aggregation |
|
949
|
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
Parameters |
|
951
|
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
1 Reference to an array of positions |
|
953
|
|
|
|
|
|
|
2 (Optional) Ref to an array of keys to sort on |
|
954
|
|
|
|
|
|
|
|
|
955
|
|
|
|
|
|
|
Return value - Array ref containing hashes of accounts. Should be the same size as the original. |
|
956
|
|
|
|
|
|
|
|
|
957
|
|
|
|
|
|
|
=cut |
|
958
|
|
|
|
|
|
|
########################################################################## |
|
959
|
|
|
|
|
|
|
#sub nonagg |
|
960
|
|
|
|
|
|
|
#{ |
|
961
|
|
|
|
|
|
|
# my ($self,$positions,$sortlist)=@_; |
|
962
|
|
|
|
|
|
|
# my %totals; # aggregated totals as arrays of individuals. |
|
963
|
|
|
|
|
|
|
# |
|
964
|
|
|
|
|
|
|
# $self->flatten($positions, [qw/market position/]); |
|
965
|
|
|
|
|
|
|
# for my $position (@$positions) |
|
966
|
|
|
|
|
|
|
# { |
|
967
|
|
|
|
|
|
|
# |
|
968
|
|
|
|
|
|
|
# my $json = JSON->new; |
|
969
|
|
|
|
|
|
|
# |
|
970
|
|
|
|
|
|
|
# $position->{profit}=($self->fetch($position,'bid')-$self->fetch($position,'level'))*$self->fetch($position,'size'); |
|
971
|
|
|
|
|
|
|
# # create new profits element |
|
972
|
|
|
|
|
|
|
# |
|
973
|
|
|
|
|
|
|
# my $open=$position->{bid}-$position->{profit}/$position->{size}; |
|
974
|
|
|
|
|
|
|
# $position->{level}=$open; |
|
975
|
|
|
|
|
|
|
# $position->{profitpc}=int(0.5+1000*$position->{profit}/($position->{level}*$position->{size}))/10; |
|
976
|
|
|
|
|
|
|
# $position->{atrisk}=$position->{bid}*$position->{size}; |
|
977
|
|
|
|
|
|
|
# $position->{createdDateOnly}=$position->{createdDate}; |
|
978
|
|
|
|
|
|
|
# $position->{createdDateOnly}=~s/ .*$//; |
|
979
|
|
|
|
|
|
|
# } |
|
980
|
|
|
|
|
|
|
# |
|
981
|
|
|
|
|
|
|
# $sortlist//=[qw(-profitpc instrumentName)]; # default sort |
|
982
|
|
|
|
|
|
|
# $self->sorter($sortlist,$positions); |
|
983
|
|
|
|
|
|
|
# return $positions; |
|
984
|
|
|
|
|
|
|
#} |
|
985
|
|
|
|
|
|
|
sub nonagg |
|
986
|
|
|
|
|
|
|
{ |
|
987
|
0
|
|
|
0
|
1
|
|
my ($self,$positions,$sortlist)=@_; |
|
988
|
0
|
|
|
|
|
|
my %totals; # aggregated totals as arrays of individuals. |
|
989
|
|
|
|
|
|
|
|
|
990
|
0
|
|
|
|
|
|
$self->flatten($positions, [qw/market position/]); |
|
991
|
0
|
|
|
|
|
|
for my $position (@$positions) |
|
992
|
|
|
|
|
|
|
{ |
|
993
|
|
|
|
|
|
|
|
|
994
|
0
|
|
|
|
|
|
my $json = JSON->new; |
|
995
|
|
|
|
|
|
|
|
|
996
|
0
|
0
|
|
|
|
|
$position->{size}=-abs($position->{size}) if ($position->{direction} eq 'SELL'); |
|
997
|
0
|
|
|
|
|
|
$position->{profit}=($position->{bid}-$position->{level})*$position->{size}; |
|
998
|
|
|
|
|
|
|
# create new profits element |
|
999
|
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
# my $open=$position->{bid}-$position->{profit}/$position->{size}; |
|
1001
|
|
|
|
|
|
|
# $position->{level}=$open; |
|
1002
|
0
|
|
|
|
|
|
$position->{profitpc}=int(0.5+1000*$position->{profit}/($position->{level}*abs($position->{size})))/10; |
|
1003
|
0
|
|
|
|
|
|
$position->{atrisk}=$position->{bid}*$position->{size}; |
|
1004
|
0
|
|
|
|
|
|
$position->{createdDateOnly}=$position->{createdDate}; |
|
1005
|
0
|
|
|
|
|
|
$position->{createdDateOnly}=~s/ .*$//; |
|
1006
|
0
|
0
|
|
|
|
|
$position->{held}=Time::Piece->strptime($position->{createdDateUTC},"%Y-%m-%dT%H:%M:%S") or die "strptime failed for ".$position->{createdDateOnly}; |
|
1007
|
0
|
|
|
|
|
|
$position->{held}=(gmtime()-$position->{held})/(24*3600); |
|
1008
|
0
|
|
|
|
|
|
$position->{held}=int($position->{held}*10+0.5)/10; |
|
1009
|
0
|
|
|
|
|
|
$position->{dailyp}=''; |
|
1010
|
0
|
0
|
|
|
|
|
$position->{dailyp}=((1+$position->{profitpc}/100.0)**(1/$position->{held})-1)*100 if ($position->{held}>0); |
|
1011
|
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
} |
|
1013
|
|
|
|
|
|
|
|
|
1014
|
0
|
|
0
|
|
|
|
$sortlist//=[qw(-profitpc instrumentName)]; # default sort |
|
1015
|
0
|
|
|
|
|
|
$self->sorter($sortlist,$positions); |
|
1016
|
0
|
|
|
|
|
|
return $positions; |
|
1017
|
|
|
|
|
|
|
} |
|
1018
|
|
|
|
|
|
|
#################################################################### |
|
1019
|
|
|
|
|
|
|
# General array sort function. |
|
1020
|
|
|
|
|
|
|
# Given an array of hash refs, and a sort key |
|
1021
|
|
|
|
|
|
|
# considtying of an array of an array of keys to the hashes |
|
1022
|
|
|
|
|
|
|
# sort in place the array. |
|
1023
|
|
|
|
|
|
|
# |
|
1024
|
|
|
|
|
|
|
# sortkey, arrayref of keys. Sort order direction reversed |
|
1025
|
|
|
|
|
|
|
# if key has - appended to start, eg -profitpc gives largest first |
|
1026
|
|
|
|
|
|
|
# pos array eo be sorted, its an inplace sort. |
|
1027
|
|
|
|
|
|
|
# uses the determinant $x eq $x+0 to determine if numeric or not. |
|
1028
|
|
|
|
|
|
|
# improvements: may need to use a deep fetch to locate the items |
|
1029
|
|
|
|
|
|
|
#################################################################### |
|
1030
|
|
|
|
|
|
|
|
|
1031
|
|
|
|
|
|
|
=head2 sorter - general array sort function for an array of hashes |
|
1032
|
|
|
|
|
|
|
|
|
1033
|
|
|
|
|
|
|
Parameters |
|
1034
|
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
1 Ref to array of keys to sort. Each my be prefixed with a - to |
|
1036
|
|
|
|
|
|
|
reverse the order on that key. If keys compare equal the next key is used. |
|
1037
|
|
|
|
|
|
|
2 Ref to an array of positions to sort. |
|
1038
|
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
The array is sorted in-place. A numeric comparison is done if for |
|
1040
|
|
|
|
|
|
|
both items $x == $x+0 |
|
1041
|
|
|
|
|
|
|
|
|
1042
|
|
|
|
|
|
|
Formatted datetimes are correctly sorted. |
|
1043
|
|
|
|
|
|
|
|
|
1044
|
|
|
|
|
|
|
=cut |
|
1045
|
|
|
|
|
|
|
#################################################################### |
|
1046
|
|
|
|
|
|
|
sub sorter |
|
1047
|
|
|
|
|
|
|
{ |
|
1048
|
0
|
|
|
0
|
1
|
|
my ($self,$sortkey,$pos)=@_; |
|
1049
|
|
|
|
|
|
|
|
|
1050
|
|
|
|
|
|
|
@$pos= sort { |
|
1051
|
0
|
|
|
|
|
|
my ($result)=0; |
|
|
0
|
|
|
|
|
|
|
|
1052
|
0
|
|
|
|
|
|
for my $fkey (@$sortkey) |
|
1053
|
|
|
|
|
|
|
{ |
|
1054
|
0
|
|
|
|
|
|
my $key=$fkey; |
|
1055
|
0
|
|
|
|
|
|
my $dir=1; |
|
1056
|
0
|
0
|
|
|
|
|
$dir=-1 if ($key=~s/^-//); |
|
1057
|
|
|
|
|
|
|
# die "key=$key value=$b->{createdDateUTC} keys are ".join(', ',keys %$a); ; |
|
1058
|
0
|
0
|
0
|
|
|
|
next if (!exists($a->{$key}) or !exists($b->{$key})); |
|
1059
|
0
|
|
|
|
|
|
my ($x1,$x2)=($a->{$key},$b->{$key}); |
|
1060
|
0
|
|
|
|
|
|
map { s/[£%]//g } ($x1,$x2); |
|
|
0
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
|
|
1062
|
1
|
|
|
1
|
|
10
|
{ no warnings qw(numeric); |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
5515
|
|
|
|
0
|
|
|
|
|
|
|
|
1063
|
0
|
|
|
|
|
|
my $warning; |
|
1064
|
|
|
|
|
|
|
|
|
1065
|
0
|
0
|
0
|
|
|
|
if ($x1 eq $x1+0 and $x2 eq $x2+0) |
|
1066
|
|
|
|
|
|
|
{ |
|
1067
|
0
|
|
|
|
|
|
$result=$x1<=>$x2; |
|
1068
|
|
|
|
|
|
|
} |
|
1069
|
|
|
|
|
|
|
else |
|
1070
|
|
|
|
|
|
|
{ # note that this correctly handles a formatted date |
|
1071
|
0
|
|
|
|
|
|
$result=$x1 cmp $x2; |
|
1072
|
|
|
|
|
|
|
} |
|
1073
|
|
|
|
|
|
|
} |
|
1074
|
0
|
0
|
|
|
|
|
return $result*$dir if ($result); |
|
1075
|
|
|
|
|
|
|
} |
|
1076
|
0
|
|
|
|
|
|
return 0; |
|
1077
|
|
|
|
|
|
|
} |
|
1078
|
|
|
|
|
|
|
@$pos; |
|
1079
|
|
|
|
|
|
|
|
|
1080
|
|
|
|
|
|
|
} |
|
1081
|
|
|
|
|
|
|
#################################################################### |
|
1082
|
|
|
|
|
|
|
# The idea is this will close all the supplied positions, optionally returning a reference to |
|
1083
|
|
|
|
|
|
|
# either/both an array of closed/non closed positions; |
|
1084
|
|
|
|
|
|
|
# This is not quite working yet, needs more work, |
|
1085
|
|
|
|
|
|
|
#################################################################### |
|
1086
|
|
|
|
|
|
|
|
|
1087
|
|
|
|
|
|
|
=head2 close - close the supplied positions. |
|
1088
|
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
|
|
1090
|
|
|
|
|
|
|
|
|
1091
|
|
|
|
|
|
|
Parameters |
|
1092
|
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
1 Ref to array of positions to close. |
|
1094
|
|
|
|
|
|
|
reverse the order on that key. |
|
1095
|
|
|
|
|
|
|
2/3 ref to done / notdone arrays to sort succesful / failed |
|
1096
|
|
|
|
|
|
|
closes in to. |
|
1097
|
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
The idea is this will close all the supplied positions, optionally returning a reference to |
|
1099
|
|
|
|
|
|
|
|
|
1100
|
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
=head3 Status - very experimental. |
|
1102
|
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
Contains die / print statements that you may wish to remove |
|
1104
|
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
=cut |
|
1106
|
|
|
|
|
|
|
#################################################################### |
|
1107
|
|
|
|
|
|
|
sub close |
|
1108
|
|
|
|
|
|
|
{ |
|
1109
|
0
|
|
|
0
|
1
|
|
my $self=shift; |
|
1110
|
0
|
|
|
|
|
|
my $positions=shift; # to close |
|
1111
|
0
|
|
|
|
|
|
my $done=shift; |
|
1112
|
0
|
|
|
|
|
|
my $notdone=shift; |
|
1113
|
|
|
|
|
|
|
|
|
1114
|
0
|
|
|
|
|
|
my $verbose=0; |
|
1115
|
|
|
|
|
|
|
|
|
1116
|
0
|
|
|
|
|
|
my @done; |
|
1117
|
|
|
|
|
|
|
my @notdone; |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
0
|
|
|
|
|
|
my $headers = { |
|
1120
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
1121
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
1122
|
|
|
|
|
|
|
VERSION => 1, |
|
1123
|
|
|
|
|
|
|
# 'IG-ACCOUNT-ID'=> $accountid, |
|
1124
|
|
|
|
|
|
|
CST=>$self->CST, |
|
1125
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
1126
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
1127
|
|
|
|
|
|
|
'_method'=>'DELETE', |
|
1128
|
|
|
|
|
|
|
}; |
|
1129
|
|
|
|
|
|
|
|
|
1130
|
0
|
|
|
|
|
|
my $data = { |
|
1131
|
|
|
|
|
|
|
#encryptedPassword => "false", |
|
1132
|
|
|
|
|
|
|
#identifier => $self->username, |
|
1133
|
|
|
|
|
|
|
#password => $self->password |
|
1134
|
|
|
|
|
|
|
#direction => 'BUY', |
|
1135
|
|
|
|
|
|
|
# epic=> |
|
1136
|
|
|
|
|
|
|
# expiry=> |
|
1137
|
|
|
|
|
|
|
orderType=>'MARKET', |
|
1138
|
|
|
|
|
|
|
#size=>0.1 |
|
1139
|
|
|
|
|
|
|
##guaranteedStop=>'false', |
|
1140
|
|
|
|
|
|
|
forceOpen=>'true', |
|
1141
|
|
|
|
|
|
|
#timeInForce => "EXECUTE_AND_ELIMINATE", # "GOOD_TILL_CANCELLED" |
|
1142
|
|
|
|
|
|
|
timeInForce => "", # "GOOD_TILL_CANCELLED" |
|
1143
|
|
|
|
|
|
|
}; |
|
1144
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
1145
|
|
|
|
|
|
|
|
|
1146
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
1147
|
|
|
|
|
|
|
|
|
1148
|
|
|
|
|
|
|
|
|
1149
|
0
|
|
|
|
|
|
my %existhash; |
|
1150
|
0
|
|
|
|
|
|
map { $existhash{$self->fetch($_,'epic')}=$_ } @$positions; # creat a hash on epic |
|
|
0
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
|
|
1152
|
0
|
|
|
|
|
|
for my $position (@$positions) |
|
1153
|
|
|
|
|
|
|
{ |
|
1154
|
|
|
|
|
|
|
# die dump($position); |
|
1155
|
|
|
|
|
|
|
|
|
1156
|
0
|
|
|
|
|
|
my $existingsize=0; |
|
1157
|
0
|
|
|
|
|
|
my $epic=$self->fetch($position,'epic'); |
|
1158
|
0
|
|
|
|
|
|
my $name=$self->fetch($position,'instrumentName'); |
|
1159
|
|
|
|
|
|
|
|
|
1160
|
0
|
|
|
|
|
|
my $ms=$self->fetch($position,'marketStatus'); |
|
1161
|
|
|
|
|
|
|
|
|
1162
|
0
|
0
|
|
|
|
|
if ($ms ne 'TRADEABLE') |
|
1163
|
|
|
|
|
|
|
{ |
|
1164
|
0
|
|
|
|
|
|
push(@notdone,$position); |
|
1165
|
0
|
|
|
|
|
|
print "$name, market status is $ms\n"; |
|
1166
|
0
|
|
|
|
|
|
next; |
|
1167
|
|
|
|
|
|
|
} |
|
1168
|
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
#$data->{epic}=$self->fetch($position,'epic'); |
|
1171
|
0
|
|
|
|
|
|
$data->{epic}=$epic; |
|
1172
|
0
|
|
|
|
|
|
$data->{size}=$self->fetch($position,'size'); |
|
1173
|
|
|
|
|
|
|
# $data->{currencyCode}=$self->fetch($position,'currency'); |
|
1174
|
0
|
|
|
|
|
|
$data->{expiry}='DFB'; |
|
1175
|
|
|
|
|
|
|
# $data->{expiry}='-'; |
|
1176
|
0
|
|
|
|
|
|
$data->{direction}='SELL'; |
|
1177
|
|
|
|
|
|
|
|
|
1178
|
|
|
|
|
|
|
#my $jdata = encode_json($data); |
|
1179
|
0
|
|
|
|
|
|
my $jdata=JSON->new->canonical->encode($data); |
|
1180
|
0
|
|
|
|
|
|
$client->PUT ( |
|
1181
|
|
|
|
|
|
|
'/positions/otc', |
|
1182
|
|
|
|
|
|
|
$jdata, |
|
1183
|
|
|
|
|
|
|
$headers |
|
1184
|
|
|
|
|
|
|
); |
|
1185
|
0
|
|
|
|
|
|
my $code=$client->responseCode(); |
|
1186
|
0
|
0
|
|
|
|
|
if ($code==200) |
|
1187
|
|
|
|
|
|
|
{ |
|
1188
|
0
|
|
|
|
|
|
my $resp=decode_json($client->responseContent()); |
|
1189
|
0
|
|
|
|
|
|
my $dealReference=$resp->{dealReference}; |
|
1190
|
0
|
|
|
|
|
|
print "$name, dr=$dealReference\n"; |
|
1191
|
0
|
0
|
0
|
|
|
|
if (defined $dealReference and length($dealReference)>5) |
|
1192
|
|
|
|
|
|
|
{ |
|
1193
|
0
|
|
|
|
|
|
push(@done,$position); |
|
1194
|
0
|
|
|
|
|
|
die; |
|
1195
|
0
|
|
|
|
|
|
next; |
|
1196
|
0
|
|
|
|
|
|
next; |
|
1197
|
|
|
|
|
|
|
} |
|
1198
|
|
|
|
|
|
|
} |
|
1199
|
|
|
|
|
|
|
else |
|
1200
|
|
|
|
|
|
|
{ |
|
1201
|
0
|
|
|
|
|
|
print "$name failed $code: ".$client->responseContent()."\n"; |
|
1202
|
0
|
|
|
|
|
|
push(@notdone,$position); |
|
1203
|
|
|
|
|
|
|
} |
|
1204
|
|
|
|
|
|
|
} |
|
1205
|
0
|
0
|
|
|
|
|
@$done=@done if ($done); |
|
1206
|
0
|
0
|
|
|
|
|
@$notdone=@notdone if ($notdone); |
|
1207
|
|
|
|
|
|
|
|
|
1208
|
0
|
|
|
|
|
|
printf "done=%d notdone=%d\n",0+@done,0+@notdone; |
|
1209
|
0
|
|
|
|
|
|
print "notdone:\n"; |
|
1210
|
|
|
|
|
|
|
|
|
1211
|
0
|
|
|
|
|
|
my $cpc='%%'; |
|
1212
|
0
|
|
|
|
|
|
my $format="%-41sinstrumentName %+4.2fsize %-9.2flevel ". |
|
1213
|
|
|
|
|
|
|
"%-8.2fbid £%-8.2fprofit %4.1fprofitpc%% £%10.2fatrisk %-9sstopLevel %-4sslpc$cpc\n"; |
|
1214
|
|
|
|
|
|
|
|
|
1215
|
0
|
|
|
|
|
|
$self->printpos("stdout" , ['Name','Size','Open','Latest','P/L','P/L%','Value','Stop','Stop'], $format); |
|
1216
|
|
|
|
|
|
|
|
|
1217
|
0
|
|
|
|
|
|
map { $self->printpos("stdout" , $_, $format) } @notdone; |
|
|
0
|
|
|
|
|
|
|
|
1218
|
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
} |
|
1220
|
|
|
|
|
|
|
##################################################################### |
|
1221
|
|
|
|
|
|
|
# given a ref to an array of positions, attempt to buy the same |
|
1222
|
|
|
|
|
|
|
# position in this object. |
|
1223
|
|
|
|
|
|
|
# if the position already exists or is succesfully brought, count as success. |
|
1224
|
|
|
|
|
|
|
# If the buy fails, include it in the returned list. |
|
1225
|
|
|
|
|
|
|
# If all buys succesful then return an empty list. |
|
1226
|
|
|
|
|
|
|
# done and notdone references may be supplied and if they are these should point to arrays |
|
1227
|
|
|
|
|
|
|
# of the succesful and unsuccesful positions. |
|
1228
|
|
|
|
|
|
|
# return value is NOT now used. |
|
1229
|
|
|
|
|
|
|
# ignortradeable ... use this if the positionis an old one, so that tradeable status could |
|
1230
|
|
|
|
|
|
|
# be out of date. |
|
1231
|
|
|
|
|
|
|
##################################################################### |
|
1232
|
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
=head2 buy - attempt to buy a number of instruments. |
|
1234
|
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
Parameters |
|
1236
|
|
|
|
|
|
|
|
|
1237
|
|
|
|
|
|
|
1 Reference to an array of positions |
|
1238
|
|
|
|
|
|
|
2 Optional ref to an array done, to be filled with succesful buys |
|
1239
|
|
|
|
|
|
|
3 Optional ref to an array notdone, to be filled with the failed |
|
1240
|
|
|
|
|
|
|
4 ignore tradeable, one of the fields in a position relates to the market |
|
1241
|
|
|
|
|
|
|
being open or closed (TRADEABLE) If this field is current, its a |
|
1242
|
|
|
|
|
|
|
good indication to skip this one (place it in the notdone array. |
|
1243
|
|
|
|
|
|
|
But if its out of date then setting this flag 1 attempts the trade |
|
1244
|
|
|
|
|
|
|
anyway. |
|
1245
|
|
|
|
|
|
|
|
|
1246
|
|
|
|
|
|
|
Attempt to buy positions. I have used this to move positions |
|
1247
|
|
|
|
|
|
|
between a demo account and real account or vice-versa. |
|
1248
|
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
=head3 Status - very experimental. |
|
1250
|
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
Contains print statements that should |
|
1252
|
|
|
|
|
|
|
probably be removed. |
|
1253
|
|
|
|
|
|
|
|
|
1254
|
|
|
|
|
|
|
=cut |
|
1255
|
|
|
|
|
|
|
##################################################################### |
|
1256
|
|
|
|
|
|
|
sub buy |
|
1257
|
|
|
|
|
|
|
{ |
|
1258
|
0
|
|
|
0
|
1
|
|
my $self=shift; |
|
1259
|
0
|
|
|
|
|
|
my $positions=shift; # to buy |
|
1260
|
0
|
|
|
|
|
|
my $done=shift; |
|
1261
|
0
|
|
|
|
|
|
my $notdone=shift; |
|
1262
|
0
|
|
|
|
|
|
my $ignoretradeable=shift; |
|
1263
|
|
|
|
|
|
|
|
|
1264
|
0
|
|
|
|
|
|
my $verbose=0; |
|
1265
|
|
|
|
|
|
|
|
|
1266
|
0
|
|
|
|
|
|
my @done; |
|
1267
|
|
|
|
|
|
|
my @notdone; |
|
1268
|
0
|
|
|
|
|
|
my $headers = { |
|
1269
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
1270
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
1271
|
|
|
|
|
|
|
VERSION => 2, |
|
1272
|
|
|
|
|
|
|
# 'IG-ACCOUNT-ID'=> $accountid, |
|
1273
|
|
|
|
|
|
|
CST=>$self->CST, |
|
1274
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
1275
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
1276
|
|
|
|
|
|
|
}; |
|
1277
|
|
|
|
|
|
|
|
|
1278
|
0
|
|
|
|
|
|
my $data = { |
|
1279
|
|
|
|
|
|
|
direction => 'BUY', |
|
1280
|
|
|
|
|
|
|
#epic=> |
|
1281
|
|
|
|
|
|
|
#size=>0.1 |
|
1282
|
|
|
|
|
|
|
orderType=>'MARKET', |
|
1283
|
|
|
|
|
|
|
guaranteedStop=>'false', |
|
1284
|
|
|
|
|
|
|
forceOpen=>'false', |
|
1285
|
|
|
|
|
|
|
timeInForce => "EXECUTE_AND_ELIMINATE", # "GOOD_TILL_CANCELLED" |
|
1286
|
|
|
|
|
|
|
}; |
|
1287
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
1288
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
1289
|
|
|
|
|
|
|
|
|
1290
|
0
|
|
|
|
|
|
my $existing=$self->positions; |
|
1291
|
0
|
|
|
|
|
|
my %existhash; |
|
1292
|
0
|
|
|
|
|
|
map { $existhash{$self->fetch($_,'epic')}=$_ } @$existing; |
|
|
0
|
|
|
|
|
|
|
|
1293
|
|
|
|
|
|
|
|
|
1294
|
0
|
|
|
|
|
|
for my $position (@$positions) |
|
1295
|
|
|
|
|
|
|
{ |
|
1296
|
|
|
|
|
|
|
# die dump($position); |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
0
|
|
|
|
|
|
my $existingsize=0; |
|
1299
|
0
|
|
|
|
|
|
my $epic=$self->fetch($position,'epic'); |
|
1300
|
0
|
|
|
|
|
|
my $name=$self->fetch($position,'instrumentName'); |
|
1301
|
|
|
|
|
|
|
|
|
1302
|
0
|
|
|
|
|
|
my $ms=$self->fetch($position,'marketStatus'); |
|
1303
|
|
|
|
|
|
|
|
|
1304
|
|
|
|
|
|
|
|
|
1305
|
0
|
0
|
|
|
|
|
if (exists $existhash{$epic}) |
|
1306
|
|
|
|
|
|
|
{ |
|
1307
|
0
|
|
|
|
|
|
my $existingposition=$existhash{$epic}; |
|
1308
|
0
|
|
|
|
|
|
$existingsize=$self->fetch($existingposition,'size'); |
|
1309
|
|
|
|
|
|
|
} |
|
1310
|
|
|
|
|
|
|
|
|
1311
|
0
|
|
|
|
|
|
my $demandsize=$self->fetch($position,'size'); |
|
1312
|
0
|
|
|
|
|
|
my $wantedsize=$demandsize-$existingsize; |
|
1313
|
|
|
|
|
|
|
|
|
1314
|
0
|
|
|
|
|
|
print "existingsize=$existingsize wantedsize=$wantedsize, demandsize=$demandsize\n"; |
|
1315
|
0
|
0
|
|
|
|
|
if ($wantedsize<=0) |
|
1316
|
|
|
|
|
|
|
{ |
|
1317
|
0
|
|
|
|
|
|
push(@done,$position); |
|
1318
|
0
|
|
|
|
|
|
print "$name, not needed\n"; |
|
1319
|
0
|
|
|
|
|
|
next; |
|
1320
|
|
|
|
|
|
|
} |
|
1321
|
|
|
|
|
|
|
|
|
1322
|
0
|
0
|
0
|
|
|
|
if ($ms ne 'TRADEABLE' and !$ignoretradeable) |
|
1323
|
|
|
|
|
|
|
{ |
|
1324
|
0
|
|
|
|
|
|
push(@notdone,$position); |
|
1325
|
0
|
|
|
|
|
|
print "$name, market status is $ms\n"; |
|
1326
|
0
|
|
|
|
|
|
next; |
|
1327
|
|
|
|
|
|
|
} |
|
1328
|
|
|
|
|
|
|
|
|
1329
|
|
|
|
|
|
|
|
|
1330
|
|
|
|
|
|
|
#$data->{epic}=$self->fetch($position,'epic'); |
|
1331
|
0
|
|
|
|
|
|
$data->{epic}=$epic; |
|
1332
|
0
|
|
|
|
|
|
$data->{size}=$wantedsize; |
|
1333
|
0
|
|
|
|
|
|
$data->{currencyCode}=$self->fetch($position,'currency'); |
|
1334
|
0
|
|
|
|
|
|
$data->{expiry}='DFB'; |
|
1335
|
|
|
|
|
|
|
|
|
1336
|
|
|
|
|
|
|
#my $jdata = encode_json($data); |
|
1337
|
0
|
|
|
|
|
|
my $jdata=JSON->new->canonical->encode($data); |
|
1338
|
|
|
|
|
|
|
# die $jdata; |
|
1339
|
0
|
|
|
|
|
|
print "$data->{direction}: $position->{instrumentName} $position->{size}\n"; |
|
1340
|
0
|
|
|
|
|
|
$client->POST ( |
|
1341
|
|
|
|
|
|
|
'/positions/otc', |
|
1342
|
|
|
|
|
|
|
$jdata, |
|
1343
|
|
|
|
|
|
|
$headers |
|
1344
|
|
|
|
|
|
|
); |
|
1345
|
0
|
|
|
|
|
|
my $code=$client->responseCode(); |
|
1346
|
0
|
0
|
|
|
|
|
if ($code==200) |
|
1347
|
|
|
|
|
|
|
{ |
|
1348
|
0
|
|
|
|
|
|
print "200: ".$client->responseContent()."\n"; |
|
1349
|
0
|
|
|
|
|
|
my $resp=decode_json($client->responseContent()); |
|
1350
|
0
|
|
|
|
|
|
my $dealReference=$resp->{dealReference}; |
|
1351
|
0
|
|
|
|
|
|
print "$name, dr=$dealReference\n"; |
|
1352
|
0
|
0
|
0
|
|
|
|
if (defined $dealReference and length($dealReference)>5) |
|
1353
|
|
|
|
|
|
|
{ |
|
1354
|
0
|
|
|
|
|
|
push(@done,$position); |
|
1355
|
0
|
|
|
|
|
|
next; |
|
1356
|
|
|
|
|
|
|
} |
|
1357
|
|
|
|
|
|
|
} |
|
1358
|
0
|
|
|
|
|
|
print "$name, failed code $code \n"; |
|
1359
|
0
|
|
|
|
|
|
push(@notdone,$position); |
|
1360
|
|
|
|
|
|
|
} |
|
1361
|
0
|
0
|
|
|
|
|
@$done=@done if ($done); |
|
1362
|
0
|
0
|
|
|
|
|
@$notdone=@notdone if ($notdone); |
|
1363
|
0
|
|
|
|
|
|
printf "done=%d notdone=%d\n",0+@done,0+@notdone; |
|
1364
|
0
|
|
|
|
|
|
print "notdone:\n"; |
|
1365
|
|
|
|
|
|
|
|
|
1366
|
0
|
|
|
|
|
|
return; |
|
1367
|
|
|
|
|
|
|
|
|
1368
|
0
|
|
|
|
|
|
my $format="%-41sinstrumentName %+4.2fsize %-9.2flevel ". |
|
1369
|
|
|
|
|
|
|
"%-8.2fbid £%-8.2fprofit %4.1fprofitpc%% £%10.2fatrisk\n"; |
|
1370
|
|
|
|
|
|
|
|
|
1371
|
0
|
|
|
|
|
|
$self->printpos("stdout" , ['Name','Size','Open','Latest','P/L','P/L%','Value','Stop','Stop'], $format); |
|
1372
|
|
|
|
|
|
|
|
|
1373
|
0
|
|
|
|
|
|
map { $self->printpos("stdout" , $_, $format) } @notdone; |
|
|
0
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
|
|
1375
|
|
|
|
|
|
|
} |
|
1376
|
|
|
|
|
|
|
|
|
1377
|
|
|
|
|
|
|
##################################################################### |
|
1378
|
|
|
|
|
|
|
|
|
1379
|
|
|
|
|
|
|
=head2 prices - Obtain historical prices |
|
1380
|
|
|
|
|
|
|
|
|
1381
|
|
|
|
|
|
|
Obtain historical price information on an instrument. |
|
1382
|
|
|
|
|
|
|
|
|
1383
|
|
|
|
|
|
|
=head3 Parameters |
|
1384
|
|
|
|
|
|
|
|
|
1385
|
|
|
|
|
|
|
Unused parameters should be set as undef or ''. (either); |
|
1386
|
|
|
|
|
|
|
|
|
1387
|
|
|
|
|
|
|
1 A aubstring to be searched for in the name. Eg "UB.D.FTNT.DAILY.IP" |
|
1388
|
|
|
|
|
|
|
|
|
1389
|
|
|
|
|
|
|
2 Resolution. Should be one of the IG defined strings (left) or (in my opinion more memorable) aliases (right) |
|
1390
|
|
|
|
|
|
|
|
|
1391
|
|
|
|
|
|
|
DAY 1d |
|
1392
|
|
|
|
|
|
|
HOUR 1h |
|
1393
|
|
|
|
|
|
|
HOUR_2 1h |
|
1394
|
|
|
|
|
|
|
HOUR_3 2h |
|
1395
|
|
|
|
|
|
|
HOUR_4 3h |
|
1396
|
|
|
|
|
|
|
MINUTE 1m |
|
1397
|
|
|
|
|
|
|
MINUTE_2 2m |
|
1398
|
|
|
|
|
|
|
MINUTE_3 3m |
|
1399
|
|
|
|
|
|
|
MINUTE_5 5m |
|
1400
|
|
|
|
|
|
|
MINUTE_10 10m |
|
1401
|
|
|
|
|
|
|
MINUTE_15 15m |
|
1402
|
|
|
|
|
|
|
MINUTE_30 30m |
|
1403
|
|
|
|
|
|
|
SECOND 1s |
|
1404
|
|
|
|
|
|
|
WEEK 1w |
|
1405
|
|
|
|
|
|
|
MONTH 1M |
|
1406
|
|
|
|
|
|
|
|
|
1407
|
|
|
|
|
|
|
4, 5 pageNumber, pageSize What page to produce, and how many items on it. |
|
1408
|
|
|
|
|
|
|
|
|
1409
|
|
|
|
|
|
|
6, 7 from , to (dates) can be a string of the form 2021-01-01T16:15:00 or a Time::Piece |
|
1410
|
|
|
|
|
|
|
|
|
1411
|
|
|
|
|
|
|
8 max Limits the number of price points (not applicable if a date range has been specified) |
|
1412
|
|
|
|
|
|
|
|
|
1413
|
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
|
|
1415
|
|
|
|
|
|
|
|
|
1416
|
|
|
|
|
|
|
|
|
1417
|
|
|
|
|
|
|
=cut |
|
1418
|
|
|
|
|
|
|
|
|
1419
|
|
|
|
|
|
|
##################################################################### |
|
1420
|
|
|
|
|
|
|
# Historical prices |
|
1421
|
|
|
|
|
|
|
# epic, resolution , pagenum, pagessize, from.to max |
|
1422
|
|
|
|
|
|
|
##################################################################### |
|
1423
|
|
|
|
|
|
|
sub prices |
|
1424
|
|
|
|
|
|
|
{ |
|
1425
|
|
|
|
|
|
|
|
|
1426
|
0
|
|
|
0
|
1
|
|
my $self=shift; |
|
1427
|
0
|
|
|
|
|
|
my $epic=shift; |
|
1428
|
0
|
|
|
|
|
|
my $resolution=shift; |
|
1429
|
0
|
|
|
|
|
|
my $pagenumber=shift; |
|
1430
|
0
|
|
|
|
|
|
my $pagesize=shift; |
|
1431
|
|
|
|
|
|
|
|
|
1432
|
0
|
|
|
|
|
|
my $from=shift; |
|
1433
|
0
|
|
|
|
|
|
my $to=shift; |
|
1434
|
0
|
|
|
|
|
|
my $max=shift; |
|
1435
|
|
|
|
|
|
|
|
|
1436
|
|
|
|
|
|
|
|
|
1437
|
0
|
0
|
|
|
|
|
if (ref($to) eq 'Time::Piece') |
|
1438
|
|
|
|
|
|
|
{ |
|
1439
|
0
|
|
|
|
|
|
$to=$to->strftime("%Y-%m-%dT%H:%M:%S"); |
|
1440
|
|
|
|
|
|
|
} |
|
1441
|
0
|
0
|
|
|
|
|
if (ref($from) eq 'Time::Piece') |
|
1442
|
|
|
|
|
|
|
{ |
|
1443
|
0
|
|
|
|
|
|
$from=$from->strftime("%Y-%m-%dT%H:%M:%S"); |
|
1444
|
|
|
|
|
|
|
} |
|
1445
|
|
|
|
|
|
|
|
|
1446
|
0
|
|
0
|
|
|
|
$pagesize//=1; # set a default of 1 item per page |
|
1447
|
|
|
|
|
|
|
# $pagenumber=1; # set a default of page 1, not needed as already set as defult |
|
1448
|
|
|
|
|
|
|
|
|
1449
|
0
|
|
|
|
|
|
my $headers = { |
|
1450
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
1451
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
1452
|
|
|
|
|
|
|
VERSION => 3, |
|
1453
|
|
|
|
|
|
|
# 'IG-ACCOUNT-ID'=> $accountid, |
|
1454
|
|
|
|
|
|
|
CST=>$self->CST, |
|
1455
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
1456
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
1457
|
|
|
|
|
|
|
}; |
|
1458
|
|
|
|
|
|
|
|
|
1459
|
0
|
|
|
|
|
|
$resolution="MINUTE_10"; |
|
1460
|
0
|
|
|
|
|
|
$resolution="HOUR_4"; |
|
1461
|
|
|
|
|
|
|
|
|
1462
|
|
|
|
|
|
|
# An alternative and more memorable resolution constants. IG values can also be used. |
|
1463
|
0
|
0
|
|
|
|
|
$resolution="DAY" if ($resolution eq '1d'); |
|
1464
|
0
|
0
|
|
|
|
|
$resolution="HOUR" if ($resolution eq'1h'); |
|
1465
|
0
|
0
|
|
|
|
|
$resolution="HOUR_2" if ($resolution eq '1h'); |
|
1466
|
0
|
0
|
|
|
|
|
$resolution="HOUR_3" if ($resolution eq '2h'); |
|
1467
|
0
|
0
|
|
|
|
|
$resolution="HOUR_4" if ($resolution eq '3h'); |
|
1468
|
0
|
0
|
|
|
|
|
$resolution="MINUTE" if ($resolution eq '1m'); |
|
1469
|
0
|
0
|
|
|
|
|
$resolution="MINUTE_2" if ($resolution eq '2m'); |
|
1470
|
0
|
0
|
|
|
|
|
$resolution="MINUTE_3" if ($resolution eq '3m'); |
|
1471
|
0
|
0
|
|
|
|
|
$resolution="MINUTE_5" if ($resolution eq '5m'); |
|
1472
|
0
|
0
|
|
|
|
|
$resolution="MINUTE_10" if ($resolution eq '10m'); |
|
1473
|
0
|
0
|
|
|
|
|
$resolution="MINUTE_15" if ($resolution eq '15m'); |
|
1474
|
0
|
0
|
|
|
|
|
$resolution="MINUTE_30" if ($resolution eq '30m'); |
|
1475
|
0
|
0
|
|
|
|
|
$resolution="SECOND" if ($resolution eq '1s'); |
|
1476
|
0
|
0
|
|
|
|
|
$resolution="WEEK" if ($resolution eq '1w'); |
|
1477
|
0
|
0
|
|
|
|
|
$resolution="MONTH" if ($resolution eq '1M'); |
|
1478
|
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
defined $resolution and |
|
1480
|
0
|
0
|
0
|
|
|
|
(0==grep { $resolution eq $_} qw(DAY HOUR HOUR_2 HOUR_3 HOUR_4 MINUTE MINUTE_10 MINUTE_15 MINUTE_2 MINUTE_3 MINUTE_30 MINUTE_5 MONTH SECOND WEEK)) and |
|
|
0
|
|
|
|
|
|
|
|
1481
|
|
|
|
|
|
|
die "Resolution is '$resolution', not recognised"; |
|
1482
|
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
#my $jheaders=JSON->new->canonical->encode($headers); |
|
1484
|
|
|
|
|
|
|
|
|
1485
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
1486
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
1487
|
|
|
|
|
|
|
#my $r; |
|
1488
|
|
|
|
|
|
|
|
|
1489
|
0
|
|
|
|
|
|
my $values={ |
|
1490
|
|
|
|
|
|
|
pageNumber=>$pagenumber, |
|
1491
|
|
|
|
|
|
|
pageSize=>$pagesize, |
|
1492
|
|
|
|
|
|
|
resolution=>$resolution, |
|
1493
|
|
|
|
|
|
|
from=>$from, |
|
1494
|
|
|
|
|
|
|
to=>$to, |
|
1495
|
|
|
|
|
|
|
max=>$max, |
|
1496
|
|
|
|
|
|
|
} ; |
|
1497
|
|
|
|
|
|
|
|
|
1498
|
0
|
|
|
|
|
|
delete @$values{ grep {!$values->{$_} } keys %$values} ; # delete all empty or undef values |
|
|
0
|
|
|
|
|
|
|
|
1499
|
0
|
|
|
|
|
|
map { $values->{$_}=$_."=".$values->{$_} } keys %$values ; |
|
|
0
|
|
|
|
|
|
|
|
1500
|
|
|
|
|
|
|
|
|
1501
|
0
|
|
|
|
|
|
my $url; |
|
1502
|
0
|
|
|
|
|
|
$url=join('&',sort values(%$values)); |
|
1503
|
0
|
0
|
|
|
|
|
$url='?'.$url if ($url); |
|
1504
|
0
|
|
|
|
|
|
$url="prices/$epic".$url; |
|
1505
|
|
|
|
|
|
|
|
|
1506
|
|
|
|
|
|
|
|
|
1507
|
0
|
|
|
|
|
|
$client->GET ( $url, |
|
1508
|
|
|
|
|
|
|
$headers |
|
1509
|
|
|
|
|
|
|
); |
|
1510
|
|
|
|
|
|
|
|
|
1511
|
|
|
|
|
|
|
|
|
1512
|
0
|
|
|
|
|
|
my $resp=decode_json($client->responseContent()); |
|
1513
|
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
|
|
1515
|
|
|
|
|
|
|
|
|
1516
|
0
|
|
|
|
|
|
$self->flatten_withunder($resp); |
|
1517
|
|
|
|
|
|
|
# print JSON->new->canonical->pretty->encode($resp); exit; |
|
1518
|
|
|
|
|
|
|
|
|
1519
|
0
|
|
|
|
|
|
return $resp; |
|
1520
|
|
|
|
|
|
|
} |
|
1521
|
|
|
|
|
|
|
##################################################################### |
|
1522
|
|
|
|
|
|
|
# flatten_withunder |
|
1523
|
|
|
|
|
|
|
# flattens a deep hash, 3 levels max, where complex hashes are |
|
1524
|
|
|
|
|
|
|
# removed and replace with _ joined shallow hash values |
|
1525
|
|
|
|
|
|
|
# for exapmple: |
|
1526
|
|
|
|
|
|
|
# { |
|
1527
|
|
|
|
|
|
|
# "metadata" : { |
|
1528
|
|
|
|
|
|
|
# "allowance" : { |
|
1529
|
|
|
|
|
|
|
# "allowanceExpiry" : 530567, |
|
1530
|
|
|
|
|
|
|
# "remainingAllowance" : 9557, |
|
1531
|
|
|
|
|
|
|
# "totalAllowance" : 10000 |
|
1532
|
|
|
|
|
|
|
# }, |
|
1533
|
|
|
|
|
|
|
# ... |
|
1534
|
|
|
|
|
|
|
# |
|
1535
|
|
|
|
|
|
|
# becomes |
|
1536
|
|
|
|
|
|
|
# { |
|
1537
|
|
|
|
|
|
|
# "metadata_allowance_allowanceExpiry" : 530473, |
|
1538
|
|
|
|
|
|
|
# "metadata_allowance_remainingAllowance" : 9556, |
|
1539
|
|
|
|
|
|
|
# "metadata_allowance_totalAllowance" : 10000, |
|
1540
|
|
|
|
|
|
|
# ... |
|
1541
|
|
|
|
|
|
|
# The advantage of a flattened structure is its easier to print. |
|
1542
|
|
|
|
|
|
|
|
|
1543
|
|
|
|
|
|
|
##################################################################### |
|
1544
|
|
|
|
|
|
|
|
|
1545
|
|
|
|
|
|
|
=head2 flatten_withunder |
|
1546
|
|
|
|
|
|
|
|
|
1547
|
|
|
|
|
|
|
Flatten a deep structure, up to 3 layers deep using underscores to create new keys by concatenating deeper keys. |
|
1548
|
|
|
|
|
|
|
Deep keys are removed. More than 3 layers can be removed by calling multiply. |
|
1549
|
|
|
|
|
|
|
|
|
1550
|
|
|
|
|
|
|
=head3 Parameters |
|
1551
|
|
|
|
|
|
|
|
|
1552
|
|
|
|
|
|
|
One or more scalers to opperate on or an array. Each will be flattened |
|
1553
|
|
|
|
|
|
|
where there are hashes or hashes or hashes of hashes of hashes |
|
1554
|
|
|
|
|
|
|
to a single depth, with elements joined by underscores |
|
1555
|
|
|
|
|
|
|
|
|
1556
|
|
|
|
|
|
|
=head3 Example |
|
1557
|
|
|
|
|
|
|
|
|
1558
|
|
|
|
|
|
|
{ |
|
1559
|
|
|
|
|
|
|
"metadata" : { |
|
1560
|
|
|
|
|
|
|
"allowance" : { |
|
1561
|
|
|
|
|
|
|
"allowanceExpiry" : 530567, |
|
1562
|
|
|
|
|
|
|
"remainingAllowance" : 9557, |
|
1563
|
|
|
|
|
|
|
"totalAllowance" : 10000 |
|
1564
|
|
|
|
|
|
|
}, |
|
1565
|
|
|
|
|
|
|
... |
|
1566
|
|
|
|
|
|
|
|
|
1567
|
|
|
|
|
|
|
becomes |
|
1568
|
|
|
|
|
|
|
{ |
|
1569
|
|
|
|
|
|
|
"metadata_allowance_allowanceExpiry" : 530473, |
|
1570
|
|
|
|
|
|
|
"metadata_allowance_remainingAllowance" : 9556, |
|
1571
|
|
|
|
|
|
|
"metadata_allowance_totalAllowance" : 10000, |
|
1572
|
|
|
|
|
|
|
... |
|
1573
|
|
|
|
|
|
|
|
|
1574
|
|
|
|
|
|
|
The advantage of a flattened structure is its easier to print with existing fuunctions like printpos |
|
1575
|
|
|
|
|
|
|
|
|
1576
|
|
|
|
|
|
|
=cut |
|
1577
|
|
|
|
|
|
|
|
|
1578
|
|
|
|
|
|
|
##################################################################### |
|
1579
|
|
|
|
|
|
|
sub flatten_withunder |
|
1580
|
|
|
|
|
|
|
{ |
|
1581
|
0
|
|
|
0
|
1
|
|
my ($self)=shift; |
|
1582
|
0
|
|
|
|
|
|
my (@items)=@_; |
|
1583
|
0
|
|
|
|
|
|
my $fudebug=0; |
|
1584
|
0
|
0
|
|
|
|
|
$fudebug and printf "%d items to process\n",0+@items; |
|
1585
|
0
|
|
|
|
|
|
for my $item (@items) |
|
1586
|
|
|
|
|
|
|
{ |
|
1587
|
0
|
0
|
|
|
|
|
$fudebug and print "item is a ".ref($item)."\n"; |
|
1588
|
0
|
0
|
|
|
|
|
return if (ref($item)eq ''); |
|
1589
|
0
|
0
|
|
|
|
|
if (ref($item) eq 'HASH') |
|
1590
|
|
|
|
|
|
|
{ |
|
1591
|
0
|
0
|
|
|
|
|
$fudebug and print "is a hash\n"; |
|
1592
|
0
|
|
|
|
|
|
for my $key (keys %$item) |
|
1593
|
|
|
|
|
|
|
{ |
|
1594
|
0
|
0
|
|
|
|
|
$fudebug and print "key1 $key\n"; |
|
1595
|
0
|
0
|
|
|
|
|
if (ref($item->{$key}) eq 'HASH') |
|
1596
|
|
|
|
|
|
|
{ |
|
1597
|
0
|
|
|
|
|
|
for my $key2 (keys %{$item->{$key}}) |
|
|
0
|
|
|
|
|
|
|
|
1598
|
|
|
|
|
|
|
{ |
|
1599
|
0
|
0
|
|
|
|
|
$fudebug and print "keyr2 $key2\n"; |
|
1600
|
0
|
|
|
|
|
|
$item->{$key."_".$key2}=$item->{$key}->{$key2}; |
|
1601
|
0
|
0
|
|
|
|
|
$fudebug and printf "creating $key"."_"."$key2 as a %s\n",ref($item->{$key}->{$key2}); |
|
1602
|
|
|
|
|
|
|
|
|
1603
|
|
|
|
|
|
|
# $self->flatten_withunder($item->{$key}) if (ref($item->{$key}->{$key2}) eq 'HASH'); |
|
1604
|
0
|
0
|
|
|
|
|
if (ref($item->{$key}->{$key2}) eq 'HASH') |
|
1605
|
|
|
|
|
|
|
{ |
|
1606
|
0
|
|
|
|
|
|
for my $key3 (keys %{$item->{$key}->{$key2}}) |
|
|
0
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
{ |
|
1608
|
0
|
0
|
|
|
|
|
$fudebug and print "key3 $key3\n"; |
|
1609
|
0
|
|
|
|
|
|
$item->{$key."_".$key2."_".$key3}=$item->{$key}->{$key2}->{$key3}; |
|
1610
|
0
|
0
|
|
|
|
|
$fudebug and printf "creating $key"."_$key2"."_$key3 as a %s\n",ref($item->{$key}->{$key2}->{$key3}); |
|
1611
|
|
|
|
|
|
|
} |
|
1612
|
0
|
0
|
|
|
|
|
$fudebug and print "deleting $key->$key2 and $key _$key2\n"; |
|
1613
|
0
|
|
|
|
|
|
delete $item->{$key}->{$key2}; |
|
1614
|
0
|
|
|
|
|
|
delete $item->{$key."_".$key2}; |
|
1615
|
|
|
|
|
|
|
} |
|
1616
|
|
|
|
|
|
|
} |
|
1617
|
0
|
0
|
|
|
|
|
$fudebug and print "deleting: $key\n"; |
|
1618
|
0
|
|
|
|
|
|
delete $item->{$key}; |
|
1619
|
|
|
|
|
|
|
} |
|
1620
|
0
|
0
|
|
|
|
|
if (ref($item->{$key}) eq 'ARRAY') |
|
1621
|
|
|
|
|
|
|
{ |
|
1622
|
0
|
0
|
|
|
|
|
$fudebug and print "$key is array ref\n"; |
|
1623
|
0
|
|
|
|
|
|
for (@{$item->{$key}}) |
|
|
0
|
|
|
|
|
|
|
|
1624
|
|
|
|
|
|
|
{ |
|
1625
|
0
|
|
|
|
|
|
$self->flatten_withunder($_); |
|
1626
|
|
|
|
|
|
|
} |
|
1627
|
|
|
|
|
|
|
} |
|
1628
|
|
|
|
|
|
|
} |
|
1629
|
|
|
|
|
|
|
} |
|
1630
|
0
|
0
|
|
|
|
|
if (ref($item) eq 'ARRAY') |
|
1631
|
|
|
|
|
|
|
{ |
|
1632
|
0
|
0
|
|
|
|
|
$fudebug and print "is an array\n"; |
|
1633
|
0
|
|
|
|
|
|
for (@$item) |
|
1634
|
|
|
|
|
|
|
{ |
|
1635
|
0
|
|
|
|
|
|
$self->flatten_withunder($_); |
|
1636
|
|
|
|
|
|
|
} |
|
1637
|
|
|
|
|
|
|
} |
|
1638
|
|
|
|
|
|
|
} |
|
1639
|
0
|
0
|
|
|
|
|
$fudebug and print "processed\n"; |
|
1640
|
|
|
|
|
|
|
} |
|
1641
|
|
|
|
|
|
|
|
|
1642
|
|
|
|
|
|
|
|
|
1643
|
|
|
|
|
|
|
##################################################################### |
|
1644
|
|
|
|
|
|
|
# uses known structure of supplied deep hash to search for item |
|
1645
|
|
|
|
|
|
|
# should probably replace with a more generalised deep fetch function. |
|
1646
|
|
|
|
|
|
|
##################################################################### |
|
1647
|
|
|
|
|
|
|
|
|
1648
|
|
|
|
|
|
|
=head2 fetch |
|
1649
|
|
|
|
|
|
|
|
|
1650
|
|
|
|
|
|
|
This function is a way to hide the various structures a position may have |
|
1651
|
|
|
|
|
|
|
|
|
1652
|
|
|
|
|
|
|
Obsolete but still used sometimes. |
|
1653
|
|
|
|
|
|
|
|
|
1654
|
|
|
|
|
|
|
Parameters |
|
1655
|
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
1 A position hash ref, $h |
|
1657
|
|
|
|
|
|
|
2 The name of the item to be retrieved. |
|
1658
|
|
|
|
|
|
|
|
|
1659
|
|
|
|
|
|
|
Returns undef if not found, or the value of item if it is. |
|
1660
|
|
|
|
|
|
|
|
|
1661
|
|
|
|
|
|
|
The function looks first in $h->{item} then |
|
1662
|
|
|
|
|
|
|
in $h->{position}=>{item} and then in $h->{market}->{item} |
|
1663
|
|
|
|
|
|
|
|
|
1664
|
|
|
|
|
|
|
Its only useful with positions, not hashes in general. |
|
1665
|
|
|
|
|
|
|
|
|
1666
|
|
|
|
|
|
|
=cut |
|
1667
|
|
|
|
|
|
|
##################################################################### |
|
1668
|
|
|
|
|
|
|
sub fetch |
|
1669
|
|
|
|
|
|
|
{ |
|
1670
|
0
|
|
|
0
|
1
|
|
my ($self,$position,$item)=@_; |
|
1671
|
|
|
|
|
|
|
|
|
1672
|
|
|
|
|
|
|
# return "NOT A HASREF $position"if (ref($position) ne 'HASH'); |
|
1673
|
0
|
0
|
|
|
|
|
die "supplied position $position to fetch() is not a HASHREF" if (ref($position) ne 'HASH'); |
|
1674
|
0
|
0
|
|
|
|
|
defined $item or die "fetch, item undefined"; |
|
1675
|
0
|
|
|
|
|
|
my $p=$position->{position}; |
|
1676
|
0
|
|
|
|
|
|
my $m=$position->{market}; |
|
1677
|
|
|
|
|
|
|
|
|
1678
|
0
|
0
|
|
|
|
|
if (exists $position->{$item}) { return $position->{$item}; } |
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1679
|
0
|
|
|
|
|
|
elsif (exists $p->{$item}) { return $p->{$item}; } |
|
1680
|
0
|
|
|
|
|
|
elsif (exists $m->{$item}) { return $m->{$item}; } |
|
1681
|
|
|
|
|
|
|
else { |
|
1682
|
0
|
|
|
|
|
|
return undef; |
|
1683
|
|
|
|
|
|
|
} |
|
1684
|
|
|
|
|
|
|
|
|
1685
|
|
|
|
|
|
|
} |
|
1686
|
|
|
|
|
|
|
|
|
1687
|
|
|
|
|
|
|
##################################################################### |
|
1688
|
|
|
|
|
|
|
# given an instrument name in search, look for it inside the instrumentName, and return |
|
1689
|
|
|
|
|
|
|
# the epic. Fail if result is not 1 item. |
|
1690
|
|
|
|
|
|
|
# used for filling in the epic (a unique identifier) in old data files |
|
1691
|
|
|
|
|
|
|
# where I forgot to store it. |
|
1692
|
|
|
|
|
|
|
##################################################################### |
|
1693
|
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
=head2 epicsearch |
|
1695
|
|
|
|
|
|
|
|
|
1696
|
|
|
|
|
|
|
Find the epic (unique identifier) for an instrument from the underlying share. |
|
1697
|
|
|
|
|
|
|
|
|
1698
|
|
|
|
|
|
|
This function calls IG's search API looking for a match to the name. If found |
|
1699
|
|
|
|
|
|
|
the value of the epic is returned. |
|
1700
|
|
|
|
|
|
|
|
|
1701
|
|
|
|
|
|
|
=head3 Status - very experimental. Seems to work well. |
|
1702
|
|
|
|
|
|
|
|
|
1703
|
|
|
|
|
|
|
Contains print and die statements. Useful if you forgot to record the epic. |
|
1704
|
|
|
|
|
|
|
|
|
1705
|
|
|
|
|
|
|
=cut |
|
1706
|
|
|
|
|
|
|
|
|
1707
|
|
|
|
|
|
|
##################################################################### |
|
1708
|
|
|
|
|
|
|
sub epicsearch |
|
1709
|
|
|
|
|
|
|
{ |
|
1710
|
0
|
|
|
0
|
1
|
|
my ($self,$search)=@_; |
|
1711
|
0
|
|
|
|
|
|
my $headers = |
|
1712
|
|
|
|
|
|
|
{ |
|
1713
|
|
|
|
|
|
|
'Content-Type' => 'application/json; charset=UTF-8', |
|
1714
|
|
|
|
|
|
|
'Accept' => 'application/json; charset=UTF-8', |
|
1715
|
|
|
|
|
|
|
VERSION => 1, |
|
1716
|
|
|
|
|
|
|
CST=>$self->CST, |
|
1717
|
|
|
|
|
|
|
'X-SECURITY-TOKEN'=> $self->XSECURITYTOKEN, |
|
1718
|
|
|
|
|
|
|
'X-IG-API-KEY'=> $self->apikey, |
|
1719
|
|
|
|
|
|
|
}; |
|
1720
|
|
|
|
|
|
|
#my $jheaders = encode_json($headers); |
|
1721
|
0
|
|
|
|
|
|
my $jheaders=JSON->new->canonical->encode($headers); |
|
1722
|
0
|
|
|
|
|
|
my $client = REST::Client->new(); |
|
1723
|
0
|
|
|
|
|
|
$client->setHost($self->_url); |
|
1724
|
0
|
|
|
|
|
|
$search=~s#/#%2F#g; |
|
1725
|
0
|
|
|
|
|
|
my $url="/markets?searchTerm=$search"; |
|
1726
|
0
|
|
|
|
|
|
$search=~s#%2F#/#g; |
|
1727
|
0
|
|
|
|
|
|
$url=~s/ /%20/g; |
|
1728
|
0
|
|
|
|
|
|
my $r=$client->GET ( $url, $headers); |
|
1729
|
|
|
|
|
|
|
|
|
1730
|
|
|
|
|
|
|
# my $resp=decode_json($client->responseContent()); |
|
1731
|
|
|
|
|
|
|
|
|
1732
|
|
|
|
|
|
|
|
|
1733
|
|
|
|
|
|
|
#print "url=$url\n"; |
|
1734
|
0
|
|
|
|
|
|
my $code; |
|
1735
|
|
|
|
|
|
|
|
|
1736
|
0
|
|
|
|
|
|
$code=$client->responseCode(); |
|
1737
|
|
|
|
|
|
|
|
|
1738
|
0
|
|
|
|
|
|
my $retried=0; |
|
1739
|
0
|
|
0
|
|
|
|
while ($code==403 and $retried<4) |
|
1740
|
|
|
|
|
|
|
{ |
|
1741
|
0
|
|
|
|
|
|
sleep 10; |
|
1742
|
0
|
|
|
|
|
|
$retried++; |
|
1743
|
0
|
|
|
|
|
|
$r=$client->GET ( $url, $headers); |
|
1744
|
0
|
|
|
|
|
|
$code=$client->responseCode(); |
|
1745
|
|
|
|
|
|
|
# print "search retried\n"; |
|
1746
|
|
|
|
|
|
|
} |
|
1747
|
|
|
|
|
|
|
|
|
1748
|
0
|
0
|
|
|
|
|
die "response code from url='$url' code=$code retried $retried times" if ($code!=200); |
|
1749
|
|
|
|
|
|
|
|
|
1750
|
0
|
|
|
|
|
|
my $markets=decode_json($client->responseContent); |
|
1751
|
|
|
|
|
|
|
# print JSON->new->ascii->pretty->encode($markets)."\n"; |
|
1752
|
|
|
|
|
|
|
|
|
1753
|
0
|
|
|
|
|
|
my @wantedmarkets=grep { $_->{expiry} eq 'DFB' } @{$markets->{markets}}; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
1754
|
0
|
|
|
|
|
|
@wantedmarkets=grep { $self->_nothe($self->fetch($_,'instrumentName') , $search) } @wantedmarkets; |
|
|
0
|
|
|
|
|
|
|
|
1755
|
|
|
|
|
|
|
|
|
1756
|
0
|
|
|
|
|
|
@wantedmarkets=map { $_->{epic} } @wantedmarkets; |
|
|
0
|
|
|
|
|
|
|
|
1757
|
0
|
0
|
|
|
|
|
die "Zero epics found for search $search" if (@wantedmarkets==0); |
|
1758
|
0
|
0
|
|
|
|
|
die "Multiple epics found @wantedmarkets for search $search" if (@wantedmarkets!=1); |
|
1759
|
|
|
|
|
|
|
|
|
1760
|
0
|
|
|
|
|
|
return $wantedmarkets[0]; |
|
1761
|
|
|
|
|
|
|
|
|
1762
|
|
|
|
|
|
|
} |
|
1763
|
|
|
|
|
|
|
##################################################### |
|
1764
|
|
|
|
|
|
|
# remove a trailing 'the' |
|
1765
|
|
|
|
|
|
|
##################################################### |
|
1766
|
|
|
|
|
|
|
sub _nothe |
|
1767
|
|
|
|
|
|
|
{ |
|
1768
|
0
|
|
|
0
|
|
|
my ($self,$x,$y)=@_; |
|
1769
|
|
|
|
|
|
|
|
|
1770
|
|
|
|
|
|
|
# print "comparing $x $y \n"; |
|
1771
|
0
|
|
|
|
|
|
$x=~s#/.*$##; |
|
1772
|
0
|
|
|
|
|
|
$y=~s#/.*$##; |
|
1773
|
|
|
|
|
|
|
|
|
1774
|
0
|
|
|
|
|
|
return $x eq $y; |
|
1775
|
|
|
|
|
|
|
} |
|
1776
|
|
|
|
|
|
|
# so this is used to read one of my old data files. |
|
1777
|
|
|
|
|
|
|
################################################################################## |
|
1778
|
|
|
|
|
|
|
# Reads am ascii file - older format and returns a list of positions, |
|
1779
|
|
|
|
|
|
|
# a hashref keyed on epic. |
|
1780
|
|
|
|
|
|
|
################################################################################## |
|
1781
|
|
|
|
|
|
|
|
|
1782
|
|
|
|
|
|
|
=head2 readfile_oldformat |
|
1783
|
|
|
|
|
|
|
|
|
1784
|
|
|
|
|
|
|
|
|
1785
|
|
|
|
|
|
|
Parameters |
|
1786
|
|
|
|
|
|
|
|
|
1787
|
|
|
|
|
|
|
1 Path to a file to read |
|
1788
|
|
|
|
|
|
|
|
|
1789
|
|
|
|
|
|
|
A file readable by this function may be generated by using printpos with format as follows: |
|
1790
|
|
|
|
|
|
|
"%-41sinstrumentName %+6.2fsize %-9.2flevel ". |
|
1791
|
|
|
|
|
|
|
"%-9.2fbid £%-8.2fprofit %5.1fprofitpc%% £%10.2fatrisk\n", |
|
1792
|
|
|
|
|
|
|
|
|
1793
|
|
|
|
|
|
|
This file was originally generated to be human readable so reading by machine is a stretch. |
|
1794
|
|
|
|
|
|
|
|
|
1795
|
|
|
|
|
|
|
=head3 Status - downright broken (for you). Sorry! |
|
1796
|
|
|
|
|
|
|
|
|
1797
|
|
|
|
|
|
|
May contains print and die statements. Contaions hardcoded paths that will need to be |
|
1798
|
|
|
|
|
|
|
changed. |
|
1799
|
|
|
|
|
|
|
|
|
1800
|
|
|
|
|
|
|
=cut |
|
1801
|
|
|
|
|
|
|
################################################################################## |
|
1802
|
|
|
|
|
|
|
sub readfile_oldformat |
|
1803
|
|
|
|
|
|
|
{ |
|
1804
|
0
|
|
|
0
|
1
|
|
my ($self, $f,$writenewfile)=@_; |
|
1805
|
0
|
|
|
|
|
|
my $positions={}; |
|
1806
|
0
|
|
|
|
|
|
my $totalline; |
|
1807
|
0
|
|
|
|
|
|
$f="/home/mark/igrec/results/$f"; |
|
1808
|
0
|
0
|
|
|
|
|
open(F,$f) or die "cannot open $f"; |
|
1809
|
|
|
|
|
|
|
#Roku Inc +0.38 16501.00 21842.0 £2029.58 32.4% £ 8299.96 |
|
1810
|
0
|
|
|
|
|
|
my @fieldhashnames=qw(epic instrumentName size level bid profit profitpc atrisk); |
|
1811
|
0
|
|
|
|
|
|
while (<F>) |
|
1812
|
|
|
|
|
|
|
{ |
|
1813
|
0
|
|
|
|
|
|
my @fields; |
|
1814
|
0
|
|
|
|
|
|
my @names=@fieldhashnames; |
|
1815
|
0
|
|
|
|
|
|
my $position={}; |
|
1816
|
|
|
|
|
|
|
|
|
1817
|
0
|
|
|
|
|
|
chomp; |
|
1818
|
0
|
0
|
|
|
|
|
if (m/\|/) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1819
|
|
|
|
|
|
|
{ |
|
1820
|
0
|
|
|
|
|
|
die; |
|
1821
|
|
|
|
|
|
|
} |
|
1822
|
|
|
|
|
|
|
elsif (m/^Name/) |
|
1823
|
|
|
|
|
|
|
{ |
|
1824
|
0
|
|
|
|
|
|
s/[£%]//g; |
|
1825
|
0
|
|
|
|
|
|
@fields=split(/ +/); |
|
1826
|
0
|
|
|
|
|
|
unshift(@fields,'Epic'); |
|
1827
|
|
|
|
|
|
|
# print "#".join("\|",@fields)."\n"; |
|
1828
|
|
|
|
|
|
|
} |
|
1829
|
|
|
|
|
|
|
elsif (m/^Total/) |
|
1830
|
|
|
|
|
|
|
{ |
|
1831
|
0
|
|
|
|
|
|
$totalline=$_; |
|
1832
|
|
|
|
|
|
|
} |
|
1833
|
|
|
|
|
|
|
else |
|
1834
|
|
|
|
|
|
|
{ |
|
1835
|
0
|
|
|
|
|
|
my $name=substr($_,0,42); |
|
1836
|
0
|
|
|
|
|
|
my $line=substr($_,43); |
|
1837
|
0
|
|
|
|
|
|
$name=~s/ +$//; |
|
1838
|
0
|
|
|
|
|
|
$line=~s/[\$£%]//g; |
|
1839
|
0
|
|
|
|
|
|
@fields=split(/ +/,$line); |
|
1840
|
0
|
|
|
|
|
|
my $epic=$self->epicsearch($name); |
|
1841
|
0
|
|
|
|
|
|
unshift(@fields,$epic,$name); |
|
1842
|
|
|
|
|
|
|
#die "$line\n@fields\n@names"; |
|
1843
|
0
|
|
|
|
|
|
while (@names) |
|
1844
|
|
|
|
|
|
|
{ |
|
1845
|
0
|
|
|
|
|
|
$position->{shift(@names)}=shift(@fields); |
|
1846
|
|
|
|
|
|
|
} |
|
1847
|
0
|
|
|
|
|
|
$positions->{$epic}=$position; |
|
1848
|
|
|
|
|
|
|
} |
|
1849
|
|
|
|
|
|
|
} |
|
1850
|
|
|
|
|
|
|
# close F; |
|
1851
|
0
|
0
|
|
|
|
|
if ($writenewfile) |
|
1852
|
|
|
|
|
|
|
{ |
|
1853
|
0
|
|
|
|
|
|
$f=~s/results/r2/; |
|
1854
|
0
|
0
|
|
|
|
|
if (! -e $f) |
|
1855
|
|
|
|
|
|
|
{ |
|
1856
|
0
|
0
|
|
|
|
|
open(my $g,">" , $f) or die "Cannot open $f for write"; |
|
1857
|
0
|
|
|
|
|
|
my $format= "%sepic|%sinstrumentName|%0.2fsize|%-0.2flevel|". |
|
1858
|
|
|
|
|
|
|
"%-0.2fbid|£%-0.2fprofit|%0.1fprofitpc%%|£%0.2fatrisk\n", |
|
1859
|
|
|
|
|
|
|
print $g "Epic|Instrumentname|Size|Level|Bid|Profit£|Profitpc%|Atrisk£\n"; |
|
1860
|
0
|
|
|
|
|
|
my $a=$self->agg([values %$positions]); |
|
1861
|
0
|
|
|
|
|
|
for (@$a) |
|
1862
|
|
|
|
|
|
|
{ |
|
1863
|
0
|
|
|
|
|
|
$self->printpos($g,$_,$format); |
|
1864
|
|
|
|
|
|
|
} |
|
1865
|
0
|
|
|
|
|
|
print $g $totalline."\n"; |
|
1866
|
|
|
|
|
|
|
} |
|
1867
|
|
|
|
|
|
|
} |
|
1868
|
0
|
|
|
|
|
|
return $positions; |
|
1869
|
|
|
|
|
|
|
} |
|
1870
|
|
|
|
|
|
|
################################################################################## |
|
1871
|
|
|
|
|
|
|
# Reads am ascii file and returns a list of positions, |
|
1872
|
|
|
|
|
|
|
# a hashref keyed on epic. |
|
1873
|
|
|
|
|
|
|
################################################################################## |
|
1874
|
|
|
|
|
|
|
|
|
1875
|
|
|
|
|
|
|
=head2 readfile |
|
1876
|
|
|
|
|
|
|
|
|
1877
|
|
|
|
|
|
|
|
|
1878
|
|
|
|
|
|
|
Parameters |
|
1879
|
|
|
|
|
|
|
|
|
1880
|
|
|
|
|
|
|
1 Path to a file to read |
|
1881
|
|
|
|
|
|
|
|
|
1882
|
|
|
|
|
|
|
A file readable by this function may be generated by using printpos with format as follows: |
|
1883
|
|
|
|
|
|
|
"%sepic|%sinstrumentName|%0.2fsize|%-0.2flevel|". |
|
1884
|
|
|
|
|
|
|
"%-0.2fbid|£%-0.2fprofit|%0.1fprofitpc%%|£%0.2fatrisk|%smarketStatus\n", |
|
1885
|
|
|
|
|
|
|
|
|
1886
|
|
|
|
|
|
|
=head3 Status - downright broken (for you). Sorry! |
|
1887
|
|
|
|
|
|
|
|
|
1888
|
|
|
|
|
|
|
The function contains a hardcoded path for reading the files. You would need a |
|
1889
|
|
|
|
|
|
|
crontab entry to generate them. |
|
1890
|
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
May contain print and die statements. Contains hardcoded paths that will need to be |
|
1892
|
|
|
|
|
|
|
changed. |
|
1893
|
|
|
|
|
|
|
|
|
1894
|
|
|
|
|
|
|
=cut |
|
1895
|
|
|
|
|
|
|
################################################################################## |
|
1896
|
|
|
|
|
|
|
sub readfile |
|
1897
|
|
|
|
|
|
|
{ |
|
1898
|
0
|
|
|
0
|
1
|
|
my ($self,$f)=@_; |
|
1899
|
|
|
|
|
|
|
|
|
1900
|
0
|
|
|
|
|
|
my $positions={}; |
|
1901
|
0
|
|
|
|
|
|
$f="/home/mark/igrec/r2/$f"; |
|
1902
|
0
|
0
|
|
|
|
|
open(F,$f) or die "cannot open $f"; |
|
1903
|
0
|
|
|
|
|
|
my @fieldhashnames=qw(epic instrumentName size level bid profit profitpc atrisk tradeable); |
|
1904
|
0
|
|
|
|
|
|
my $ln=0; |
|
1905
|
0
|
|
|
|
|
|
while (<F>) |
|
1906
|
|
|
|
|
|
|
{ |
|
1907
|
0
|
|
|
|
|
|
my @fields; |
|
1908
|
0
|
|
|
|
|
|
my @names=@fieldhashnames; |
|
1909
|
0
|
|
|
|
|
|
my $position={}; |
|
1910
|
|
|
|
|
|
|
|
|
1911
|
0
|
|
|
|
|
|
$ln++; |
|
1912
|
0
|
|
|
|
|
|
chomp; |
|
1913
|
0
|
0
|
|
|
|
|
if (m/^Total/) |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
1914
|
|
|
|
|
|
|
{ |
|
1915
|
0
|
|
|
|
|
|
next; |
|
1916
|
|
|
|
|
|
|
} |
|
1917
|
|
|
|
|
|
|
elsif (m/ Positions$/) |
|
1918
|
|
|
|
|
|
|
{ |
|
1919
|
0
|
|
|
|
|
|
next; |
|
1920
|
|
|
|
|
|
|
} |
|
1921
|
|
|
|
|
|
|
elsif (m/^ *$/) |
|
1922
|
|
|
|
|
|
|
{ |
|
1923
|
0
|
|
|
|
|
|
next; |
|
1924
|
|
|
|
|
|
|
} |
|
1925
|
|
|
|
|
|
|
elsif (m/#/) |
|
1926
|
|
|
|
|
|
|
{ |
|
1927
|
0
|
|
|
|
|
|
next; |
|
1928
|
|
|
|
|
|
|
} |
|
1929
|
|
|
|
|
|
|
elsif (!m/\|/) |
|
1930
|
|
|
|
|
|
|
{ |
|
1931
|
0
|
|
|
|
|
|
die "No | lin line $ln file $f"; |
|
1932
|
|
|
|
|
|
|
} |
|
1933
|
|
|
|
|
|
|
elsif (m/Epic/) |
|
1934
|
|
|
|
|
|
|
{ |
|
1935
|
0
|
|
|
|
|
|
next; |
|
1936
|
|
|
|
|
|
|
} |
|
1937
|
|
|
|
|
|
|
else |
|
1938
|
|
|
|
|
|
|
{ |
|
1939
|
0
|
|
|
|
|
|
s/[£&]//g; |
|
1940
|
0
|
|
|
|
|
|
@fields=split(/\|/); |
|
1941
|
0
|
|
|
|
|
|
for my $fieldname (@fieldhashnames) |
|
1942
|
|
|
|
|
|
|
{ |
|
1943
|
0
|
0
|
|
|
|
|
die if (!defined $names[0]); |
|
1944
|
|
|
|
|
|
|
#print "names[0]=$names[0]\n"; |
|
1945
|
0
|
|
|
|
|
|
$position->{$fieldname}=shift(@fields); |
|
1946
|
|
|
|
|
|
|
} |
|
1947
|
0
|
|
|
|
|
|
$positions->{$position->{epic}}=$position; |
|
1948
|
0
|
|
0
|
|
|
|
$position->{marketStatus}//=''; # older files do not record this. |
|
1949
|
|
|
|
|
|
|
} |
|
1950
|
|
|
|
|
|
|
} |
|
1951
|
0
|
|
|
|
|
|
return $positions; |
|
1952
|
|
|
|
|
|
|
} |
|
1953
|
|
|
|
|
|
|
##################################################################### |
|
1954
|
|
|
|
|
|
|
# format strings contained embedded printf specifiers followed by |
|
1955
|
|
|
|
|
|
|
# a hash element name . |
|
1956
|
|
|
|
|
|
|
# |
|
1957
|
|
|
|
|
|
|
# eg "%sdate %sdescription %sepic %sstatus\n"; |
|
1958
|
|
|
|
|
|
|
# eg "%-20sdate %-30sdescription %-20sepic %-15sstatus\n"; |
|
1959
|
|
|
|
|
|
|
# eg |
|
1960
|
|
|
|
|
|
|
# "%sepic|%sinstrumentName|%6.2fsize|%-9.2flevel|". |
|
1961
|
|
|
|
|
|
|
# "%-9.2fbid|£%-8.2fprofit|%5.1fprofitpc%%|£%10.2fatrisk\n", |
|
1962
|
|
|
|
|
|
|
#eg |
|
1963
|
|
|
|
|
|
|
# "%-41sinstrumentName %+6.2fsize %-9.2flevel ". |
|
1964
|
|
|
|
|
|
|
# "%-9.2fbid £%-8.2fprofit %5.1fprofitpc%% £%10.2fatrisk\n", |
|
1965
|
|
|
|
|
|
|
# Arguments: |
|
1966
|
|
|
|
|
|
|
# 1) An IG object ref. (self) Is not really used. |
|
1967
|
|
|
|
|
|
|
# 2) Either "stdout" or an open writable file handle. |
|
1968
|
|
|
|
|
|
|
# 3) A hash possibly deep, with items. Ig the item is not found directly in the hash, |
|
1969
|
|
|
|
|
|
|
# the $self->fetch function is used for access. If still not found |
|
1970
|
|
|
|
|
|
|
# then "UNDEF" is printed. |
|
1971
|
|
|
|
|
|
|
# CHANGED to $self->uds |
|
1972
|
|
|
|
|
|
|
# OR: If this is an array ref, then a title line is ptinted using the format string |
|
1973
|
|
|
|
|
|
|
# and the referenced array of titles |
|
1974
|
|
|
|
|
|
|
# OR: If empty dtring ort undef, derive titles from the format |
|
1975
|
|
|
|
|
|
|
# string and print a title line. |
|
1976
|
|
|
|
|
|
|
# 4) A formatting string. Can contain text, containing embedded |
|
1977
|
|
|
|
|
|
|
# format instructions like %6.2fsize here %6.2f is a print f |
|
1978
|
|
|
|
|
|
|
# specifier and size is the name of the item to retrieve from the hash. |
|
1979
|
|
|
|
|
|
|
# 5,6) up /down can be percent gives green if > up, bold green if > 5*up. |
|
1980
|
|
|
|
|
|
|
# can be a coloration function of position. |
|
1981
|
|
|
|
|
|
|
# just one function, so no down ever. |
|
1982
|
|
|
|
|
|
|
# function takes argument position, and returns optional colors |
|
1983
|
|
|
|
|
|
|
##################################################################### |
|
1984
|
|
|
|
|
|
|
|
|
1985
|
|
|
|
|
|
|
=head2 printpos |
|
1986
|
|
|
|
|
|
|
|
|
1987
|
|
|
|
|
|
|
=head3 Parmeters |
|
1988
|
|
|
|
|
|
|
|
|
1989
|
|
|
|
|
|
|
A file handle or the word stdout, all output sent here. |
|
1990
|
|
|
|
|
|
|
|
|
1991
|
|
|
|
|
|
|
A hashref of items to print |
|
1992
|
|
|
|
|
|
|
OR: If this is an array ref, then a title line is ptinted using the format string |
|
1993
|
|
|
|
|
|
|
and the referenced array of titles |
|
1994
|
|
|
|
|
|
|
OR: If empty string or undef, derive titles from the format |
|
1995
|
|
|
|
|
|
|
string and print a title line. |
|
1996
|
|
|
|
|
|
|
|
|
1997
|
|
|
|
|
|
|
A formatting string. Can contain text, containing embedded |
|
1998
|
|
|
|
|
|
|
format instructions like %6.2fsize here %6.2f is a print f |
|
1999
|
|
|
|
|
|
|
specifier and size is the name of the item to retrieve from the hash. |
|
2000
|
|
|
|
|
|
|
|
|
2001
|
|
|
|
|
|
|
OPTIONAL up can be percent gives green if > up, bold green if > 5*up. |
|
2002
|
|
|
|
|
|
|
can be a coloration function of position. Just one function, so no down ever if a function is given |
|
2003
|
|
|
|
|
|
|
function takes argument position, and returns optional colors |
|
2004
|
|
|
|
|
|
|
|
|
2005
|
|
|
|
|
|
|
OPTIONAL down can be percent gives red if <down , bold red if < 5*down. |
|
2006
|
|
|
|
|
|
|
|
|
2007
|
|
|
|
|
|
|
=head3 Description |
|
2008
|
|
|
|
|
|
|
|
|
2009
|
|
|
|
|
|
|
This is a very general function will work with any hash. |
|
2010
|
|
|
|
|
|
|
|
|
2011
|
|
|
|
|
|
|
=cut |
|
2012
|
|
|
|
|
|
|
##################################################################### |
|
2013
|
|
|
|
|
|
|
sub printpos |
|
2014
|
|
|
|
|
|
|
{ |
|
2015
|
|
|
|
|
|
|
|
|
2016
|
0
|
|
|
0
|
1
|
|
my ($self,$out,$position,$format,$up,$down)=@_; |
|
2017
|
|
|
|
|
|
|
|
|
2018
|
0
|
|
|
|
|
|
my $colsub; |
|
2019
|
|
|
|
|
|
|
|
|
2020
|
0
|
0
|
|
|
|
|
$out=*STDOUT if ($out eq "stdout"); |
|
2021
|
|
|
|
|
|
|
|
|
2022
|
0
|
0
|
0
|
|
|
|
$down=-$up if (defined $up and ref($up) eq '' and !defined $down) ; |
|
|
|
|
0
|
|
|
|
|
|
2023
|
|
|
|
|
|
|
|
|
2024
|
0
|
0
|
0
|
|
|
|
if (defined $up and ref($up) ne 'CODE') |
|
2025
|
|
|
|
|
|
|
{ |
|
2026
|
|
|
|
|
|
|
$colsub=sub |
|
2027
|
|
|
|
|
|
|
{ |
|
2028
|
0
|
|
|
0
|
|
|
my ($position)=shift; |
|
2029
|
0
|
|
|
|
|
|
my $v1=$position->{dbid}; |
|
2030
|
0
|
|
|
|
|
|
my $col=''; |
|
2031
|
0
|
|
|
|
|
|
$v1=~s/%//; |
|
2032
|
0
|
0
|
0
|
|
|
|
$col=Green if (defined $up and $v1>$up); |
|
2033
|
0
|
0
|
0
|
|
|
|
$col=Red if (defined $down and $v1<$down); |
|
2034
|
0
|
0
|
0
|
|
|
|
$col=Green+Bold if (defined $up and $v1>$up*5); |
|
2035
|
0
|
0
|
0
|
|
|
|
$col=Red+Bold if (defined $down and $v1<5*$down); |
|
2036
|
0
|
|
|
|
|
|
return $col; |
|
2037
|
0
|
|
|
|
|
|
}; |
|
2038
|
|
|
|
|
|
|
} |
|
2039
|
0
|
0
|
0
|
|
|
|
$colsub=$up if (defined $up and ref($up) eq 'CODE'); |
|
2040
|
0
|
0
|
|
0
|
|
|
$colsub=sub {''} if (!defined $up); |
|
|
0
|
|
|
|
|
|
|
|
2041
|
|
|
|
|
|
|
|
|
2042
|
|
|
|
|
|
|
|
|
2043
|
0
|
|
|
|
|
|
my $titles=$format; |
|
2044
|
0
|
0
|
|
|
|
|
if (ref($position) eq 'ARRAY') # its titles to print! |
|
2045
|
|
|
|
|
|
|
{ |
|
2046
|
|
|
|
|
|
|
#$format=~s/%[-+]/%/g; |
|
2047
|
|
|
|
|
|
|
#print "$format\n"; |
|
2048
|
0
|
|
|
|
|
|
while ($format=~m/[-+]?([0-9]+)\.([0-9]+)/) |
|
2049
|
|
|
|
|
|
|
{ |
|
2050
|
0
|
|
|
|
|
|
my $x; |
|
2051
|
0
|
|
|
|
|
|
$x=$1; |
|
2052
|
0
|
0
|
|
|
|
|
abs($2)>abs($x) and $x=$2; |
|
2053
|
0
|
|
|
|
|
|
$format=~s/%([-+]?)([0-9]+)\.([0-9]+)/%$1$x/; |
|
2054
|
|
|
|
|
|
|
} |
|
2055
|
|
|
|
|
|
|
#print "#1 $format\n"; |
|
2056
|
0
|
|
|
|
|
|
$format=~s/%\+\+/%+/g; |
|
2057
|
|
|
|
|
|
|
#print "#2 $format\n"; |
|
2058
|
0
|
|
|
|
|
|
$format=~s/%([-\+]?[0-9]+)\.[0-9]+/%$1/g; |
|
2059
|
|
|
|
|
|
|
#print "#3 $format\n"; |
|
2060
|
0
|
|
|
|
|
|
$format=~s/%([-\+]?[0-9]+)[fd]/%$1s/g; |
|
2061
|
|
|
|
|
|
|
#print "#4 $format\n"; |
|
2062
|
0
|
|
|
|
|
|
$format=~s/%([-\+]?[0-9]*)([a-zA-Z_][a-zA-Z0-9_]*)/%$1s/g; |
|
2063
|
|
|
|
|
|
|
#die $format; |
|
2064
|
|
|
|
|
|
|
# print "$format\n"; exit; |
|
2065
|
|
|
|
|
|
|
#$"=":"; print "@$position\n"; |
|
2066
|
|
|
|
|
|
|
|
|
2067
|
|
|
|
|
|
|
|
|
2068
|
0
|
|
|
|
|
|
$format=~s/[\x82\x83\xc3]+//g; # so we get some strange characters like ÃÂ occuring in pairs. Not sure why. This removes them. |
|
2069
|
|
|
|
|
|
|
#$format="%-41s %+7s %11s %-10s £%-10s %5s%% £%12s %-9s %-4s"; |
|
2070
|
|
|
|
|
|
|
#print "$format\n"; #exit; |
|
2071
|
0
|
0
|
0
|
|
|
|
print $out Bold if ($self->col and defined $INC{'Term/Chrome.pm'}); |
|
2072
|
|
|
|
|
|
|
# print "format='$format' @$position\n"; |
|
2073
|
0
|
|
|
|
|
|
printf $out $format,@$position; |
|
2074
|
0
|
0
|
0
|
|
|
|
print $out Reset if ($self->col and defined $INC{'Term/Chrome.pm'}); |
|
2075
|
0
|
|
|
|
|
|
return; |
|
2076
|
|
|
|
|
|
|
} |
|
2077
|
|
|
|
|
|
|
|
|
2078
|
|
|
|
|
|
|
# auto generated title list from the names |
|
2079
|
0
|
0
|
0
|
|
|
|
if (!defined $position or $position eq '') |
|
2080
|
|
|
|
|
|
|
{ |
|
2081
|
0
|
|
|
|
|
|
$titles=~s/\n//g; |
|
2082
|
0
|
|
|
|
|
|
$titles=~s/%([-+0-9.]*)([sfd])/%/g; |
|
2083
|
0
|
|
|
|
|
|
$titles=~s/%%/__PC__/g; |
|
2084
|
0
|
|
|
|
|
|
$titles=~s/%//; # just one |
|
2085
|
0
|
|
|
|
|
|
$titles=~s/£%([a-zA-Z]+)/%$1£/g; |
|
2086
|
0
|
|
|
|
|
|
my @titles=split(/%/,$titles); |
|
2087
|
0
|
|
|
|
|
|
map {s/[|,]//g } @titles; |
|
|
0
|
|
|
|
|
|
|
|
2088
|
0
|
|
|
|
|
|
map {s/ +//g } @titles; |
|
|
0
|
|
|
|
|
|
|
|
2089
|
0
|
|
|
|
|
|
map { s/__PC__//g; } @titles; |
|
|
0
|
|
|
|
|
|
|
|
2090
|
0
|
|
|
|
|
|
map { s/([\w']+)/\u\L$1/g; } @titles; |
|
|
0
|
|
|
|
|
|
|
|
2091
|
0
|
|
|
|
|
|
while ($format=~m/%[-+]?([0-9]+)\.([0-9]+)/) |
|
2092
|
|
|
|
|
|
|
{ |
|
2093
|
0
|
|
|
|
|
|
my $x; |
|
2094
|
|
|
|
|
|
|
#my $x=$1+$2; |
|
2095
|
0
|
|
|
|
|
|
$x=$1; |
|
2096
|
0
|
0
|
|
|
|
|
$2>$x and $x=$2; |
|
2097
|
0
|
|
|
|
|
|
$format=~s/%([-+]?)([0-9]+)\.([0-9]+)/%$1$x/; |
|
2098
|
|
|
|
|
|
|
} |
|
2099
|
0
|
|
|
|
|
|
$format=~s/(%[-+0-9.]*)[a-zA-Z]+/$1s/g; |
|
2100
|
|
|
|
|
|
|
#$format=~s/(%[-+0-9]+)\.[0-9]+/$1/g; |
|
2101
|
0
|
|
|
|
|
|
$format=~s/£//g; |
|
2102
|
|
|
|
|
|
|
#die "format=$format titles=@titles"; |
|
2103
|
0
|
|
|
|
|
|
$format=~s/[\x82\x83\xc3]+//g; # so we get some strange characters like ÃÂ occuring in pairs. Not sure why. This removes them. |
|
2104
|
0
|
0
|
0
|
|
|
|
print $out Bold if ($self->col and defined $INC{'Term/Chrome.pm'}); |
|
2105
|
0
|
|
|
|
|
|
printf $out $format, @titles; |
|
2106
|
0
|
0
|
0
|
|
|
|
print $out Reset if ($self->col and defined $INC{'Term/Chrome.pm'}); |
|
2107
|
0
|
|
|
|
|
|
return; |
|
2108
|
|
|
|
|
|
|
} |
|
2109
|
|
|
|
|
|
|
|
|
2110
|
|
|
|
|
|
|
|
|
2111
|
|
|
|
|
|
|
|
|
2112
|
|
|
|
|
|
|
# $p=$position->{position}; |
|
2113
|
|
|
|
|
|
|
# $m=$position->{market}; |
|
2114
|
|
|
|
|
|
|
|
|
2115
|
0
|
|
|
|
|
|
$format=~s/%%/##/g; |
|
2116
|
|
|
|
|
|
|
|
|
2117
|
|
|
|
|
|
|
|
|
2118
|
|
|
|
|
|
|
# while (($format=~s/%([-+0-9]+\.[0-9]+)([a-z][a-zA-Z0-9]*)/%$1__S__/) || ($format=~s/%([-+0-9]*)([a-z][a-zA-Z0-9]*)/%$1__F__/)) |
|
2119
|
|
|
|
|
|
|
# { |
|
2120
|
|
|
|
|
|
|
# my $s; |
|
2121
|
|
|
|
|
|
|
# $s=$activity->{$2}; |
|
2122
|
|
|
|
|
|
|
# my $pos=$1; |
|
2123
|
|
|
|
|
|
|
# $pos=~s/-//; |
|
2124
|
|
|
|
|
|
|
# $s=substr($s,0,$pos) if (defined(pos) and $pos ne '' and $pos<length($s)); |
|
2125
|
|
|
|
|
|
|
# push(@args,$s); |
|
2126
|
|
|
|
|
|
|
# } |
|
2127
|
|
|
|
|
|
|
|
|
2128
|
0
|
|
|
|
|
|
my $col=''; |
|
2129
|
0
|
|
|
|
|
|
while ($format=~s/%([-+0-9.]*[dsf])([a-zA-Z_][a-zA-Z0-9_]*)/%s/) |
|
2130
|
|
|
|
|
|
|
{ |
|
2131
|
0
|
|
|
|
|
|
my $s; |
|
2132
|
|
|
|
|
|
|
|
|
2133
|
0
|
|
|
|
|
|
my $item=$2; |
|
2134
|
0
|
|
0
|
|
|
|
my $len=$1//""; |
|
2135
|
|
|
|
|
|
|
# die "item is UNDEF" if ($item eq 'UNDEF'); |
|
2136
|
|
|
|
|
|
|
# die "len is UNDEF" if ($len eq 'UNDEF'); |
|
2137
|
|
|
|
|
|
|
# $len='' if ($len eq 'UNDEF'); |
|
2138
|
0
|
0
|
|
|
|
|
$len="%".$len if ($len); |
|
2139
|
0
|
0
|
0
|
|
|
|
if (defined $item and $item ne '' and exists $position->{$item} and defined $position->{$item}) |
|
|
|
0
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
2140
|
|
|
|
|
|
|
{ |
|
2141
|
0
|
|
|
|
|
|
$position->{$item}=~s/%//g; |
|
2142
|
|
|
|
|
|
|
#$position->{$item}='0' if ($position->{$item} eq 'UNDEF'); |
|
2143
|
0
|
|
|
|
|
|
$s=sprintf($len,$position->{$item}); |
|
2144
|
0
|
0
|
0
|
|
|
|
if ($item eq 'dbid' and exists $INC{'Term/Chrome.pm'} and $self->col) |
|
|
|
|
0
|
|
|
|
|
|
2145
|
|
|
|
|
|
|
{ |
|
2146
|
|
|
|
|
|
|
##my $v1=$position->{dbid}; |
|
2147
|
|
|
|
|
|
|
##$v1=~s/%//; |
|
2148
|
|
|
|
|
|
|
##$col=Green if (defined $up and $v1>$up); |
|
2149
|
|
|
|
|
|
|
##$col=Red if (defined $down and $v1<$down); |
|
2150
|
|
|
|
|
|
|
##$col=Green+Bold if (defined $up and $v1>$up*5); |
|
2151
|
|
|
|
|
|
|
##$col=Red+Bold if (defined $down and $v1<5*$down); |
|
2152
|
|
|
|
|
|
|
|
|
2153
|
|
|
|
|
|
|
# $col=&$colsub($position); |
|
2154
|
|
|
|
|
|
|
} |
|
2155
|
|
|
|
|
|
|
# $col=Yellow if (defined $up); |
|
2156
|
|
|
|
|
|
|
# $col=&$colsub($position); |
|
2157
|
|
|
|
|
|
|
} |
|
2158
|
|
|
|
|
|
|
elsif (defined $self->fetch($position,$item)) |
|
2159
|
|
|
|
|
|
|
{ |
|
2160
|
|
|
|
|
|
|
#$s=sprintf($len,$self->fetch($position,$2)//"UNDEF"); |
|
2161
|
0
|
|
0
|
|
|
|
$s=sprintf($len,$self->fetch($position,$item)//$self->uds); |
|
2162
|
0
|
0
|
0
|
|
|
|
if ($item eq 'dbid' and defined $INC{'Term/Chrome.pm'} and $self->col) |
|
|
|
|
0
|
|
|
|
|
|
2163
|
|
|
|
|
|
|
{ |
|
2164
|
|
|
|
|
|
|
#my $v1; |
|
2165
|
|
|
|
|
|
|
|
|
2166
|
|
|
|
|
|
|
#$v1=$self->fetch($position,'dbid'); |
|
2167
|
|
|
|
|
|
|
#$v1=~s/%//; |
|
2168
|
|
|
|
|
|
|
#$v1=100*$v1/$self->fetch($position,'bid'); |
|
2169
|
|
|
|
|
|
|
###$col=Green if (defined $up and $self->col and $self->fetch($position,'dbid')/$self->fetch($position,'bid')>$up/100); |
|
2170
|
|
|
|
|
|
|
###$col=Red if (defined $down and $self->col and $self->fetch($position,'dbid')/$self->fetch($position,'bid')<$down/100); |
|
2171
|
|
|
|
|
|
|
#$col=Green if (defined $up and $self->col and $v1>$up); |
|
2172
|
|
|
|
|
|
|
#$col=Red if (defined $down and $self->col and $v1<$down); |
|
2173
|
|
|
|
|
|
|
#$col=Green+Bold if (defined $up and $self->col and $v1>$up*5); |
|
2174
|
|
|
|
|
|
|
#$col=Red+Bold if (defined $down and $self->col and $v1<5*$down); |
|
2175
|
|
|
|
|
|
|
#$col=&$colsub($position); |
|
2176
|
|
|
|
|
|
|
} |
|
2177
|
|
|
|
|
|
|
#$col=&$colsub($position); |
|
2178
|
|
|
|
|
|
|
|
|
2179
|
|
|
|
|
|
|
} |
|
2180
|
|
|
|
|
|
|
else |
|
2181
|
|
|
|
|
|
|
{ |
|
2182
|
0
|
|
|
|
|
|
$len=~s/[df]/s/; |
|
2183
|
0
|
|
|
|
|
|
$len=~s/\.[0-9]+//; |
|
2184
|
|
|
|
|
|
|
#$s=sprintf($len,"UNDEF"); |
|
2185
|
0
|
|
|
|
|
|
$s=sprintf($len,$self->uds); |
|
2186
|
|
|
|
|
|
|
} |
|
2187
|
|
|
|
|
|
|
|
|
2188
|
0
|
|
|
|
|
|
$col=&$colsub($position); |
|
2189
|
0
|
|
|
|
|
|
$len=~s/[dsf]$//; |
|
2190
|
0
|
0
|
|
|
|
|
if ($len ne '') # len can be something like 0.2 |
|
2191
|
|
|
|
|
|
|
{ |
|
2192
|
0
|
|
|
|
|
|
$len=~s/%//; |
|
2193
|
0
|
0
|
|
|
|
|
$len=abs($len) if ($len ne ''); |
|
2194
|
0
|
0
|
0
|
|
|
|
$s=substr($s,0,$len) if ($len and $len<length($s) and $len>=1); |
|
|
|
|
0
|
|
|
|
|
|
2195
|
|
|
|
|
|
|
} |
|
2196
|
|
|
|
|
|
|
|
|
2197
|
0
|
|
|
|
|
|
$format=~s/%s/$s/; |
|
2198
|
|
|
|
|
|
|
} |
|
2199
|
|
|
|
|
|
|
|
|
2200
|
0
|
0
|
0
|
|
|
|
$col=&$colsub($position)//'' if ($self->col and defined $INC{'Term/Chrome.pm'}); |
|
|
|
|
0
|
|
|
|
|
|
2201
|
0
|
|
|
|
|
|
$format=~s/##/%/g; |
|
2202
|
0
|
|
|
|
|
|
$format=~s/£-/-£/g; |
|
2203
|
0
|
|
|
|
|
|
$format=~s/[\x82\x83\xc3]+//g; # so we get some strange characters like ÃÂ occuring in pairs. Not sure why. This removes them. |
|
2204
|
0
|
|
|
|
|
|
print $out $col, $format; |
|
2205
|
0
|
0
|
|
|
|
|
if (ref($col) ne '') |
|
2206
|
0
|
|
|
|
|
|
{ print $out Reset; |
|
2207
|
|
|
|
|
|
|
} |
|
2208
|
|
|
|
|
|
|
|
|
2209
|
|
|
|
|
|
|
} |
|
2210
|
|
|
|
|
|
|
|
|
2211
|
|
|
|
|
|
|
|
|
2212
|
|
|
|
|
|
|
|
|
2213
|
|
|
|
|
|
|
=head2 sortrange |
|
2214
|
|
|
|
|
|
|
|
|
2215
|
|
|
|
|
|
|
=head3 Parameters |
|
2216
|
|
|
|
|
|
|
|
|
2217
|
|
|
|
|
|
|
Ref to an array containing dates in printed ascii format. |
|
2218
|
|
|
|
|
|
|
|
|
2219
|
|
|
|
|
|
|
If there are no dates or an empty array, an empty string is returned. |
|
2220
|
|
|
|
|
|
|
|
|
2221
|
|
|
|
|
|
|
If there is one date, then that date is returned |
|
2222
|
|
|
|
|
|
|
|
|
2223
|
|
|
|
|
|
|
If there is more than one then the first and last after sorting is returned, with a dash between them. |
|
2224
|
|
|
|
|
|
|
|
|
2225
|
|
|
|
|
|
|
This is used in aggregation of positions and relates to creation dates with multiple positions |
|
2226
|
|
|
|
|
|
|
in the same security purchased at different times. |
|
2227
|
|
|
|
|
|
|
|
|
2228
|
|
|
|
|
|
|
=cut |
|
2229
|
|
|
|
|
|
|
|
|
2230
|
|
|
|
|
|
|
sub sortrange |
|
2231
|
|
|
|
|
|
|
{ |
|
2232
|
0
|
|
|
0
|
1
|
|
my ($self,$ar)=@_; |
|
2233
|
|
|
|
|
|
|
|
|
2234
|
0
|
|
|
|
|
|
my @dates=sort @$ar; |
|
2235
|
|
|
|
|
|
|
|
|
2236
|
0
|
0
|
|
|
|
|
return '' if (@dates==0); |
|
2237
|
0
|
0
|
|
|
|
|
return $dates[0] if (@dates==1); |
|
2238
|
0
|
|
|
|
|
|
return $dates[0] . "-".$dates[-1]; |
|
2239
|
|
|
|
|
|
|
} |
|
2240
|
|
|
|
|
|
|
|
|
2241
|
|
|
|
|
|
|
|
|
2242
|
|
|
|
|
|
|
|
|
2243
|
|
|
|
|
|
|
=head2 Red Blue Bold Reset Underline Green color |
|
2244
|
|
|
|
|
|
|
|
|
2245
|
|
|
|
|
|
|
=head3 Description |
|
2246
|
|
|
|
|
|
|
|
|
2247
|
|
|
|
|
|
|
The above parameterless functions are provided if Term::Chrome is not available. They are "do nothing" subs provided to satisfy references only. |
|
2248
|
|
|
|
|
|
|
|
|
2249
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
|
2250
|
|
|
|
|
|
|
|
|
2251
|
|
|
|
|
|
|
Moose |
|
2252
|
|
|
|
|
|
|
Term::Chrom if available. |
|
2253
|
|
|
|
|
|
|
|
|
2254
|
|
|
|
|
|
|
=head1 UTILITIES |
|
2255
|
|
|
|
|
|
|
|
|
2256
|
|
|
|
|
|
|
A more complete position lister is given as igdisp.pl |
|
2257
|
|
|
|
|
|
|
|
|
2258
|
|
|
|
|
|
|
=head1 AUTHOR |
|
2259
|
|
|
|
|
|
|
|
|
2260
|
|
|
|
|
|
|
Mark Winder, C<< <markwin at cpan.org> >> |
|
2261
|
|
|
|
|
|
|
|
|
2262
|
|
|
|
|
|
|
=head1 BUGS |
|
2263
|
|
|
|
|
|
|
|
|
2264
|
|
|
|
|
|
|
Please report any bugs or feature requests to C<bug-finance-ig at rt.cpan.org>, or through |
|
2265
|
|
|
|
|
|
|
the web interface at L<https://rt.cpan.org/NoAuth/ReportBug.html?Queue=Finance-IG>. I will be notified, and then you'll |
|
2266
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
|
2267
|
|
|
|
|
|
|
|
|
2268
|
|
|
|
|
|
|
|
|
2269
|
|
|
|
|
|
|
|
|
2270
|
|
|
|
|
|
|
|
|
2271
|
|
|
|
|
|
|
=head1 SUPPORT |
|
2272
|
|
|
|
|
|
|
|
|
2273
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
|
2274
|
|
|
|
|
|
|
|
|
2275
|
|
|
|
|
|
|
perldoc Finance::IG |
|
2276
|
|
|
|
|
|
|
|
|
2277
|
|
|
|
|
|
|
|
|
2278
|
|
|
|
|
|
|
You can also look for information at: |
|
2279
|
|
|
|
|
|
|
|
|
2280
|
|
|
|
|
|
|
=over 4 |
|
2281
|
|
|
|
|
|
|
|
|
2282
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker (report bugs here) |
|
2283
|
|
|
|
|
|
|
|
|
2284
|
|
|
|
|
|
|
L<https://rt.cpan.org/NoAuth/Bugs.html?Dist=Finance-IG> |
|
2285
|
|
|
|
|
|
|
|
|
2286
|
|
|
|
|
|
|
=item * CPAN Ratings |
|
2287
|
|
|
|
|
|
|
|
|
2288
|
|
|
|
|
|
|
L<https://cpanratings.perl.org/d/Finance-IG> |
|
2289
|
|
|
|
|
|
|
|
|
2290
|
|
|
|
|
|
|
=item * Search CPAN |
|
2291
|
|
|
|
|
|
|
|
|
2292
|
|
|
|
|
|
|
L<https://metacpan.org/release/Finance-IG> |
|
2293
|
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
=back |
|
2295
|
|
|
|
|
|
|
|
|
2296
|
|
|
|
|
|
|
|
|
2297
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
|
2298
|
|
|
|
|
|
|
|
|
2299
|
|
|
|
|
|
|
=head1 FURTHER READING |
|
2300
|
|
|
|
|
|
|
|
|
2301
|
|
|
|
|
|
|
IG REST API Reference https://labs.ig.com/rest-trading-api-reference |
|
2302
|
|
|
|
|
|
|
|
|
2303
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
|
2304
|
|
|
|
|
|
|
|
|
2305
|
|
|
|
|
|
|
This software is Copyright (c) 2020 by Mark Winder. |
|
2306
|
|
|
|
|
|
|
|
|
2307
|
|
|
|
|
|
|
This is free software, licensed under: |
|
2308
|
|
|
|
|
|
|
|
|
2309
|
|
|
|
|
|
|
The Artistic License 2.0 (GPL Compatible) |
|
2310
|
|
|
|
|
|
|
|
|
2311
|
|
|
|
|
|
|
|
|
2312
|
|
|
|
|
|
|
=cut |
|
2313
|
|
|
|
|
|
|
|
|
2314
|
|
|
|
|
|
|
1; # End of Finance::IG |