jgtfx2console 0.4.19__py3-none-any.whl → 0.4.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jgtfx2console/LiveChartDataExport.py +428 -0
- jgtfx2console/__init__.py +1 -1
- jgtfx2console/config_generator.py +74 -0
- {jgtfx2console-0.4.19.dist-info → jgtfx2console-0.4.20.dist-info}/METADATA +1 -1
- {jgtfx2console-0.4.19.dist-info → jgtfx2console-0.4.20.dist-info}/RECORD +9 -7
- jgtfx2console-0.4.20.dist-info/entry_points.txt +4 -0
- jgtfx2console-0.4.19.dist-info/entry_points.txt +0 -4
- {jgtfx2console-0.4.19.dist-info → jgtfx2console-0.4.20.dist-info}/LICENSE +0 -0
- {jgtfx2console-0.4.19.dist-info → jgtfx2console-0.4.20.dist-info}/WHEEL +0 -0
- {jgtfx2console-0.4.19.dist-info → jgtfx2console-0.4.20.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,428 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import time
|
3
|
+
import argparse
|
4
|
+
import os
|
5
|
+
import sys
|
6
|
+
|
7
|
+
from jgtutils import jgtcommon
|
8
|
+
|
9
|
+
|
10
|
+
path_from_where_we_call = os.getcwd()
|
11
|
+
|
12
|
+
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
|
13
|
+
|
14
|
+
c_script_dir_name = os.path.dirname(__file__)
|
15
|
+
abs_path_of_this_file = os.path.abspath(c_script_dir_name)
|
16
|
+
print("abs_path_of_this_file: "+abs_path_of_this_file)
|
17
|
+
print("c_script_dir_name: "+c_script_dir_name)
|
18
|
+
path1 = os.path.join(c_script_dir_name, '.')
|
19
|
+
print("path1: "+path1)
|
20
|
+
abs_path1 = os.path.abspath(path1)
|
21
|
+
print("abs_path1: "+abs_path1)
|
22
|
+
sys.path.append(abs_path1)
|
23
|
+
#cd to : abs_path_of_this_file #@STCIssue Wont go further that CONNECTED if we are not in that directory.
|
24
|
+
os.chdir(abs_path_of_this_file)
|
25
|
+
|
26
|
+
from forexconnect import fxcorepy, ForexConnect
|
27
|
+
from forexconnect import Common, LiveHistoryCreator
|
28
|
+
|
29
|
+
from dateutil import parser
|
30
|
+
|
31
|
+
import xml.etree.ElementTree as ET
|
32
|
+
|
33
|
+
import common_samples
|
34
|
+
import csv
|
35
|
+
import pytz
|
36
|
+
from datetime import timedelta, datetime
|
37
|
+
|
38
|
+
delimiter = None
|
39
|
+
datetime_separator = None
|
40
|
+
format_decimal_places = None
|
41
|
+
timezone = None
|
42
|
+
|
43
|
+
|
44
|
+
def parse_args():
|
45
|
+
arg_parser = argparse.ArgumentParser(description='Process command parameters.')
|
46
|
+
arg_parser.add_argument('-p',
|
47
|
+
metavar="PASSWORD",
|
48
|
+
required=False,
|
49
|
+
help='Your password.')
|
50
|
+
arg_parser.add_argument('-config', metavar="CONFIG_FILE", default='jgtfxliveconfig.xml',
|
51
|
+
help='Config file')
|
52
|
+
|
53
|
+
args = arg_parser.parse_args()
|
54
|
+
return args
|
55
|
+
|
56
|
+
|
57
|
+
verbose = 0
|
58
|
+
order_request_id = ""
|
59
|
+
|
60
|
+
|
61
|
+
class SaveNewBar:
|
62
|
+
def __init__(self, symbol, bars):
|
63
|
+
self.symbol = symbol
|
64
|
+
self.bars = bars
|
65
|
+
|
66
|
+
def save_bar(self, instrument, history, filename):
|
67
|
+
global delimiter
|
68
|
+
global datetime_separator
|
69
|
+
global format_decimal_places
|
70
|
+
global timezone
|
71
|
+
|
72
|
+
if timezone == 'Local':
|
73
|
+
time_difference = -time.timezone
|
74
|
+
else:
|
75
|
+
tz = pytz.timezone(timezone)
|
76
|
+
time_difference = tz.utcoffset(datetime.now).total_seconds()
|
77
|
+
|
78
|
+
hist = history[:-1].tail(1)
|
79
|
+
with open(filename, "a", newline="") as file:
|
80
|
+
writer = csv.writer(file, delimiter=delimiter)
|
81
|
+
last_complete_data_frame = hist
|
82
|
+
dtime = str(last_complete_data_frame.index.values[0])
|
83
|
+
|
84
|
+
dtime = dtime.replace('T', ' ')
|
85
|
+
dt = parser.parse(dtime)
|
86
|
+
dt = dt+timedelta(0, time_difference)
|
87
|
+
dt = str(dt)
|
88
|
+
dtime = dt.replace(' ', datetime_separator)
|
89
|
+
out = [dtime]
|
90
|
+
str_prices = dtime + ", "
|
91
|
+
for price_name in last_complete_data_frame:
|
92
|
+
price_entry = last_complete_data_frame.get(price_name)
|
93
|
+
price_value = price_entry.values[0]
|
94
|
+
str_prices += price_name + "=" + str(price_value) + ", "
|
95
|
+
price = price_entry.values[0]
|
96
|
+
if format_decimal_places and price_name != "Volume":
|
97
|
+
if instrument.find("JPY") >= 0:
|
98
|
+
out.append("%.3f" % price)
|
99
|
+
else:
|
100
|
+
out.append("%.5f" % price)
|
101
|
+
else:
|
102
|
+
out.append(price)
|
103
|
+
writer.writerow(out)
|
104
|
+
file.close()
|
105
|
+
print("New bar saved to "+filename+": "+str_prices[0: -2])
|
106
|
+
|
107
|
+
return
|
108
|
+
|
109
|
+
|
110
|
+
order_created_count = 0
|
111
|
+
|
112
|
+
|
113
|
+
def find_in_tree(tree, node):
|
114
|
+
found = tree.find(node)
|
115
|
+
if found is None:
|
116
|
+
found = []
|
117
|
+
return found
|
118
|
+
|
119
|
+
|
120
|
+
def on_request_completed(request_id, response):
|
121
|
+
del request_id, response
|
122
|
+
global order_created_count
|
123
|
+
order_created_count += 1
|
124
|
+
return True
|
125
|
+
|
126
|
+
|
127
|
+
def on_changed(live_history, instrument):
|
128
|
+
def _on_changed(table_listener, row_id, row):
|
129
|
+
del table_listener, row_id
|
130
|
+
try:
|
131
|
+
if row.table_type == fxcorepy.O2GTableType.OFFERS and row.instrument == instrument:
|
132
|
+
live_history.add_or_update(row)
|
133
|
+
except Exception as e:
|
134
|
+
common_samples.print_exception(e)
|
135
|
+
return
|
136
|
+
|
137
|
+
return _on_changed
|
138
|
+
|
139
|
+
|
140
|
+
def on_bar_added(instrument, filename):
|
141
|
+
def _on_bar_added(history):
|
142
|
+
snb = SaveNewBar(instrument, history[:-1])
|
143
|
+
snb.save_bar(instrument, history, filename)
|
144
|
+
|
145
|
+
return _on_bar_added
|
146
|
+
|
147
|
+
|
148
|
+
def session_status_changed(fx, live_history, instrument, str_user_id, str_password, str_url, str_connection,
|
149
|
+
reconnect_on_disconnected):
|
150
|
+
offers_listener = None
|
151
|
+
first_call = reconnect_on_disconnected
|
152
|
+
orders_listener = None
|
153
|
+
|
154
|
+
def _session_status_changed(session, status):
|
155
|
+
nonlocal offers_listener
|
156
|
+
nonlocal first_call
|
157
|
+
nonlocal orders_listener
|
158
|
+
if not first_call:
|
159
|
+
common_samples.session_status_changed(session.trading_session_descriptors, status)
|
160
|
+
else:
|
161
|
+
first_call = False
|
162
|
+
if status == fxcorepy.AO2GSessionStatus.O2GSessionStatus.CONNECTED:
|
163
|
+
offers = fx.get_table(ForexConnect.OFFERS)
|
164
|
+
if live_history is not None:
|
165
|
+
on_changed_callback = on_changed(live_history, instrument)
|
166
|
+
offers_listener = Common.subscribe_table_updates(offers, on_change_callback=on_changed_callback)
|
167
|
+
elif status == fxcorepy.AO2GSessionStatus.O2GSessionStatus.DISCONNECTING or \
|
168
|
+
status == fxcorepy.AO2GSessionStatus.O2GSessionStatus.RECONNECTING or \
|
169
|
+
status == fxcorepy.AO2GSessionStatus.O2GSessionStatus.SESSION_LOST:
|
170
|
+
if orders_listener is not None:
|
171
|
+
orders_listener.unsubscribe()
|
172
|
+
orders_listener = None
|
173
|
+
if offers_listener is not None:
|
174
|
+
offers_listener.unsubscribe()
|
175
|
+
offers_listener = None
|
176
|
+
elif status == fxcorepy.AO2GSessionStatus.O2GSessionStatus.DISCONNECTED and reconnect_on_disconnected:
|
177
|
+
fx.session.login(str_user_id, str_password, str_url, str_connection)
|
178
|
+
|
179
|
+
return _session_status_changed
|
180
|
+
|
181
|
+
|
182
|
+
def check_params(instr, tf, offer):
|
183
|
+
if not instr:
|
184
|
+
raise Exception(
|
185
|
+
"The instrument is empty")
|
186
|
+
|
187
|
+
if not tf:
|
188
|
+
raise Exception(
|
189
|
+
"The timeframe is empty")
|
190
|
+
|
191
|
+
if not offer:
|
192
|
+
raise Exception(
|
193
|
+
"The instrument '{0}' is not valid".format(instr))
|
194
|
+
|
195
|
+
|
196
|
+
def parse_xml(config_file):
|
197
|
+
try:
|
198
|
+
jgtconf=jgtcommon.readconfig()
|
199
|
+
#print(jgtconf)
|
200
|
+
except:
|
201
|
+
raise Exception(
|
202
|
+
"The JGT configuration file '$HOME/config.json' does not exist")
|
203
|
+
try:
|
204
|
+
os.stat(config_file)
|
205
|
+
except OSError:
|
206
|
+
raise Exception(
|
207
|
+
"The configuration file '{0}' does not exist".format(config_file))
|
208
|
+
|
209
|
+
xmlfile = open(config_file, "r")
|
210
|
+
conf = ET.parse(xmlfile)
|
211
|
+
root = conf.getroot()
|
212
|
+
|
213
|
+
settings = find_in_tree(root, "Settings")
|
214
|
+
|
215
|
+
|
216
|
+
str_user_id = find_in_tree(settings, "Login").text
|
217
|
+
if str_user_id == '0' or str_user_id == '' or str_user_id is None:
|
218
|
+
str_user_id =jgtconf.get('user_id')
|
219
|
+
|
220
|
+
str_url = find_in_tree(settings, "Url").text
|
221
|
+
str_connection = find_in_tree(settings, "Connection").text
|
222
|
+
if str_connection == '' or str_connection is None or str_connection=='0':
|
223
|
+
str_connection = os.getenv('connection') or jgtconf.get('connection')
|
224
|
+
str_session_id = find_in_tree(settings, "SessionID").text
|
225
|
+
str_pin = find_in_tree(settings, "Pin").text
|
226
|
+
delim = find_in_tree(settings, "Delimiter").text
|
227
|
+
output_dir = find_in_tree(settings, "OutputDir").text
|
228
|
+
#test if our path is relative
|
229
|
+
if output_dir and output_dir[0] != '/':
|
230
|
+
output_dir = os.path.join(path_from_where_we_call, output_dir)
|
231
|
+
#print("relative output_dir modified: "+output_dir)
|
232
|
+
if output_dir == '' or output_dir is None or output_dir=='0':
|
233
|
+
output_dir = os.getenv('JGTPY_DATA') or jgtconf.get('JGTPY_DATA')
|
234
|
+
dt_separator = find_in_tree(settings, "DateTimeSeparator").text
|
235
|
+
fdp = find_in_tree(settings, "FormatDecimalPlaces").text
|
236
|
+
tzone = find_in_tree(settings, "Timezone").text
|
237
|
+
|
238
|
+
if tzone != 'EST' and tzone != 'UTC' and tzone != 'Local':
|
239
|
+
print('Timezone is not recognized, using EST')
|
240
|
+
tzone = 'UTC' #Default timezone
|
241
|
+
|
242
|
+
print("=============================")
|
243
|
+
print("User ID: " + str_user_id)
|
244
|
+
print("URL: " + str_url)
|
245
|
+
print("Connection: " + str_connection)
|
246
|
+
#print("Session ID: " + str_session_id)
|
247
|
+
#print("Pin: " + str_pin)
|
248
|
+
print("Delimiter: " + delim)
|
249
|
+
print("Output Directory: " + output_dir)
|
250
|
+
print("DateTime Separator: " + dt_separator)
|
251
|
+
print("Format Decimal Places: " + fdp)
|
252
|
+
print("Timezone: " + tzone)
|
253
|
+
|
254
|
+
print("=============================")
|
255
|
+
|
256
|
+
if output_dir:
|
257
|
+
if not os.path.exists(output_dir):
|
258
|
+
raise Exception(
|
259
|
+
"The output directory '{0}' does not exist. (try writting an absolute path in the config or use 0 so it will read the JGTPY_DATA environment variable)".format(output_dir))
|
260
|
+
|
261
|
+
if fdp == "Y" or fdp == "y":
|
262
|
+
fdp = True
|
263
|
+
else:
|
264
|
+
fdp = False
|
265
|
+
|
266
|
+
data = []
|
267
|
+
|
268
|
+
for elem in settings.findall("History"):
|
269
|
+
data2 = [find_in_tree(elem, "Instrument").text]
|
270
|
+
data2.append(find_in_tree(elem, "Timeframe").text)
|
271
|
+
if output_dir:
|
272
|
+
ifn = find_in_tree(elem, "Filename").text
|
273
|
+
data2.append(os.path.join(output_dir, ifn))
|
274
|
+
else:
|
275
|
+
data2.append(find_in_tree(elem, "Filename").text)
|
276
|
+
data2.append(find_in_tree(elem, "NumBars").text)
|
277
|
+
data2.append(find_in_tree(elem, "Headers").text)
|
278
|
+
data.append(data2)
|
279
|
+
|
280
|
+
if len(data) == 0:
|
281
|
+
raise Exception(
|
282
|
+
"No instruments in the config file are present")
|
283
|
+
|
284
|
+
return str_user_id, str_url, str_connection, str_session_id, str_pin, \
|
285
|
+
delim, output_dir, dt_separator, fdp, tzone, data
|
286
|
+
|
287
|
+
|
288
|
+
def set_init_history(fx, lhc, instr, tf, filename, numbars, str_user_id, str_password, str_url, str_connection):
|
289
|
+
lhc.append(LiveHistoryCreator(tf))
|
290
|
+
last_index = len(lhc)-1
|
291
|
+
on_bar_added_callback = on_bar_added(instr, filename)
|
292
|
+
lhc[last_index].subscribe(on_bar_added_callback)
|
293
|
+
session_status_changed_callback = session_status_changed(fx, lhc[last_index], instr, str_user_id,
|
294
|
+
str_password, str_url, str_connection, True)
|
295
|
+
session_status_changed_callback(fx.session, fx.session.session_status)
|
296
|
+
fx.set_session_status_listener(session_status_changed_callback)
|
297
|
+
|
298
|
+
nd_array_history = fx.get_history(instr, tf, None, None, int(numbars)+2)
|
299
|
+
|
300
|
+
lhc[last_index].history = nd_array_history
|
301
|
+
|
302
|
+
return lhc, nd_array_history
|
303
|
+
|
304
|
+
|
305
|
+
def get_time_difference(tzone):
|
306
|
+
if tzone == 'Local':
|
307
|
+
time_difference = -time.timezone
|
308
|
+
else:
|
309
|
+
tz = pytz.timezone(tzone)
|
310
|
+
time_difference = tz.utcoffset(datetime.now).total_seconds()
|
311
|
+
return time_difference
|
312
|
+
|
313
|
+
|
314
|
+
def save_old_history(instr, filename, headers, nd_array_history, time_difference, dt_separator):
|
315
|
+
header = ['DateTime', 'Bid Open', 'Bid High', 'Bid Low', 'Bid Close', 'Ask Open', 'Ask High',
|
316
|
+
'Ask Low', 'Ask Close', 'Volume']
|
317
|
+
with open(filename, "w", newline="") as file:
|
318
|
+
writer = csv.writer(file, delimiter=delimiter)
|
319
|
+
if headers:
|
320
|
+
head = [headers]
|
321
|
+
writer.writerow(head)
|
322
|
+
else:
|
323
|
+
writer.writerow(header)
|
324
|
+
for i in range(1, len(nd_array_history)-1):
|
325
|
+
last_complete_data_frame = nd_array_history[i:i+1]
|
326
|
+
str2 = str(last_complete_data_frame[0])
|
327
|
+
str2 = str2.replace('(', '')
|
328
|
+
str2 = str2.replace(')', '')
|
329
|
+
str2 = str2.replace("'", '')
|
330
|
+
str2 = str2.replace(' ', '')
|
331
|
+
str2 = str2.split(',')
|
332
|
+
array = np.array(str2)
|
333
|
+
array[0] = array[0].replace('T', ' ')
|
334
|
+
dt = parser.parse(array[0])
|
335
|
+
dt = dt+timedelta(0, time_difference)
|
336
|
+
dt = str(dt)
|
337
|
+
array[0] = dt.replace(' ', dt_separator)
|
338
|
+
out = [array[0]]
|
339
|
+
for i2 in range(1, len(array)-1):
|
340
|
+
price = float(array[i2])
|
341
|
+
if format_decimal_places:
|
342
|
+
if instr.find("JPY") >= 0:
|
343
|
+
out.append("%.3f" % price)
|
344
|
+
else:
|
345
|
+
out.append("%.5f" % price)
|
346
|
+
else:
|
347
|
+
out.append(price)
|
348
|
+
out.append(array[len(array)-1])
|
349
|
+
writer.writerow(out)
|
350
|
+
|
351
|
+
def main():
|
352
|
+
global delimiter
|
353
|
+
global datetime_separator
|
354
|
+
global format_decimal_places
|
355
|
+
global timezone
|
356
|
+
|
357
|
+
args = parse_args()
|
358
|
+
config_file = args.config
|
359
|
+
if not os.path.exists(config_file):
|
360
|
+
config_file=os.path.join(path_from_where_we_call,config_file)
|
361
|
+
|
362
|
+
jgtconf=jgtcommon.readconfig()
|
363
|
+
|
364
|
+
if args.p:
|
365
|
+
str_password = args.p
|
366
|
+
else:
|
367
|
+
str_password = jgtconf.get('password') or os.getenv('password')
|
368
|
+
|
369
|
+
if str_password is None:
|
370
|
+
raise Exception("Password is required. Use -p option or export password=mypasswd")
|
371
|
+
sys.exit("Password is required. Use -p option or export password=mypasswd")
|
372
|
+
|
373
|
+
str_user_id, str_url, str_connection, str_session_id, str_pin, delimiter, output_dir, \
|
374
|
+
datetime_separator, format_decimal_places, timezone, data = parse_xml(config_file)
|
375
|
+
|
376
|
+
# print("cwd: "+os.getcwd())
|
377
|
+
# os.chdir(abs_path_of_this_file)
|
378
|
+
# print("cwd: "+os.getcwd())
|
379
|
+
with ForexConnect() as fx:
|
380
|
+
try:
|
381
|
+
print("Connecting to: user id:"+str_user_id+", url:"+str_url+", Connection:"+str_connection)
|
382
|
+
try:
|
383
|
+
fx.login(str_user_id, str_password, str_url,
|
384
|
+
str_connection, str_session_id, str_pin,
|
385
|
+
common_samples.session_status_changed)
|
386
|
+
except Exception as e:
|
387
|
+
print("Exception: " + str(e))
|
388
|
+
raise Exception(
|
389
|
+
"Login failed. Invalid parameters")
|
390
|
+
|
391
|
+
lhc = []
|
392
|
+
for param in data:
|
393
|
+
offer = Common.get_offer(fx, param[0])
|
394
|
+
|
395
|
+
check_params(param[0], param[1], offer)
|
396
|
+
|
397
|
+
tf = ForexConnect.get_timeframe(fx, param[1])
|
398
|
+
if not tf:
|
399
|
+
raise Exception(
|
400
|
+
"The timeframe '{0}' is not valid".format(param[1]))
|
401
|
+
|
402
|
+
lhc, nd_array_history = set_init_history(fx, lhc, param[0], param[1], param[2], param[3],
|
403
|
+
str_user_id, str_password, str_url, str_connection)
|
404
|
+
|
405
|
+
time_difference = get_time_difference(timezone)
|
406
|
+
|
407
|
+
save_old_history(param[0], param[2], param[4], nd_array_history, time_difference, datetime_separator)
|
408
|
+
|
409
|
+
print("Old history saved to "+param[2])
|
410
|
+
|
411
|
+
while True:
|
412
|
+
time.sleep(60)
|
413
|
+
|
414
|
+
except Exception as e:
|
415
|
+
common_samples.print_exception(e)
|
416
|
+
try:
|
417
|
+
fx.set_session_status_listener(session_status_changed(fx, None, None, str_user_id, str_password,
|
418
|
+
str_url, str_connection, False))
|
419
|
+
fx.logout()
|
420
|
+
except Exception as e:
|
421
|
+
common_samples.print_exception(e)
|
422
|
+
|
423
|
+
|
424
|
+
if __name__ == "__main__":
|
425
|
+
main()
|
426
|
+
print("")
|
427
|
+
input("Done! Press enter key to exit\n")
|
428
|
+
|
jgtfx2console/__init__.py
CHANGED
@@ -0,0 +1,74 @@
|
|
1
|
+
import argparse
|
2
|
+
|
3
|
+
|
4
|
+
import os
|
5
|
+
import xml.etree.ElementTree as ET
|
6
|
+
|
7
|
+
def generate_config(instruments, timeframes, nb_bar=500, default_headers="DateTime,Bid Open,Bid Close,Ask High,Ask Low,Volume", data_dir=None):
|
8
|
+
# Split the CSV strings into lists
|
9
|
+
instruments = instruments.split(',')
|
10
|
+
timeframes = timeframes.split(',')
|
11
|
+
|
12
|
+
# Create the root element
|
13
|
+
config = ET.Element('configuration')
|
14
|
+
|
15
|
+
# Create the Settings element
|
16
|
+
settings = ET.SubElement(config, 'Settings')
|
17
|
+
|
18
|
+
# Add the Login, OutputDir, and Url elements
|
19
|
+
ET.SubElement(settings, 'Login').text = '0'#os.getenv('user_id','0')
|
20
|
+
ET.SubElement(settings, 'OutputDir').text = data_dir or os.getenv('JGTPY_DATA')
|
21
|
+
ET.SubElement(settings, 'Url').text = os.getenv('url','https://www.fxcorporate.com/Hosts.jsp')
|
22
|
+
|
23
|
+
# Add the other elements
|
24
|
+
ET.SubElement(settings, 'Connection').text = os.getenv('connection','Real')
|
25
|
+
ET.SubElement(settings, 'SessionID').text = ''
|
26
|
+
ET.SubElement(settings, 'Pin').text = ''
|
27
|
+
ET.SubElement(settings, 'Delimiter').text = ','
|
28
|
+
ET.SubElement(settings, 'DateTimeSeparator').text = 'T'
|
29
|
+
ET.SubElement(settings, 'FormatDecimalPlaces').text = 'Y'
|
30
|
+
ET.SubElement(settings, 'Timezone').text = 'UTC'
|
31
|
+
|
32
|
+
# Add the History elements
|
33
|
+
for instrument in instruments:
|
34
|
+
for timeframe in timeframes:
|
35
|
+
history = ET.SubElement(settings, 'History')
|
36
|
+
ET.SubElement(history, 'Instrument').text = instrument
|
37
|
+
ET.SubElement(history, 'Timeframe').text = timeframe
|
38
|
+
ifn = instrument.replace("/","-")
|
39
|
+
ET.SubElement(history, 'Filename').text = f'{ifn}_{timeframe}.csv'
|
40
|
+
ET.SubElement(history, 'NumBars').text = str(nb_bar)
|
41
|
+
ET.SubElement(history, 'Headers').text = default_headers
|
42
|
+
|
43
|
+
# Return the XML as a string
|
44
|
+
return ET.tostring(config, encoding='utf8', method='xml').decode()
|
45
|
+
|
46
|
+
def main():
|
47
|
+
import argparse
|
48
|
+
parser = argparse.ArgumentParser(description='Generate a configuration file for the ptoLiveChartDataExport')
|
49
|
+
parser.add_argument('--instruments', help='The list of instruments to export (comma-separated)')
|
50
|
+
parser.add_argument('--timeframes', help='The list of timeframes to export (comma-separated)')
|
51
|
+
parser.add_argument('--outxml', type=str,default="jgtfxliveconfig.xml", help='Output XML file')
|
52
|
+
|
53
|
+
#data_dir = os.getenv('JGTPY_DATA') or if --data_dir
|
54
|
+
parser.add_argument('--data_dir', help='The directory where the data will be saved')
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
args = parser.parse_args()
|
59
|
+
instruments = args.instruments
|
60
|
+
timeframes = args.timeframes
|
61
|
+
data_dir = args.data_dir or os.getenv('JGTPY_DATA')
|
62
|
+
outxml = args.outxml
|
63
|
+
|
64
|
+
# Generate the configuration
|
65
|
+
config = generate_config(instruments, timeframes, data_dir=data_dir)
|
66
|
+
|
67
|
+
# Print the configuration
|
68
|
+
print(config)
|
69
|
+
with open(outxml, 'w') as f:
|
70
|
+
f.write(config)
|
71
|
+
print("Configuration file written to:", outxml)
|
72
|
+
|
73
|
+
if __name__ == '__main__':
|
74
|
+
main()
|
@@ -1,4 +1,6 @@
|
|
1
|
-
jgtfx2console/
|
1
|
+
jgtfx2console/LiveChartDataExport.py,sha256=Diqo49bOOLlqMF4spwcmpH5iNakmvx5z5zHFKhYwrOo,15202
|
2
|
+
jgtfx2console/__init__.py,sha256=0uXiOgT0kYlhowNnbfV_X2selvurEgSez1B-lQ6OZyo,1611
|
3
|
+
jgtfx2console/config_generator.py,sha256=hJ7P9luXFxY2E8YsOCI51tFtQMXEM4uA3IPeuP7GZeo,2827
|
2
4
|
jgtfx2console/fxcli2console.py,sha256=oI4d01jkXNriP2tF8aHcdep_r2YORWMfSnysrIHEAqQ,7426
|
3
5
|
jgtfx2console/forexconnect/EachRowListener.py,sha256=jbRaSqhHtoaRnEnLGoUmVLvE6VPt-WXYtglRPzK440k,1649
|
4
6
|
jgtfx2console/forexconnect/ForexConnect.py,sha256=Yf6AcLdgMSD8rd1s2DTIJPfzJ7BljIaZy_8vTfPfSOI,25755
|
@@ -29,9 +31,9 @@ jgtfx2console/forexconnect/lib/linux/libpython3.7m.so,sha256=UQzwU5QcaVnwQdt2yXi
|
|
29
31
|
jgtfx2console/forexconnect/lib/linux/libquotesmgr2.so.2.8,sha256=QIUMvjI-4m3YX3N23Pad9TIhYbmk17raTTW0gyrElkM,1245248
|
30
32
|
jgtfx2console/forexconnect/lib/linux/libsqlite3.8.so,sha256=aqjV6ZcluDkzyr_bbWQCOHt9uitLvp7uPS-53Z3toQk,641120
|
31
33
|
jgtfx2console/forexconnect/lib/linux/requirements.txt,sha256=62zntle4oq_jlsvrAK1bbfgYvAcRPtVHkGYu_eWiRZs,77
|
32
|
-
jgtfx2console-0.4.
|
33
|
-
jgtfx2console-0.4.
|
34
|
-
jgtfx2console-0.4.
|
35
|
-
jgtfx2console-0.4.
|
36
|
-
jgtfx2console-0.4.
|
37
|
-
jgtfx2console-0.4.
|
34
|
+
jgtfx2console-0.4.20.dist-info/LICENSE,sha256=uCA_HysYVK9qIMbDEFjcDYhCoKTg19AJhKhM71EKSGA,1086
|
35
|
+
jgtfx2console-0.4.20.dist-info/METADATA,sha256=ykt2gYX4HZUJx0pA3Iv1EKW-lA04IktIp3PsLVr-bEE,2201
|
36
|
+
jgtfx2console-0.4.20.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
37
|
+
jgtfx2console-0.4.20.dist-info/entry_points.txt,sha256=5-2VwwmwQtOqI0d2qvq3Lupgu9sUVe8S6zOYf-9whQ4,164
|
38
|
+
jgtfx2console-0.4.20.dist-info/top_level.txt,sha256=PBNCBgqX42I7ctUTX9FcTnIX0utvgRqzuflefzZ30D8,14
|
39
|
+
jgtfx2console-0.4.20.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|