25
25
from lean .constants import DEFAULT_ENGINE_IMAGE
26
26
from lean .container import container
27
27
from lean .models .docker import DockerImage
28
- from tests .test_helpers import create_fake_lean_cli_directory
28
+ from tests .test_helpers import create_fake_lean_cli_directory , reset_state_installed_modules
29
29
from tests .conftest import initialize_container
30
+ from click .testing import Result
30
31
31
32
ENGINE_IMAGE = DockerImage .parse (DEFAULT_ENGINE_IMAGE )
32
33
@@ -413,7 +414,21 @@ def test_live_sets_dependent_configurations_from_modules_json_based_on_environme
413
414
"Terminal Link" : terminal_link_required_options ,
414
415
"Kraken" : brokerage_required_options ["Kraken" ],
415
416
"TDAmeritrade" : brokerage_required_options ["TDAmeritrade" ],
416
- "Bybit" : brokerage_required_options ["Bybit" ]
417
+ "Bybit" : brokerage_required_options ["Bybit" ],
418
+ }
419
+
420
+ data_provider_required_options = {
421
+ "IEX" : {
422
+ "iex-cloud-api-key" : "123" ,
423
+ "iex-price-plan" : "Launch" ,
424
+ },
425
+ "Polygon" : {
426
+ "polygon-api-key" : "123" ,
427
+ },
428
+ "AlphaVantage" : {
429
+ "alpha-vantage-api-key" : "111" ,
430
+ "alpha-vantage-price-plan" : "Free"
431
+ }
417
432
}
418
433
419
434
@@ -530,7 +545,7 @@ def test_live_non_interactive_raise_error_when_missing_data_provider_live_option
530
545
531
546
error_msg = str (result .exc_info [1 ]).split ()
532
547
533
- assert "data-provider-live" in error_msg
548
+ assert "-- data-provider-live" in error_msg
534
549
assert "data-queue-handler" not in error_msg
535
550
536
551
assert result .exit_code != 0
@@ -1026,3 +1041,191 @@ def test_live_passes_live_holdings_to_lean_runner_when_given_as_option(brokerage
1026
1041
return
1027
1042
1028
1043
assert args [0 ]["live-holdings" ] == holding_list
1044
+
1045
+ def test_live_non_interactive_deploy_with_live_and_historical_provider_missed_historical_not_optional_config () -> None :
1046
+ create_fake_lean_cli_directory ()
1047
+ create_fake_environment ("live-paper" , True )
1048
+
1049
+ container .initialize (docker_manager = mock .Mock (), lean_runner = mock .Mock (), api_client = mock .MagicMock ())
1050
+
1051
+ provider_live_option = ["--data-provider-live" , "IEX" ,
1052
+ "--iex-cloud-api-key" , "123" ,
1053
+ "--iex-price-plan" , "Launch" ]
1054
+
1055
+ provider_history_option = ["--data-provider-historical" , "Polygon" ]
1056
+ # "--polygon-api-key", "123"]
1057
+
1058
+ result = CliRunner ().invoke (lean , ["live" , "deploy" , "--brokerage" , "Paper Trading" ,
1059
+ * provider_live_option ,
1060
+ * provider_history_option ,
1061
+ "Python Project" ,
1062
+ ])
1063
+ error_msg = str (result .exc_info [1 ]).split ()
1064
+
1065
+ assert "--polygon-api-key" in error_msg
1066
+ assert "--iex-cloud-api-key" not in error_msg
1067
+
1068
+ assert result .exit_code == 1
1069
+
1070
+ def test_live_non_interactive_deploy_with_live_and_historical_provider_missed_live_not_optional_config () -> None :
1071
+ create_fake_lean_cli_directory ()
1072
+ create_fake_environment ("live-paper" , True )
1073
+
1074
+ container .initialize (docker_manager = mock .Mock (), lean_runner = mock .Mock (), api_client = mock .MagicMock ())
1075
+
1076
+ provider_live_option = ["--data-provider-live" , "IEX" ,
1077
+ "--iex-cloud-api-key" , "123" ]
1078
+ #"--iex-price-plan", "Launch"]
1079
+
1080
+ provider_history_option = ["--data-provider-historical" , "Polygon" , "--polygon-api-key" , "123" ]
1081
+
1082
+ result = CliRunner ().invoke (lean , ["live" , "deploy" , "--brokerage" , "Paper Trading" ,
1083
+ * provider_live_option ,
1084
+ * provider_history_option ,
1085
+ "Python Project" ,
1086
+ ])
1087
+
1088
+ error_msg = str (result .exc_info [1 ]).split ()
1089
+
1090
+ assert "--iex-price-plan" in error_msg
1091
+ assert "--polygon-api-key" not in error_msg
1092
+
1093
+ assert result .exit_code == 1
1094
+
1095
+ def test_live_non_interactive_deploy_with_real_brokerage_without_credentials () -> None :
1096
+ create_fake_lean_cli_directory ()
1097
+ create_fake_environment ("live-paper" , True )
1098
+
1099
+ container .initialize (docker_manager = mock .Mock (), lean_runner = mock .Mock (), api_client = mock .MagicMock ())
1100
+
1101
+ # create fake environment has IB configs already
1102
+ brokerage = ["--brokerage" , "OANDA" ]
1103
+
1104
+ provider_live_option = ["--data-provider-live" , "IEX" ,
1105
+ "--iex-cloud-api-key" , "123" ,
1106
+ "--iex-price-plan" , "Launch" ]
1107
+
1108
+ result = CliRunner ().invoke (lean , ["live" , "deploy" ,
1109
+ * brokerage ,
1110
+ * provider_live_option ,
1111
+ "Python Project" ,
1112
+ ])
1113
+ assert result .exit_code == 1
1114
+
1115
+ error_msg = str (result .exc_info [1 ]).split ()
1116
+
1117
+ assert "--oanda-account-id" in error_msg
1118
+ assert "--oanda-access-token" in error_msg
1119
+ assert "--oanda-environment" in error_msg
1120
+ assert "--iex-price-plan" not in error_msg
1121
+
1122
+ def create_lean_option (brokerage_name : str , data_provider_live_name : str , data_provider_historical_name : str , api_client : any ) -> Result :
1123
+ reset_state_installed_modules ()
1124
+ create_fake_lean_cli_directory ()
1125
+ create_fake_environment ("live-paper" , True )
1126
+
1127
+ initialize_container (api_client_to_use = api_client )
1128
+
1129
+ option = ["--brokerage" , brokerage_name ]
1130
+ for key , value in brokerage_required_options [brokerage_name ].items ():
1131
+ option .extend ([f"--{ key } " , value ])
1132
+
1133
+ data_feed_required_options .update (data_provider_required_options )
1134
+
1135
+ option .extend (["--data-provider-live" , data_provider_live_name ])
1136
+ for key , value in data_feed_required_options [data_provider_live_name ].items ():
1137
+ if f"--{ key } " not in option :
1138
+ option .extend ([f"--{ key } " , value ])
1139
+
1140
+ if data_provider_historical_name is not None :
1141
+ option .extend (["--data-provider-historical" , data_provider_historical_name ])
1142
+ if data_provider_historical_name is not "Local" :
1143
+ for key , value in data_feed_required_options [data_provider_historical_name ].items ():
1144
+ if f"--{ key } " not in option :
1145
+ option .extend ([f"--{ key } " , value ])
1146
+
1147
+ result = CliRunner ().invoke (lean , ["live" , "deploy" ,
1148
+ * option ,
1149
+ "Python Project" ,
1150
+ ])
1151
+ assert result .exit_code == 0
1152
+ return result
1153
+
1154
+ @pytest .mark .parametrize ("brokerage_name,data_provider_live_name,data_provider_historical_name,brokerage_product_id,data_provider_live_product_id,data_provider_historical_id" ,
1155
+ [("Interactive Brokers" , "IEX" , "Polygon" , "181" , "333" , "306" ),
1156
+ ("Paper Trading" , "IEX" , "Polygon" , None , "333" , "306" ),
1157
+ ("Tradier" , "IEX" , "AlphaVantage" , "185" , "333" , "334" ),
1158
+ ("Paper Trading" , "IEX" , "Local" , None , "333" , "222" )])
1159
+ def test_live_deploy_with_different_brokerage_and_different_live_data_provider_and_historical_data_provider (brokerage_name : str , data_provider_live_name : str , data_provider_historical_name : str , brokerage_product_id : str , data_provider_live_product_id : str , data_provider_historical_id : str ) -> None :
1160
+ api_client = mock .MagicMock ()
1161
+ create_lean_option (brokerage_name , data_provider_live_name , data_provider_historical_name , api_client )
1162
+
1163
+ is_exists = []
1164
+ if brokerage_product_id is None and data_provider_historical_name != "Local" :
1165
+ assert len (api_client .method_calls ) == 3
1166
+ for m_c , id in zip (api_client .method_calls , [data_provider_live_product_id , data_provider_historical_id ]):
1167
+ if id in m_c [1 ]:
1168
+ is_exists .append (True )
1169
+ assert is_exists
1170
+ assert len (is_exists ) == 2
1171
+ elif brokerage_product_id is None and data_provider_historical_name == "Local" :
1172
+ assert len (api_client .method_calls ) == 2
1173
+ if data_provider_live_product_id in api_client .method_calls [0 ][1 ]:
1174
+ is_exists .append (True )
1175
+ assert is_exists
1176
+ assert len (is_exists ) == 1
1177
+ else :
1178
+ assert len (api_client .method_calls ) == 3
1179
+ for m_c , id in zip (api_client .method_calls , [brokerage_product_id , data_provider_live_product_id , data_provider_historical_id ]):
1180
+ if id in f"{ m_c [1 ]} " :
1181
+ is_exists .append (True )
1182
+ assert is_exists
1183
+ assert len (is_exists ) == 3
1184
+
1185
+ @pytest .mark .parametrize ("brokerage_name,data_provider_live_name,brokerage_product_id,data_provider_live_product_id" ,
1186
+ [("Interactive Brokers" , "IEX" , "181" , "333" ),
1187
+ ("Tradier" , "IEX" , "185" , "333" )])
1188
+ def test_live_non_interactive_deploy_with_different_brokerage_and_different_live_data_provider (brokerage_name : str , data_provider_live_name : str , brokerage_product_id : str , data_provider_live_product_id : str ) -> None :
1189
+ api_client = mock .MagicMock ()
1190
+ create_lean_option (brokerage_name , data_provider_live_name , None , api_client )
1191
+
1192
+ assert len (api_client .method_calls ) == 2
1193
+ is_exists = []
1194
+ for m_c , id in zip (api_client .method_calls , [brokerage_product_id , data_provider_live_product_id ]):
1195
+ if id in m_c [1 ]:
1196
+ is_exists .append (True )
1197
+
1198
+ assert is_exists
1199
+ assert len (is_exists ) == 2
1200
+
1201
+ @pytest .mark .parametrize ("brokerage_name,data_provider_live_name,brokerage_product_id" ,
1202
+ [("Bybit" , "Bybit" , "305" ),
1203
+ ("Coinbase Advanced Trade" , "Coinbase Advanced Trade" , "183" ),
1204
+ ("Interactive Brokers" , "Interactive Brokers" , "181" ),
1205
+ ("Tradier" , "Tradier" , "185" )])
1206
+ def test_live_non_interactive_deploy_with_different_brokerage_with_the_same_live_data_provider (brokerage_name : str , data_provider_live_name : str , brokerage_product_id : str ) -> None :
1207
+ api_client = mock .MagicMock ()
1208
+ create_lean_option (brokerage_name , data_provider_live_name , None , api_client )
1209
+
1210
+ print (api_client .call_args_list )
1211
+ print (api_client .call_args )
1212
+
1213
+ for m_c in api_client .method_calls :
1214
+ if brokerage_product_id in m_c [1 ]:
1215
+ is_exist = True
1216
+
1217
+ assert is_exist
1218
+
1219
+ @pytest .mark .parametrize ("brokerage_name,data_provider_live_name,data_provider_live_product_id" ,
1220
+ [("Paper Trading" , "IEX" , "333" ),
1221
+ ("Paper Trading" , "Polygon" , "306" )])
1222
+ def test_live_non_interactive_deploy_paper_brokerage_different_live_data_provider (brokerage_name : str , data_provider_live_name : str , data_provider_live_product_id : str ) -> None :
1223
+ api_client = mock .MagicMock ()
1224
+ create_lean_option (brokerage_name , data_provider_live_name , None , api_client )
1225
+
1226
+ assert len (api_client .method_calls ) == 2
1227
+ for m_c in api_client .method_calls :
1228
+ if data_provider_live_product_id in m_c [1 ]:
1229
+ is_exist = True
1230
+
1231
+ assert is_exist
0 commit comments