-
Notifications
You must be signed in to change notification settings - Fork 1
/
requester.py
1259 lines (1060 loc) · 53.8 KB
/
requester.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import re
from commandprice import CommandPrice
from commanderror import CommandError
from datetimeutil import DateTimeUtil
CURRENCY_SYMBOL_MIN_LENGTH = 3
class Requester:
'''
Reads in commands entered by the
user, typically
oo btc [5/7 0.0015899 6/7 0.00153] [usd-chf] -nosave
and return a command filled with the command parsed parm data
:seqdiag_note Parses the user requests, storing the request parms into the the appropriate Command.
'''
ENTER_COMMAND_PROMPT = 'Enter command (h for help, q to quit)\n'
'''
oo open order
xo closed order
lo lowest order or order at lowest price
ho highest order or ordedr at highest price
ro range orders
va variation percents
'''
USER_COMMAND_GRP_PATTERN = r"(OO|XO|LO|HO|RO|VA) "
'''
Stores correspondence between user input command parms and
CommmandPrice.parsedParmData dictionary keys
'''
INPUT_PARMS_PARM_DATA_DIC_KEY_DIC = {'-C': CommandPrice.CRYPTO,
'-U': CommandPrice.UNIT,
'-D': CommandPrice.DAY_MONTH_YEAR,
'-T': CommandPrice.HOUR_MINUTE,
'-E': CommandPrice.EXCHANGE,
'-V': CommandPrice.OPTION_VALUE_DATA,
'-F': CommandPrice.OPTION_FIAT_DATA,
'-P': CommandPrice.OPTION_PRICE_DATA,
'-R': CommandPrice.OPTION_RESULT_DATA,
'-L': CommandPrice.OPTION_LIMIT_DATA}
OPTION_MODIFIER = 'optionModifier'
OPTION_UNSUPPORTED = 'optionUnsupported'
# changed r"\d+/\d+(?:/\d+)*|^0$" into r"\d+/\d+(?:/\d+)*|^\d+$" was required so
# that a full request like btc usd 1 12:45 bitfinex does generate an ERROR - date not valid
# in CommandPrice. With the old version of the pattern, CommandPrice.DAY_MONTH_YEAR was none,
# which was considered like a valid full request with only the time provided, a feature which
# was not supported before !
#
# So, allowing the user to provide only the time in the full request implied that we are
# more permissive at the level of the Requester in order for CommandPrice to be able
# to correctly identify the invalid date/time full request component in the form of
# D HH:MM or DD HH:MM
# pattern modified to enable handling erroneous option specification with no option
# data like in full request eth btc 0 binance -v or -vs or -f or -fs
PATTERN_COMMAND_DIC = {r"\d+/\d+(?:/\d+)*|^\d+$": CommandPrice.DAY_MONTH_YEAR,
r"\d+:\d\d": CommandPrice.HOUR_MINUTE,
r"[A-Za-z]+": CommandPrice.EXCHANGE,
r"(?:-[vV])([sS]?)([\w\.]*)": CommandPrice.OPTION_VALUE_DATA,
r"(?:-[vV])([sS]?)([\w\.]*)" + OPTION_MODIFIER: CommandPrice.OPTION_VALUE_SAVE,
r"(?:-[fF])([sS]?)([\w\.]*)": CommandPrice.OPTION_FIAT_DATA,
r"(?:-[fF])([sS]?)([\w\.]*)" + OPTION_MODIFIER: CommandPrice.OPTION_FIAT_SAVE,
r"(?:-[pP])([sS]?)([\w\.]*)": CommandPrice.OPTION_PRICE_DATA,
# \w instead of \d enables the generation
# of an error msg if a fiat symbol is appended
# to the price amount !
r"(?:-[pP])([sS]?)([\w\.]*)" + OPTION_MODIFIER: CommandPrice.OPTION_PRICE_SAVE,
r"(?:-[rR])([sS]?)([\w\.:-]*)": CommandPrice.OPTION_RESULT_DATA,
r"(?:-[rR])([sS]?)([\w\.:-]*)" + OPTION_MODIFIER: CommandPrice.OPTION_RESULT_SAVE,
r"(?:-[lL])([sS]?)([\w\.]*)": CommandPrice.OPTION_LIMIT_DATA,
r"(?:-[lL])([sS]?)([\w\.]*)" + OPTION_MODIFIER: CommandPrice.OPTION_LIMIT_SAVE,
r"(-[^vVfFpPrRlL]{1})([sS]?)([\w\.]*)": CommandPrice.UNSUPPORTED_OPTION_DATA,
r"(-[^vVfFpPrRlL]{1})([sS]?)([\w\.]*)" + OPTION_UNSUPPORTED: CommandPrice.UNSUPPORTED_OPTION,
r"(-[^vVfFpPrRlL]{1})([sS]?)([\w\.]*)" + OPTION_MODIFIER: CommandPrice.UNSUPPORTED_OPTION_MODIFIER, }
'''
Full price command parms pattern. Crypto symbol (mandatory, first position mandatory), unit symbol (optional,
if provided, must be in second position), date (optional), time (optional) and exchange (optional). The three
last parms can be provided in any order after the 2 first parms !
Additionally, request options can be added to the regular full command. For example -vs12btc, -fusd.kraken.
Ex; btc usd 0 Kraken
btc usd 10/9/17 12:45 kraken
btc usd 10/9/17 kraken
btc usd 10/9 kraken
btc usd 0 Kraken -v0.01btc
btc usd 10/9/17 12:45 binance -v0.01btc -fusd.kraken
btc usd 10/9/17 kraken -v0.01btc
btc usd 10/9 kraken -v0.01btc
Additional rules for the date and time parms. Those rules are enforced by the
_buildFullCommandPriceOptionalParmsDic() method.
° 0 is legal for date and means now, real time !
° Date must be 0 or contain a '/'.
° Time must be composed of two numerical groups separated by ':', the second group being a 2 digits
group. Note 0:00 or 00:00 does not mean now, but midnight !
° Exchange name must start with a letter. May contain digits.
Ex:
Date can be: 0, accepted.
1, rejected.
10, rejected.
01, rejected.
0/0, accepted.
01/0, accepted.
01/1, accepted.
01/10, accepted.
1/1, accepted.
1/10, accepted.
01/12/16, accepted.
01/12/2015, accepted.
01/12/0, accepted.
Hour minute can be: 0, rejected.
1, rejected.
10, rejected.
01, rejected.
01:1, rejected.
01:01, accepted.
01:10, accepted.
1:10, accepted.
00:00, accepted.
0:00, accepted.
0:0, rejected.
'''
# PATTERN_FULL_PRICE_REQUEST_WITH_OPTIONAL_COMMAND_DATA = r"(\w+)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.]+))?(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.]+))?"
# pattern modified to enable handling erroneous option specification with no option
# data like in full request eth btc 0 binance -v or -vs or -f or -fs
#PATTERN_FULL_PRICE_REQUEST_WITH_OPTIONAL_COMMAND_DATA = r"(\w+)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.]*))?(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.]*))?"
# modifying the full request pattern to handle the new -r option. This
# option can be -r, -rs, -r999.999, -rs999.999, -r-1, -rs-1, -r-1-2-3-n,
# -rs-1-2-3-n, -r-1:-n, -rs-1:-n
# Here are the pythex test strings used to validate the new pattern
'''
btc usd 12/2/21 13:55 hitbtc
btc usd 12/2/21 13:55 hitbtc -vs21.23btc -fschf.kraken -ps52012.45 -r-2:-3
btc usd 12/2/21 13:55 hitbtc -vs21.23btc -fschf.kraken -rs -ps52012.45
btc usd 12/2/21 13:55 hitbtc -vs21.23btc -fschf.kraken -rs-1-2-3
btc usd 12/2/21 13:55 hitbtc -ps52012.45 -vs21.23btc -fschf.kraken -rs-1:-3
btc usd 12/2/21 13:55 hitbtc -vs21.23btc -rs-1:-3 -fschf.kraken
btc usd 12/2/21 13:55 hitbtc -rs-1:-3 -vs21.23btc -ps52012.45 -fschf.kraken
btc usd 12/2/21 13:55 hitbtc -vs21.23btc -fschf.kraken -rs-1
Those requests are unit tested by TestRequester.test_parseGroupsFullVariousResultOptions().
'''
# The full price request pattern is configured to parse 5 required full request
# parameters (crypto, unit, date, time and exchange) as well as a maximum of 4 options
# (-v value, -f fiat, -p price, and -r result). Adding a fifth -l limit option to the
# 4 options makes no sense, so a full price request pattern with 9 (5 + 4) groups instead
# of 10 is ok !
#
# Ex of biggest full request:
#
# btc usd 12/2/21 13:55 hitbtc -vs21.23btc -fschf.kraken -ps52012 -r-1-2
#
# The TestControllerGui.testOptionValueOptionFiatFullRequestHistoDayPriceRequiredParmsOrderChanged()
# unit test verifies that the date, time and exchange required parameters can in fact be specified
# in any order.
PATTERN_FULL_PRICE_REQUEST_WITH_OPTIONAL_COMMAND_DATA = r"(\w+)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: ([\w/:]+)|)(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.-]*))?(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.-]*))?(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.-]*))?(?: (-[a-zA-Z][a-zA-Z]?[\w/:\.-]*))?"
'''
Partial price command parms pattern. Grabs groups of kind -cbtc or -t12:54 or -d15/09 or -ebittrex
or option groups sticking to the same format -<command letter> followed by 1 or
more \w or \d or /. or - or : characters.
Unlike with pattern 'full', the groups - option or not - can all occur in any order, reason for which
all groups have the same pattern structure.
The rules below apply to -d and -t values !
Date can be: 0, accepted.
1, accepted.
10, accepted.
01, accepted.
01/0, accepted.
01/1, accepted.
01/10, accepted.
0/0, accepted.
1/0, accepted.
1/1, accepted.
1/10, accepted.
01/12/16, accepted.
01/12/2015, accepted.
01/12/0, accepted.
1 12:45, accepted.
Hour minute can be: 0, rejected.
1, rejected.
10, rejected.
01, rejected.
01:1, accepted.
01:01, accepted.
01:10, accepted.
1:10, accepted.
00:00, accepted.
0:00, accepted.
0:0, accepted.
'''
# PATTERN_PARTIAL_PRICE_REQUEST_DATA = r"(?:(-[a-zA-Z])([\w/:\.]*))(?: (-[a-zA-Z])([\w/:\.]*))?(?: (-[a-zA-Z])([\w/:\.]*))?(?: (-[a-zA-Z])([\w/:\.]*))?(?: (-[a-zA-Z])([\w/:\.]*))?(?: (-[a-zA-Z])([\w/:\.]*))?(?: (-[a-zA-Z])([\w/:\.]*))?(?: (-[a-zA-Z])([\w/:\.]*))?"
# The partial price request pattern is configured to parse a maximum of 9 parameters,
# 5 basic request parameters (-c, -u, -d, -t and -e) and a maximum of 4 options,
# (-v, -f, -p and -r). Adding a fifth limit -l option to the 4 options makes
# no sense, so a partial price request pattern with 9 (5 + 4) groups instead of 10 is ok !
#
# The partial request specifications can be in any order.
#
# Ex: -ceth -ueur -d1 -t12:45 -ebittrex -vs34usd -fschf.kraken -rs-1:-2 -ebittrex -p1450
PATTERN_PARTIAL_PRICE_REQUEST_DATA = r"(?:(-[a-zA-Z])([\w/:\.-]*))(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?(?: (-[a-zA-Z])([\w/:\.-]*))?"
PATTERN_PARTIAL_PRICE_REQUEST_ERROR = r"({}([\d\w,\./]*))(?: .+|)"
'''
The next pattern splits the parameter data appended to the -v partial command option.
Ex: -vs0.004325btc is splitted into 's', '0.00432', 'btc', None
-v0.004325btc is splitted into '', '0.00432', 'btc', None
-v0 is splitted into None, None, None, '0' and will mean 'erase previous -v parm specification
'''
OPTION_VALUE_PARM_DATA_PATTERN = r"([sS]?)([\d\.]+)(\w+)|(0)"
'''
The next pattern splits the parameter data appended to the -f partial command option.
Ex: -fsusd.kraken is splitted into 's', 'usd', 'kraken', None
-fusd.kraken is splitted into '', 'usd', 'kraken', None
-fusd is splitted into '', 'usd', None, None
-f0 is splitted into None, None, None, '0' and will mean 'erase previous -f parm
specification
'''
OPTION_FIAT_PARM_DATA_PATTERN = r"(?:([sS]?)([a-zA-Z]+)(?:(?:\.)(\w+))?)|(0)"
'''
The next pattern splits the parameter data appended to the -p partial command option.
Ex: -ps0.004325 is splitted into 's', '0.00432'
-p0.004325 is splitted into '', '0.00432'
-ps0.004325usd is splitted into '', '0.00432usd' this invalid -ps option will generate an error msg
-p0.004325usd is splitted into '', '0.00432usd' this invalid -p option will generate an error msg
-p0 is splitted into '', '0' and will mean 'erase previous -p parm specification
'''
OPTION_PRICE_PARM_DATA_PATTERN = r"(?:([sS]?)([\w\.]*))" # \w instead of \d enables the generation
# of an error msg if a fiat symbol is appended
# to the price amount !
# uaing * insteadof + makes the generic
# Requester._validateOptionMandatoryComponents()
# method usefull
'''
The next pattern splits the parameter data appended to the -r partial command option.
Ex: -rs is splitted into '', '', None save option modifier is set to 's' in self._extractData()
-r is splitted into '', '', None save option modifier is set to 's' in self._extractData()
-rs-1 is splitted into '', '-1', None save option modifier is set to 's' in self._extractData()
-rs-1-2-3 is splitted into '', '-1-2-3', None save option modifier is set to 's' in self._extractData()
-r-1-2-3 is splitted into '', '-1-2-3', None
-rs-1:-3 is splitted into '', '-1:-3', None save option modifier is set to 's' in self._extractData()
-r-1:-3 is splitted into '', '-1:-3', None
-r0 is splitted into '', '0', None and will mean 'erase previous -r parm specification
'''
OPTION_RESULT_PARM_DATA_PATTERN = r"([sS]?)([\d\.:-]*)|(s)"
'''
The next pattern splits the parameter data appended to the -l partial command option.
Ex: -ls16500usd.kraken is splitted into 's', '16500', 'usd', 'kraken', None
-l16500usd.kraken is splitted into '', '16500', 'usd', 'kraken', None
-l16500usd is splitted into '', '16500', 'usd', None, None
-l0 is splitted into None, None, None, None, '0' and will mean 'erase previous -l parm
specification
'''
OPTION_LIMIT_PARM_DATA_PATTERN = r"([sS]?)([\d\.]+)(\w+)(?:(?:\.)(\w+))?|(0)"
REQUEST_TYPE_PARTIAL = 'PARTIAL'
REQUEST_TYPE_FULL = 'FULL'
def __init__(self, configMgr):
self.configMgr = configMgr
self.commandQuit = None
self.commandError = None
self.commandPrice = None
self.commandCrypto = None
self.commandTrade = None
def getCommandFromCommandLine(self):
"""
Used essentially by the command line version of CryptoPricer.
"""
inputStr = input(Requester.ENTER_COMMAND_PROMPT)
upperInputStr = inputStr.upper()
while upperInputStr == 'H':
self._printHelp()
inputStr = input(Requester.ENTER_COMMAND_PROMPT)
upperInputStr = inputStr.upper()
if upperInputStr == 'Q':
return self.commandQuit
else: #here, neither help nor quit command entered. Need to determine which command
#is entered by the user finding unique pattern match that identify this command
return self.getCommand(inputStr)
def getCommand(self, inputStr):
"""
Used by the gui version of CryptoPricer.
Parses the passed input string and return a Command concrete instance
filled with the command specific data. May return a CommandError.
:param inputStr: input string to parse
:seqdiag_return AbstractCommand
:seqdiag_return_note May return a CommandError in case of parsing problem.
:return: Command concrete instance
"""
returnedCommand = None
if inputStr == '' and self.commandPrice.isValid():
#here, user entered RETURN to replay the last commannd
self._alignCommandPriceDateTimeDataWithPriceType()
# ensuring that no unsupported option is in effect which would cause
# a warning to disturb the replay of the previous request execution.
self.commandPrice.resetUnsupportedOptionData()
returnedCommand = self.commandPrice
else:
upperInputStr = inputStr.upper()
match = self._tryMatchCommandSymbol(upperInputStr)
if match == None:
#here, either full or partial historical/RT price request which has no command symbol
#or user input error
command = self._parseAndFillCommandPrice(inputStr)
if command == self.commandPrice or command == self.commandError:
returnedCommand = command
else:
# here, either invalid historical/RT price request which has no command symbol (for ex -t alone)
# or other request with missing command symbol (for ex [btc 05/07 0.0015899] [usd-chf] -nosave)
if '[' in inputStr:
# command symbol missing
self.commandError.parsedParmData[
self.commandError.COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_INVALID_COMMAND
self.commandError.parsedParmData[self.commandError.COMMAND_ERROR_MSG_KEY] = self.commandError.USER_COMMAND_MISSING_MSG
else:
# invalid partial command parm
self.commandError.parsedParmData[self.commandError.COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_PARTIAL_REQUEST
self.commandError.parsedParmData[self.commandError.COMMAND_ERROR_MSG_KEY] = ''
returnedCommand = self.commandError
else:
userCommandSymbol = match.group(1)
if userCommandSymbol == "OO":
upperInputStrWithoutUserCommand = upperInputStr[len(userCommandSymbol) + 1:]
cryptoDataList, unitDataList, flag = self._parseOOCommandParms(inputStr, upperInputStrWithoutUserCommand)
if cryptoDataList == self.commandError:
returnedCommand = self.commandError
else:
self.commandCrypto.parsedParmData = {self.commandCrypto.CRYPTO_LIST : cryptoDataList, \
self.commandCrypto.UNIT_LIST : unitDataList, \
self.commandCrypto.FLAG : flag}
returnedCommand = self.commandCrypto
else:
self.commandError.parsedParmData[
self.commandError.COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_INVALID_COMMAND
self.commandError.parsedParmData[self.commandError.COMMAND_ERROR_MSG_KEY] = self.commandError.USER_COMMAND_MISSING_MSG
returnedCommand = self.commandError
returnedCommand.requestInputString = inputStr
return returnedCommand
def _alignCommandPriceDateTimeDataWithPriceType(self):
if self.commandPrice.parsedParmData[self.commandPrice.PRICE_TYPE] == self.commandPrice.PRICE_TYPE_RT:
self.commandPrice.parsedParmData[self.commandPrice.YEAR] = '0'
self.commandPrice.parsedParmData[self.commandPrice.MONTH] = '0'
self.commandPrice.parsedParmData[self.commandPrice.DAY] = '0'
self.commandPrice.parsedParmData[self.commandPrice.HOUR] = None
self.commandPrice.parsedParmData[self.commandPrice.MINUTE] = None
def _tryMatchCommandSymbol(self, upperInputStr):
'''
Try matching a command symbol like OO|XO|LO|HO|RO|VA in the command entered by the user.
If the user entered a price command, no command symbol is used !
:param upperInputStr:
:return: None or a Match object
'''
return re.match(Requester.USER_COMMAND_GRP_PATTERN, upperInputStr)
def _parseGroups(self, pattern, inputStr):
'''
Embedding this trivial code in a method enables to specifically test the correct
functioning of the used patterns.
:param pattern: pattern to parse
:param inputStr: string to parse
:return:
'''
match = re.match(pattern, inputStr)
if match != None:
return match.groups()
else:
return () # returning () instead of none since an iterator will be activated
# on the returned result !
def _buildFullCommandPriceOrderFreeParmsDic(self, orderFreeParmList):
'''
This method is called only on full requests. A full request starts with 2 mandatory parameters,
CRYPTO and UNIT provided in this mandatory order. The other full request mandatory parameters,
date, time and exchange can be specified in any order. A full request can be ended by options
whose order is free as well.
The purpose of this method is precisely to acquire the order free full request parameters.
Since DAY_MONTH_YEAR, HOUR_MINUTE, EXCHANGE and additional OPTIONS can be provided in any order
after CRYPTO and UNIT, this method differentiate them and build an order free command price parm
data dictionary with the right key. This dictionary will then be added to the CommandPrice parmData
dictionary.
:seqdiag_return optionalParsedParmDataDic
:param orderFreeParmList: contains the request parms entered after the CRYPTO and UNIT
specification aswell as any request option (namely -v, -f or -p).
:return optionalParsedParmDataDic or None in case of syntax error.
'''
'''
° 0 is legal for both date and time parms. Zero for either date or/and time means now, real time !
° Date must be 0 or contain a '/'.
° Time must be composed of two numerical groups separated by ':', the second group being a 2 digits
group. Note 00:00 does not mean now, but midnight !
° Exchange name must start with a letter. May contain digits.
Ex:
Date can be 0, accepted. 1, rejected. 10, rejected. 01, rejected. 01/1, accepted. 01/10, accepted.
1/1, accepted. 1/10, accepted. 01/12/16, accepted. 01/12/2015, accepted.
Hour minute can be 0, rejected. 1, rejected. 10, rejected. 01, rejected. 01:1, rejected. 01:01, accepted.
01:10, accepted. 1:10, accepted. 00:00, accepted. 0:00, accepted. 0:0, rejected.
'''
orderFreeParmList = list(orderFreeParmList)
orderFreeParsedParmDataDic = {}
# it does not make sense to parse the order free full request parms with a pattern combined with
# OPTION_MODIFIER string !
patternCommandDicKeysWithoutModifier = [x for x in self.PATTERN_COMMAND_DIC.keys() if self.OPTION_MODIFIER not in x]
for pattern in patternCommandDicKeysWithoutModifier:
for orderFreeParm in orderFreeParmList:
if orderFreeParm and re.search(pattern, orderFreeParm):
parsedParmDataDicKey = self.PATTERN_COMMAND_DIC[pattern]
if parsedParmDataDicKey not in orderFreeParsedParmDataDic:
# if for example DMY already found in optional full command parms,
# it will not be overwritten ! Ex: 12/09/17 0: both token match DMY
# pattern !
data, option, optionModifier = self._extractData(pattern, orderFreeParm)
if data != None:
orderFreeParsedParmDataDic[parsedParmDataDicKey] = data
patternOptionModifierKey = pattern + self.OPTION_MODIFIER
if optionModifier != None and optionModifier != '':
optionModifierParsedParmDataDicKey = self.PATTERN_COMMAND_DIC[patternOptionModifierKey]
orderFreeParsedParmDataDic[optionModifierParsedParmDataDicKey] = optionModifier
# elif patternOptionModifierKey in orderFreeParsedParmDataDic.keys():
# This situation never happens when handling full request ! In fact,
# orderFreeParsedParmDataDic is initialized to [] at the beginning of the method.
# optionModifierParsedParmDataDicKey = patternCommandDic[patternOptionModifierKey]
# orderFreeParsedParmDataDic[optionModifierParsedParmDataDicKey] = None
orderFreeParmList.remove(orderFreeParm)
if option:
# here, handling an unsupported option. If handling a supported option, option
# is None. For valid options, the correct option symbol will be set later in the
# command price in _fillOptionValueInfo() like methods !
patternUnsupportedOptionKey = pattern + self.OPTION_UNSUPPORTED
orderFreeParsedParmDataDic[self.PATTERN_COMMAND_DIC[patternUnsupportedOptionKey]] = option
else:
#full command syntax error !
return None
from seqdiagbuilder import SeqDiagBuilder
SeqDiagBuilder.recordFlow()
return orderFreeParsedParmDataDic
def _extractData(self, pattern, dataOrOptionStr):
'''
Applies the passed pattern to the passed dataOrOptionStr. If the passed dataOrOptionStr
is an option of type -v0.1btc or -vs0.1btc, the passed pattern will extract the data part of the
dataOrOptionStr, i.e. 0.01btc in this case and the option modifier part of the dataOrOptionStr,
i.e. s in this case. The option itself is not extracted here since the pattern ignores the
option symbol (like (?:-[vV]) in (?:-[vV])([sS]?)([\\w\\.]* for value option pattern. The option
symbol will be set later in the command price in _fillOptionValueInfo() like methods !
Else if the dataOrOptionStr does contain data only, without an option symbol, the passed
pattern does not extract any group and the data is returned as is.
:param pattern:
:param dataOrOptionStr:
:return: passed data or data part of the passed option + data aswell as the option modifier.
If -v0.1btc is passed as dataOrOptionStr, 0.1btc is returned. If -vs0.1btc is passed as
dataOrOptionStr, 0.1btc and s is returned.
In case of syntax error, None is returned
'''
match = re.match(pattern, dataOrOptionStr)
if match == None:
#denotes a syntax error !
return None, None, None
option = None
optionModifier = None
grpNumber = len(match.groups())
if grpNumber == 1:
data = match.group(1)
elif grpNumber == 2:
optionModifier = match.group(1)
data = match.group(2)
elif grpNumber == 3:
# here, handling an unsupported option. If handling a supported option, option
# is None. For valid options, the correct option symbol will be set later in the
# command price in _fillOptionValueInfo() like methods !
option = match.group(1)
optionModifier = match.group(2)
data = match.group(3)
else:
data = dataOrOptionStr
return data, option, optionModifier
def _parseAndFillCommandPrice(self, inputStr):
'''
This method parses either a full or a partial request.
Here are 2 examples of a full request, one without any option and the second
with 2 options:
eth btc 0 binance
eth btc 10/2/21 10:35 bittrex -v2eth -fusd.kraken
The method first try to parse a full request. If no list of request elements was
returned by the parsing, it then try to parse a partial request.
Here are 2 examples of a partial request, one with 1 option and the second
with 2 options:
-ebitfinex
-d11/2 -fchf.kraken
:param inputStr:
:seqdiag_return CommandPrice or CommandError
:return: self.commandPrice or self.commandError or None, which will cause an error to be raised
'''
# First, try to parse a full request partialRequestStr
groupList = self._parseGroups(self.PATTERN_FULL_PRICE_REQUEST_WITH_OPTIONAL_COMMAND_DATA, inputStr)
if groupList == ():
# Second, as full request pattern was not matched, try to parse a partial request partialRequestStr
groupList = self._parseGroups(self.PATTERN_PARTIAL_PRICE_REQUEST_DATA, inputStr)
if groupList != ():
# Partial request entered. Here, parm values are associated to parm tags
# (i.e -c or -d). This means they have been entered in any order and are all
# optional. Ensuring command price temporary data like unsupported command data
# from the previous request are purged is necessary here when handling a partial
# command(s) since, unlike when a full command is processed, the command price is not
# reinitialized ! Otherwise, a warning signaling that an unsupported option is in
# effect would be displayed in case such unsupported option was part of the preceeding
# full or partial request !
requestType = self.REQUEST_TYPE_PARTIAL
# ensuring that no unsupported option is in effect which would cause
# a warning to disturb the partial request execution.
self.commandPrice.resetUnsupportedOptionData()
keys = self.INPUT_PARMS_PARM_DATA_DIC_KEY_DIC.keys()
it = iter(groupList)
for command in it:
value = next(it)
if value != None:
value = value
commandUpper = command.upper()
if commandUpper in keys:
if commandUpper != '-R' and (value == '' or value.upper() == 'S'):
# partial request option -r or -rs with no value is valid !
invalidPartialCommand, invalidValue = self._wholeParmAndInvalidValue(command, inputStr)
self.commandError.parsedParmData[
self.commandError.COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_PARTIAL_REQUEST
self.commandError.parsedParmData[
self.commandError.COMMAND_ERROR_MSG_KEY] = self.commandError.PARTIAL_REQUEST_EMPTY_VALUE_MSG.format(
invalidPartialCommand, invalidPartialCommand)
# remove invalid '' value from parsedParData to avoid polluting next partial
# request !
self.commandPrice.parsedParmData[self.INPUT_PARMS_PARM_DATA_DIC_KEY_DIC[commandUpper]] = None
return self.commandError
else:
self.commandPrice.parsedParmData[self.INPUT_PARMS_PARM_DATA_DIC_KEY_DIC[commandUpper]] = value
else:
# unknown partial command symbol
self.commandPrice.parsedParmData[self.commandPrice.UNSUPPORTED_OPTION] = command
if value != '' and value[0].upper() == 'S':
self.commandPrice.parsedParmData[self.commandPrice.UNSUPPORTED_OPTION_DATA] = value[1:]
self.commandPrice.parsedParmData[self.commandPrice.UNSUPPORTED_OPTION_MODIFIER] = value[0]
else:
self.commandPrice.parsedParmData[self.commandPrice.UNSUPPORTED_OPTION_DATA] = value
self.commandPrice.parsedParmData[self.commandPrice.UNSUPPORTED_OPTION_MODIFIER] = None
if self.commandPrice.parsedParmData[CommandPrice.DAY_MONTH_YEAR] == '0':
# -d0 which means RT entered. In this case, the previous
# date/time info are no longer relevant and must be erased!
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_RT
hourMinute, dayMonthYear = self._wipeOutDateTimeInfoFromCommandPrice()
elif self.commandPrice.parsedParmData[CommandPrice.DAY_MONTH_YEAR] == None and self.commandPrice.parsedParmData[CommandPrice.HOUR_MINUTE] == None:
# here, partial command(s) which aren't date/time related were entered: the previous request price type must be considered !
if self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] == CommandPrice.PRICE_TYPE_RT:
hourMinute, dayMonthYear = self._wipeOutDateTimeInfoFromCommandPrice()
else:
# here, since previous request was not RT, hourMinute and dayMonthYear must be rebuilt
# from the date/time values of the previous request. Don't forget that OAY_MONTH_YEAR
# and HOUR_MINUTE are set to None once date/time values have been acquired !
if self._isMinimalDateTimeInfoFromPreviousRequestAvailable():
dayMonthYear, hourMinute = self._rebuildPreviousRequestDateTimeValues()
elif self._isPreviousFullRequestActive():
return None # will cause an error. This occurs in a special situation when the previous request
# was in error, which explains why the date/time info from previous request is
# incoherent. Such a case is tested by TestController.
# testControllerHistoDayPriceIncompleteCommandScenario
else:
# here, a partial request was entered before submitting any full request
self.commandError.parsedParmData[
self.commandError.COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_PARTIAL_REQUEST_WITH_NO_PREVIOUS_FULL_REQUEST
# ensuring no previous error msg info are in effect which would pollute this error msg
self.commandError.parsedParmData[
self.commandError.COMMAND_ERROR_MSG_KEY] = ''
return self.commandError
else:
# here, either commandPrice.parsedParmData[CommandPrice.DAY_MONTH_YEAR]
# or commandPrice.parsedParmData[CommandPrice.HOUR_MINUTE] or both are
# not None. Since the -d partial request value can be -d21/2/20 13:05,
# which could not be parsed by the
# self._parseGroups(self.PATTERN_PARTIAL_PRICE_REQUEST_DATA, partialRequestStr)
# above, we have to try to extract a DMY and HM date/time value from the
# partialRequestStr in case the -d partial request did contain a time element
dayMonthYear, hourMinute = self._tryExtractDateTimeValueFromPartialRequest(inputStr)
if dayMonthYear is None and hourMinute is None:
# here, partial request -d contained no time value and so was parsed
# by self._parseGroups(self.PATTERN_PARTIAL_PRICE_REQUEST_DATA, partialRequestStr)
# above
dayMonthYear = self.commandPrice.parsedParmData[CommandPrice.DAY_MONTH_YEAR]
hourMinute = self.commandPrice.parsedParmData[CommandPrice.HOUR_MINUTE]
else: #neither full nor parrial pattern matched
return None # will cause an error.
else:
# full request entered. Here, request parms were entered in an order reflected in the
# pattern: crypto unit in this mandatory order, then date time exchange and options,
# which can be entered in any order.
requestType = self.REQUEST_TYPE_FULL
self.commandPrice._initialiseParsedParmData()
self.commandPrice.parsedParmData[CommandPrice.CRYPTO] = groupList[0] #mandatory crrypto parm, its order is fixed
self.commandPrice.parsedParmData[CommandPrice.UNIT] = groupList[1] #mandatory unit parm, its order is fixed
orderFreeParmDic = self._buildFullCommandPriceOrderFreeParmsDic(groupList[2:])
if orderFreeParmDic != None:
self.commandPrice.parsedParmData.update(orderFreeParmDic)
else:
# invalid full command format
self.commandError.parsedParmData[self.commandError.COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_FULL_REQUEST
return self.commandError
hourMinute = self.commandPrice.parsedParmData[CommandPrice.HOUR_MINUTE]
dayMonthYear = self.commandPrice.parsedParmData[CommandPrice.DAY_MONTH_YEAR]
if hourMinute != None:
hourMinuteList = hourMinute.split(':')
if len(hourMinuteList) == 1:
# supplied time is invalid: does not respect expected format of 0:10 or 12:01 etc
# invalid time partial command format
invalidPartialCommand, invalidValue = self._wholeParmAndInvalidValue('-t', inputStr)
dtFormatDic = DateTimeUtil.getDateAndTimeFormatDictionary(self.configMgr.dateTimeFormat)
timeFormat = dtFormatDic[DateTimeUtil.TIME_FORMAT_KEY]
self.commandError.parsedParmData[self.commandError.COMMAND_ERROR_TYPE_KEY] = self.commandError.COMMAND_ERROR_TYPE_PARTIAL_REQUEST
self.commandError.parsedParmData[self.commandError.COMMAND_ERROR_MSG_KEY] = self.commandError.PARTIAL_PRICE_COMMAND_TIME_FORMAT_INVALID_MSG.format(invalidPartialCommand, invalidValue, timeFormat)
# remove invalid time specification from parsedParData to avoid polluting next partial
# request !
self.commandPrice.parsedParmData[CommandPrice.HOUR_MINUTE] = None
return self.commandError
else:
minute = hourMinuteList[1]
hour = hourMinuteList[0] #in both cases, first item in hourMinuteList is hour
else:
hour = self.commandPrice.parsedParmData[CommandPrice.HOUR]
minute = self.commandPrice.parsedParmData[CommandPrice.MINUTE]
self._fillHourMinuteInfo(hour, minute)
if dayMonthYear != None:
if dayMonthYear == '0':
day = '0'
month = '0'
year = '0'
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_RT
else:
dayMonthYearList = dayMonthYear.split('/')
if len(dayMonthYearList) == 1: #only day specified, the case for -d12 for example (12th of current month)
day = dayMonthYearList[0]
if CommandPrice.DAY in self.commandPrice.parsedParmData:
month = self.commandPrice.parsedParmData[CommandPrice.MONTH]
year = self.commandPrice.parsedParmData[CommandPrice.YEAR]
else:
month = None
year = None
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO
elif len(dayMonthYearList) == 2:
day = dayMonthYearList[0]
month = dayMonthYearList[1]
if CommandPrice.YEAR in self.commandPrice.parsedParmData:
year = self.commandPrice.parsedParmData[CommandPrice.YEAR]
else: # year not provided and not obtained from previous full price command input.
# Will be set by PriceRequester which knows in which timezone we are
year = None
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO
elif len(dayMonthYearList) == 3:
day = dayMonthYearList[0]
month = dayMonthYearList[1]
year = dayMonthYearList[2]
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO
else: #invalid date format here !
if CommandPrice.DAY in self.commandPrice.parsedParmData:
day = self.commandPrice.parsedParmData[CommandPrice.DAY]
month = self.commandPrice.parsedParmData[CommandPrice.MONTH]
year = self.commandPrice.parsedParmData[CommandPrice.YEAR]
else:
day = None
month = None
year = None
else:
if CommandPrice.DAY in self.commandPrice.parsedParmData:
day = self.commandPrice.parsedParmData[CommandPrice.DAY]
month = self.commandPrice.parsedParmData[CommandPrice.MONTH]
year = self.commandPrice.parsedParmData[CommandPrice.YEAR]
if day == '0' and month == '0' and year == '0':
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_RT
else:
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] = CommandPrice.PRICE_TYPE_HISTO
else:
day = None
month = None
year = None
self._fillDayMonthYearInfo(day, month, year)
command = None
for optionType in CommandPrice.OPTION_TYPE_LIST:
commandPriceOptionDataConstantValue = self.commandPrice.getCommandPriceOptionComponentConstantValue(optionType, optionComponent='_DATA')
optionData = self.commandPrice.parsedParmData[commandPriceOptionDataConstantValue]
if optionData is not None:
# optionData is None if the full request has no option for this option type
# optionData == '' if the full request has an option with no data.
# For example eth btc 0 binance -v or eth btc 0 binance -vs
command = self._fillOptionValueInfo(optionType, optionData, requestType)
if isinstance(command, CommandError):
# in case an error was detected, we do not continue handling the options
break
if command:
return command
else:
# here, no option -v, -f or -p specified !
return self.commandPrice
def _tryExtractDateTimeValueFromPartialRequest(self, partialRequestStr):
"""
Handles a -d partial request containing time component which could not
be parsed sooner.
:param partialRequestStr: partial request string
:return: None, None if no time value or date and time components otherwise
"""
dateTimePattern = r"-d(\d+/\d+/\d+ \d+:\d+)|-d(\d+/\d+ \d+:\d+)|-d(\d+ \d+:\d+)|-d(\d+/\d+/\d+)|-d(\d+/\d+)|^-d(\d+)"
dateTimeInputStr = ''
for grps in re.finditer(dateTimePattern, partialRequestStr):
for elem in grps.groups():
if elem is not None:
dateTimeInputStr = elem
if ':' in dateTimeInputStr:
dateTimeValueLst = dateTimeInputStr.split(' ')
dayMonthYear = dateTimeValueLst[0]
hourMinute = dateTimeValueLst[1]
return dayMonthYear, hourMinute
else:
return None, None
def _isPreviousFullRequestActive(self):
"""
Checks if a full request was entered before the currently handled partial request.
Returns True if yes, False otherwise.
:return: True or False
"""
return self.commandPrice.parsedParmData[CommandPrice.CRYPTO] and \
self.commandPrice.parsedParmData[CommandPrice.UNIT] and \
self.commandPrice.parsedParmData[CommandPrice.PRICE_TYPE] and \
self.commandPrice.parsedParmData[CommandPrice.EXCHANGE]
def _rebuildPreviousRequestDateTimeValues(self):
hour = self.commandPrice.parsedParmData[CommandPrice.HOUR]
minute = self.commandPrice.parsedParmData[CommandPrice.MINUTE]
if hour and minute:
hourMinute = hour + ':' + minute
else:
hourMinute = None
year = self.commandPrice.parsedParmData[CommandPrice.YEAR]
if year:
dayMonthYear = self.commandPrice.parsedParmData[CommandPrice.DAY] + '/' + \
self.commandPrice.parsedParmData[CommandPrice.MONTH] + '/' + year
else:
dayMonthYear = self.commandPrice.parsedParmData[CommandPrice.DAY] + '/' + \
self.commandPrice.parsedParmData[CommandPrice.MONTH]
return dayMonthYear, hourMinute
def _fillHourMinuteInfo(self, hour, minute):
'''
Fill parsed parm data hour and minute fields and empty combined hour/minute field
:param hour:
:param minute:
:return:
'''
self.commandPrice.parsedParmData[CommandPrice.HOUR] = hour
self.commandPrice.parsedParmData[CommandPrice.MINUTE] = minute
self.commandPrice.parsedParmData[CommandPrice.HOUR_MINUTE] = None
def _fillDayMonthYearInfo(self, day, month, year):
'''
Fill parsed parm data day, month and year fields and empty combined daa/month/year field
:param day:
:param month:
:param year:
:return:
'''
self.commandPrice.parsedParmData[CommandPrice.DAY] = day
self.commandPrice.parsedParmData[CommandPrice.MONTH] = month
self.commandPrice.parsedParmData[CommandPrice.YEAR] = year
self.commandPrice.parsedParmData[CommandPrice.DAY_MONTH_YEAR] = None
def _fillOptionValueInfo(self, optionType, optionData, requestType):
'''
This method is called in case of both full and partial request handling in order to
complete filling the option value info in the CommandPrice parsed parm data dictionary.
It fills the parsed parm data option value amount and option value symbol fields and
erases the combined option value data field.
:param optionData: the data following the -v partial command specification
:param requestType: indicate if we are handling a full or a partial request
:return: self.commandPrice or self.commandError in case the -v option is invalid
'''
optionSaveFlag = None
optionErase = None
optionSymbol = None
optionAmount = None
requesterOptionPattern = self._getRequesterOptionPattern(optionType)
match = re.match(requesterOptionPattern, optionData)
if match:
# if len(match.groups()) == 5:
# # case if OPTION_FIAT_PARM_DATA_PATTERN or OPTION_PRICE_PARM_DATA_PATTERN
# optionSaveFlag = match.group(1)
# optionAmount = match.group(2)
# optionSymbol = match.group(3)
# optionExchange = match.group(4)
# optionErase = match.group(5)
# else:
# # case if OPTION_VALUE_PARM_DATA_PATTERN
# optionSaveFlag = match.group(1)
# optionAmount = match.group(2)
# optionSymbol = match.group(3)
# optionErase = match.group(4)
groupNumber = len(match.groups())
if optionType == 'VALUE':
if groupNumber == 4:
optionSaveFlag = match.group(1)
optionAmount = match.group(2)
optionSymbol = match.group(3)
optionErase = match.group(4)
elif optionType == 'FIAT':
if groupNumber == 4:
optionSaveFlag = match.group(1)
optionSymbol = match.group(2)
optionExchange = match.group(3)
optionErase = match.group(4)
self.commandPrice.parsedParmData[CommandPrice.OPTION_FIAT_EXCHANGE] = optionExchange
if (optionErase == None and (optionSymbol == None or len(optionSymbol) < CURRENCY_SYMBOL_MIN_LENGTH)) or \
(optionErase == '0' and optionSaveFlag == None and optionSymbol == None and optionData != '0'):
# solving very difficult error message formatting for invalid -f option erase. -f0.01 in partial
# and full requests or -fs0.01 in partial and full requests. I spent days solving it !
return self._handleInvalidOptionFormat(optionData, optionType, requestType)
elif optionType == 'PRICE':
if groupNumber == 2:
optionSaveFlag = match.group(1)
optionAmount = match.group(2)
if optionAmount == '0':
optionErase = '0'
else:
optionErase = None
if self._isNumber(optionAmount):
self.commandPrice.parsedParmData[CommandPrice.OPTION_PRICE_AMOUNT] = optionAmount
else:
optionAmount = None
elif optionType == 'RESULT':
if groupNumber == 3:
optionSaveFlag = match.group(1)
optionSymbol = None # not used for option result
optionAmount = match.group(2)
if optionAmount == '0':
optionErase = '0'
else:
optionErase = None
if '-' not in optionAmount and self._isNumber(optionAmount):
# the case for -r20.45 for example
return self._handleInvalidOptionFormat(optionData, optionType, requestType)
else:
self.commandPrice.parsedParmData[CommandPrice.OPTION_RESULT_AMOUNT] = optionAmount
elif optionType == 'LIMIT':
if groupNumber == 5:
optionSaveFlag = match.group(1)
optionAmount = match.group(2)
optionSymbol = match.group(3)
optionExchange = match.group(4)
optionErase = match.group(5)
self.commandPrice.parsedParmData[CommandPrice.OPTION_LIMIT_EXCHANGE] = optionExchange
if optionErase == None:
if optionSymbol and optionSymbol.isdigit():
# case when no currency symbol entered, like -v0.01 or -vs0.01 instead of -v0.01btc/-vs0.01btc
optionAmount += optionSymbol
optionSymbol = ''
if optionAmount != None:
# if optionType == FIAT, optionAmount == None !
commandPriceOptionAmountConstantValue = self.commandPrice.getCommandPriceOptionComponentConstantValue(optionType, optionComponent='_AMOUNT')
self.commandPrice.parsedParmData[commandPriceOptionAmountConstantValue] = optionAmount
commandPriceOptionSymbolConstantValue = self.commandPrice.getCommandPriceOptionComponentConstantValue(optionType, optionComponent='_SYMBOL')
self.commandPrice.parsedParmData[commandPriceOptionSymbolConstantValue] = optionSymbol
if requestType == self.REQUEST_TYPE_PARTIAL:
# only in case of partial request containing a value command may the passed
# optionValueData contain a s option.
# for full requests containing a value command, the s option if present was parsed
# in _buildFullCommandPriceOrderFreeParmsDic() and is not contained in the passed
# optionValueData !
commandPriceOptionSaveConstantValue = self.commandPrice.getCommandPriceOptionComponentConstantValue(optionType, optionComponent='_SAVE')
if optionSaveFlag.upper() == 'S':
self.commandPrice.parsedParmData[commandPriceOptionSaveConstantValue] = True
else: