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.
@@ -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