jgtfx2console 0.4.19__py3-none-any.whl → 0.4.20__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -42,6 +42,6 @@ with warnings.catch_warnings():
42
42
  # your code here
43
43
 
44
44
 
45
- __version__ = "0.4.19"
45
+ __version__ = "0.4.20"
46
46
 
47
47
 
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jgtfx2console
3
- Version: 0.4.19
3
+ Version: 0.4.20
4
4
  Summary: PDS Services
5
5
  Home-page: https://github.com/jgwill/jgtfx2console
6
6
  Author: GUillaume Isabelle
@@ -1,4 +1,6 @@
1
- jgtfx2console/__init__.py,sha256=d2ZjTBf1ZErmm3jAtwHb_89qfjf3SLAdajfrvxbpi88,1611
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.19.dist-info/LICENSE,sha256=uCA_HysYVK9qIMbDEFjcDYhCoKTg19AJhKhM71EKSGA,1086
33
- jgtfx2console-0.4.19.dist-info/METADATA,sha256=QVK3KW_3eRrMNfWIqK6Fdhbtonpdnj4lHyBfUzbtNfk,2201
34
- jgtfx2console-0.4.19.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
35
- jgtfx2console-0.4.19.dist-info/entry_points.txt,sha256=iSwe90GK9NXcJIDBaKiASHxFPDsYv0FqyKYcq-D2kdo,210
36
- jgtfx2console-0.4.19.dist-info/top_level.txt,sha256=PBNCBgqX42I7ctUTX9FcTnIX0utvgRqzuflefzZ30D8,14
37
- jgtfx2console-0.4.19.dist-info/RECORD,,
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,,
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ fxcli2console = jgtfx2console.fxcli2console:main
3
+ fxlive = jgtfx2console.LiveChartDataExport:main
4
+ fxliveconf = jgtfx2console.config_generator:main
@@ -1,4 +0,0 @@
1
- [console_scripts]
2
- fxcli2console = jgtfx2console.fxcli2console:main
3
- fxlive = jgtfx2console.ptoLiveChartDataExport.LiveChartDataExport:main
4
- fxliveconf = jgtfx2console.ptoLiveChartDataExport.config_generator:main