tigeropen 3.5.9__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.
- build/lib/dist/tigeropen-3.5.8.tar.gz +0 -0
- build/lib/tigeropen/__init__.py +7 -0
- build/lib/tigeropen/cli/__init__.py +7 -0
- build/lib/tigeropen/cli/account_cmd.py +84 -0
- build/lib/tigeropen/cli/client_factory.py +79 -0
- build/lib/tigeropen/cli/config_cmd.py +129 -0
- build/lib/tigeropen/cli/formatting.py +117 -0
- build/lib/tigeropen/cli/main.py +197 -0
- build/lib/tigeropen/cli/push_cmd.py +118 -0
- build/lib/tigeropen/cli/quote_cmd.py +599 -0
- build/lib/tigeropen/cli/trade_cmd.py +226 -0
- build/lib/tigeropen/common/__init__.py +6 -0
- build/lib/tigeropen/common/consts/__init__.py +279 -0
- build/lib/tigeropen/common/consts/filter_fields.py +471 -0
- build/lib/tigeropen/common/consts/fundamental_fields.py +850 -0
- build/lib/tigeropen/common/consts/params.py +30 -0
- build/lib/tigeropen/common/consts/push_destinations.py +12 -0
- build/lib/tigeropen/common/consts/push_subscriptions.py +13 -0
- build/lib/tigeropen/common/consts/push_types.py +66 -0
- build/lib/tigeropen/common/consts/quote_keys.py +29 -0
- build/lib/tigeropen/common/consts/service_types.py +136 -0
- build/lib/tigeropen/common/consts/tick_constants.py +94 -0
- build/lib/tigeropen/common/exceptions.py +25 -0
- build/lib/tigeropen/common/model.py +36 -0
- build/lib/tigeropen/common/request.py +32 -0
- build/lib/tigeropen/common/response.py +28 -0
- build/lib/tigeropen/common/util/__init__.py +6 -0
- build/lib/tigeropen/common/util/account_util.py +17 -0
- build/lib/tigeropen/common/util/common_utils.py +53 -0
- build/lib/tigeropen/common/util/contract_utils.py +110 -0
- build/lib/tigeropen/common/util/order_utils.py +242 -0
- build/lib/tigeropen/common/util/price_util.py +90 -0
- build/lib/tigeropen/common/util/signature_utils.py +107 -0
- build/lib/tigeropen/common/util/string_utils.py +33 -0
- build/lib/tigeropen/common/util/tick_util.py +27 -0
- build/lib/tigeropen/common/util/web_utils.py +39 -0
- build/lib/tigeropen/examples/__init__.py +6 -0
- build/lib/tigeropen/examples/ai/mcp_server/MANIFEST.in +2 -0
- build/lib/tigeropen/examples/ai/mcp_server/README.md +84 -0
- build/lib/tigeropen/examples/ai/mcp_server/__init__.py +3 -0
- build/lib/tigeropen/examples/ai/mcp_server/dist/tigermcp-0.1.7.tar.gz +0 -0
- build/lib/tigeropen/examples/ai/mcp_server/setup.py +35 -0
- build/lib/tigeropen/examples/ai/mcp_server/tigermcp/__init__.py +3 -0
- build/lib/tigeropen/examples/ai/mcp_server/tigermcp/server.py +1264 -0
- build/lib/tigeropen/examples/ai/mcp_server/tigermcp/version.py +1 -0
- build/lib/tigeropen/examples/client_config.py +25 -0
- build/lib/tigeropen/examples/financial_demo.py +135 -0
- build/lib/tigeropen/examples/nasdaq100.py +309 -0
- build/lib/tigeropen/examples/option_helpers/__init__.py +4 -0
- build/lib/tigeropen/examples/option_helpers/extra_calculator.py +58 -0
- build/lib/tigeropen/examples/option_helpers/helpers.py +308 -0
- build/lib/tigeropen/examples/option_helpers/probability_calculator.py +270 -0
- build/lib/tigeropen/examples/option_helpers/util.py +589 -0
- build/lib/tigeropen/examples/push_client_demo.py +395 -0
- build/lib/tigeropen/examples/push_client_stomp_demo.py +270 -0
- build/lib/tigeropen/examples/quote_client_demo.py +323 -0
- build/lib/tigeropen/examples/trade_client_demo.py +228 -0
- build/lib/tigeropen/fundamental/__init__.py +1 -0
- build/lib/tigeropen/fundamental/domain/__init__.py +1 -0
- build/lib/tigeropen/fundamental/request/__init__.py +1 -0
- build/lib/tigeropen/fundamental/request/model.py +363 -0
- build/lib/tigeropen/fundamental/response/__init__.py +1 -0
- build/lib/tigeropen/fundamental/response/corporate_dividend_response.py +31 -0
- build/lib/tigeropen/fundamental/response/corporate_earnings_calendar_response.py +29 -0
- build/lib/tigeropen/fundamental/response/corporate_split_response.py +28 -0
- build/lib/tigeropen/fundamental/response/dataframe_response.py +27 -0
- build/lib/tigeropen/fundamental/response/financial_daily_response.py +28 -0
- build/lib/tigeropen/fundamental/response/financial_exchange_rate_response.py +29 -0
- build/lib/tigeropen/fundamental/response/financial_report_response.py +29 -0
- build/lib/tigeropen/fundamental/response/industry_response.py +62 -0
- build/lib/tigeropen/push/__init__.py +28 -0
- build/lib/tigeropen/push/network/__init__.py +0 -0
- build/lib/tigeropen/push/network/connect.py +85 -0
- build/lib/tigeropen/push/network/exception.py +26 -0
- build/lib/tigeropen/push/network/listener.py +229 -0
- build/lib/tigeropen/push/network/protocal.py +45 -0
- build/lib/tigeropen/push/network/transport.py +818 -0
- build/lib/tigeropen/push/network/utils.py +107 -0
- build/lib/tigeropen/push/pb/AssetData.proto +23 -0
- build/lib/tigeropen/push/pb/AssetData_pb2.py +36 -0
- build/lib/tigeropen/push/pb/AssetData_pb2.pyi +35 -0
- build/lib/tigeropen/push/pb/KlineData.proto +16 -0
- build/lib/tigeropen/push/pb/KlineData_pb2.py +36 -0
- build/lib/tigeropen/push/pb/KlineData_pb2.pyi +31 -0
- build/lib/tigeropen/push/pb/OptionTopData.proto +40 -0
- build/lib/tigeropen/push/pb/OptionTopData_pb2.py +42 -0
- build/lib/tigeropen/push/pb/OptionTopData_pb2.pyi +69 -0
- build/lib/tigeropen/push/pb/OrderStatusData.proto +51 -0
- build/lib/tigeropen/push/pb/OrderStatusData_pb2.py +36 -0
- build/lib/tigeropen/push/pb/OrderStatusData_pb2.pyi +96 -0
- build/lib/tigeropen/push/pb/OrderTransactionData.proto +24 -0
- build/lib/tigeropen/push/pb/OrderTransactionData_pb2.py +36 -0
- build/lib/tigeropen/push/pb/OrderTransactionData_pb2.pyi +43 -0
- build/lib/tigeropen/push/pb/PositionData.proto +29 -0
- build/lib/tigeropen/push/pb/PositionData_pb2.py +36 -0
- build/lib/tigeropen/push/pb/PositionData_pb2.pyi +53 -0
- build/lib/tigeropen/push/pb/PushData.proto +32 -0
- build/lib/tigeropen/push/pb/PushData_pb2.py +60 -0
- build/lib/tigeropen/push/pb/PushData_pb2.pyi +57 -0
- build/lib/tigeropen/push/pb/QuoteBBOData.proto +20 -0
- build/lib/tigeropen/push/pb/QuoteBBOData_pb2.py +38 -0
- build/lib/tigeropen/push/pb/QuoteBBOData_pb2.pyi +29 -0
- build/lib/tigeropen/push/pb/QuoteBasicData.proto +38 -0
- build/lib/tigeropen/push/pb/QuoteBasicData_pb2.py +40 -0
- build/lib/tigeropen/push/pb/QuoteBasicData_pb2.pyi +57 -0
- build/lib/tigeropen/push/pb/QuoteData.proto +57 -0
- build/lib/tigeropen/push/pb/QuoteData_pb2.py +39 -0
- build/lib/tigeropen/push/pb/QuoteData_pb2.pyi +85 -0
- build/lib/tigeropen/push/pb/QuoteDepthData.proto +20 -0
- build/lib/tigeropen/push/pb/QuoteDepthData_pb2.py +38 -0
- build/lib/tigeropen/push/pb/QuoteDepthData_pb2.pyi +31 -0
- build/lib/tigeropen/push/pb/Request.proto +28 -0
- build/lib/tigeropen/push/pb/Request_pb2.py +42 -0
- build/lib/tigeropen/push/pb/Request_pb2.pyi +47 -0
- build/lib/tigeropen/push/pb/Response.proto +15 -0
- build/lib/tigeropen/push/pb/Response_pb2.py +88 -0
- build/lib/tigeropen/push/pb/Response_pb2.pyi +35 -0
- build/lib/tigeropen/push/pb/SocketCommon.proto +46 -0
- build/lib/tigeropen/push/pb/SocketCommon_pb2.py +42 -0
- build/lib/tigeropen/push/pb/SocketCommon_pb2.pyi +72 -0
- build/lib/tigeropen/push/pb/StockTopData.proto +20 -0
- build/lib/tigeropen/push/pb/StockTopData_pb2.py +40 -0
- build/lib/tigeropen/push/pb/StockTopData_pb2.pyi +32 -0
- build/lib/tigeropen/push/pb/TickData.proto +19 -0
- build/lib/tigeropen/push/pb/TickData_pb2.py +38 -0
- build/lib/tigeropen/push/pb/TickData_pb2.pyi +35 -0
- build/lib/tigeropen/push/pb/TradeTickData.proto +26 -0
- build/lib/tigeropen/push/pb/TradeTickData_pb2.py +38 -0
- build/lib/tigeropen/push/pb/TradeTickData_pb2.pyi +45 -0
- build/lib/tigeropen/push/pb/__init__.py +7 -0
- build/lib/tigeropen/push/pb/readme.md +5 -0
- build/lib/tigeropen/push/pb/trade_tick.py +28 -0
- build/lib/tigeropen/push/pb/util.py +251 -0
- build/lib/tigeropen/push/protobuf_push_client.py +556 -0
- build/lib/tigeropen/push/push_client.py +399 -0
- build/lib/tigeropen/push/stomp_push_client.py +609 -0
- build/lib/tigeropen/push/thread_pool.py +59 -0
- build/lib/tigeropen/quote/__init__.py +6 -0
- build/lib/tigeropen/quote/domain/__init__.py +6 -0
- build/lib/tigeropen/quote/domain/bar.py +22 -0
- build/lib/tigeropen/quote/domain/capital_distribution.py +19 -0
- build/lib/tigeropen/quote/domain/filter.py +243 -0
- build/lib/tigeropen/quote/domain/market_status.py +20 -0
- build/lib/tigeropen/quote/domain/option_analysis.py +51 -0
- build/lib/tigeropen/quote/domain/quote_brief.py +60 -0
- build/lib/tigeropen/quote/domain/stock_broker.py +33 -0
- build/lib/tigeropen/quote/domain/tick.py +18 -0
- build/lib/tigeropen/quote/domain/timeline.py +17 -0
- build/lib/tigeropen/quote/quote_client.py +3266 -0
- build/lib/tigeropen/quote/request/__init__.py +8 -0
- build/lib/tigeropen/quote/request/model.py +1614 -0
- build/lib/tigeropen/quote/response/__init__.py +6 -0
- build/lib/tigeropen/quote/response/broker_hold_response.py +23 -0
- build/lib/tigeropen/quote/response/capital_distribution_response.py +24 -0
- build/lib/tigeropen/quote/response/capital_flow_response.py +27 -0
- build/lib/tigeropen/quote/response/fund_contracts_response.py +28 -0
- build/lib/tigeropen/quote/response/future_briefs_response.py +36 -0
- build/lib/tigeropen/quote/response/future_contract_response.py +44 -0
- build/lib/tigeropen/quote/response/future_depth_response.py +31 -0
- build/lib/tigeropen/quote/response/future_exchange_response.py +37 -0
- build/lib/tigeropen/quote/response/future_history_main_contract_response.py +28 -0
- build/lib/tigeropen/quote/response/future_quote_bar_response.py +42 -0
- build/lib/tigeropen/quote/response/future_quote_ticks_response.py +25 -0
- build/lib/tigeropen/quote/response/future_trading_times_response.py +42 -0
- build/lib/tigeropen/quote/response/kline_quota_response.py +21 -0
- build/lib/tigeropen/quote/response/market_scanner_response.py +48 -0
- build/lib/tigeropen/quote/response/market_status_response.py +51 -0
- build/lib/tigeropen/quote/response/option_analysis_response.py +64 -0
- build/lib/tigeropen/quote/response/option_briefs_response.py +69 -0
- build/lib/tigeropen/quote/response/option_chains_response.py +42 -0
- build/lib/tigeropen/quote/response/option_depth_response.py +37 -0
- build/lib/tigeropen/quote/response/option_expirations_response.py +34 -0
- build/lib/tigeropen/quote/response/option_quote_bar_response.py +60 -0
- build/lib/tigeropen/quote/response/option_quote_ticks_response.py +54 -0
- build/lib/tigeropen/quote/response/option_symbols_response.py +25 -0
- build/lib/tigeropen/quote/response/option_timeline_response.py +57 -0
- build/lib/tigeropen/quote/response/quote_bar_response.py +36 -0
- build/lib/tigeropen/quote/response/quote_brief_response.py +53 -0
- build/lib/tigeropen/quote/response/quote_dataframe_response.py +35 -0
- build/lib/tigeropen/quote/response/quote_delay_briefs_response.py +28 -0
- build/lib/tigeropen/quote/response/quote_depth_response.py +31 -0
- build/lib/tigeropen/quote/response/quote_grab_permission_response.py +21 -0
- build/lib/tigeropen/quote/response/quote_hour_trading_timeline_response.py +66 -0
- build/lib/tigeropen/quote/response/quote_overnight_response.py +19 -0
- build/lib/tigeropen/quote/response/quote_ticks_response.py +66 -0
- build/lib/tigeropen/quote/response/quote_timeline_history_response.py +65 -0
- build/lib/tigeropen/quote/response/quote_timeline_response.py +81 -0
- build/lib/tigeropen/quote/response/stock_briefs_response.py +36 -0
- build/lib/tigeropen/quote/response/stock_broker_response.py +46 -0
- build/lib/tigeropen/quote/response/stock_details_response.py +135 -0
- build/lib/tigeropen/quote/response/stock_short_interest_response.py +43 -0
- build/lib/tigeropen/quote/response/stock_trade_meta_response.py +39 -0
- build/lib/tigeropen/quote/response/symbol_names_response.py +24 -0
- build/lib/tigeropen/quote/response/symbols_response.py +23 -0
- build/lib/tigeropen/quote/response/trade_rank_response.py +28 -0
- build/lib/tigeropen/quote/response/trading_calendar_response.py +21 -0
- build/lib/tigeropen/quote/response/warrant_briefs_response.py +24 -0
- build/lib/tigeropen/quote/response/warrant_filter_response.py +27 -0
- build/lib/tigeropen/tiger_open_client.py +278 -0
- build/lib/tigeropen/tiger_open_config.py +574 -0
- build/lib/tigeropen/trade/__init__.py +6 -0
- build/lib/tigeropen/trade/domain/__init__.py +6 -0
- build/lib/tigeropen/trade/domain/account.py +269 -0
- build/lib/tigeropen/trade/domain/contract.py +164 -0
- build/lib/tigeropen/trade/domain/option_exercise.py +94 -0
- build/lib/tigeropen/trade/domain/order.py +314 -0
- build/lib/tigeropen/trade/domain/position.py +78 -0
- build/lib/tigeropen/trade/domain/prime_account.py +149 -0
- build/lib/tigeropen/trade/domain/profile.py +20 -0
- build/lib/tigeropen/trade/domain/transfer.py +157 -0
- build/lib/tigeropen/trade/request/__init__.py +6 -0
- build/lib/tigeropen/trade/request/model.py +2162 -0
- build/lib/tigeropen/trade/response/__init__.py +14 -0
- build/lib/tigeropen/trade/response/account_profile_response.py +29 -0
- build/lib/tigeropen/trade/response/aggregate_assets_response.py +18 -0
- build/lib/tigeropen/trade/response/analytics_asset_response.py +29 -0
- build/lib/tigeropen/trade/response/assets_response.py +93 -0
- build/lib/tigeropen/trade/response/contracts_response.py +42 -0
- build/lib/tigeropen/trade/response/forex_order_response.py +20 -0
- build/lib/tigeropen/trade/response/fund_details_response.py +29 -0
- build/lib/tigeropen/trade/response/funding_history_response.py +30 -0
- build/lib/tigeropen/trade/response/option_exercise_response.py +98 -0
- build/lib/tigeropen/trade/response/order_id_response.py +43 -0
- build/lib/tigeropen/trade/response/order_preview_response.py +17 -0
- build/lib/tigeropen/trade/response/orders_response.py +163 -0
- build/lib/tigeropen/trade/response/positions_response.py +79 -0
- build/lib/tigeropen/trade/response/prime_assets_response.py +36 -0
- build/lib/tigeropen/trade/response/segment_fund_response.py +69 -0
- build/lib/tigeropen/trade/response/transactions_response.py +44 -0
- build/lib/tigeropen/trade/response/transfer_response.py +104 -0
- build/lib/tigeropen/trade/trade_client.py +1761 -0
- dist/tigeropen-3.5.8.tar.gz +0 -0
- dist/tigeropen-3.5.9.tar.gz +0 -0
- tigeropen/__init__.py +7 -0
- tigeropen/cli/__init__.py +7 -0
- tigeropen/cli/account_cmd.py +84 -0
- tigeropen/cli/client_factory.py +79 -0
- tigeropen/cli/config_cmd.py +129 -0
- tigeropen/cli/formatting.py +117 -0
- tigeropen/cli/main.py +197 -0
- tigeropen/cli/push_cmd.py +118 -0
- tigeropen/cli/quote_cmd.py +599 -0
- tigeropen/cli/trade_cmd.py +226 -0
- tigeropen/common/__init__.py +6 -0
- tigeropen/common/consts/__init__.py +279 -0
- tigeropen/common/consts/filter_fields.py +471 -0
- tigeropen/common/consts/fundamental_fields.py +850 -0
- tigeropen/common/consts/params.py +30 -0
- tigeropen/common/consts/push_destinations.py +12 -0
- tigeropen/common/consts/push_subscriptions.py +13 -0
- tigeropen/common/consts/push_types.py +66 -0
- tigeropen/common/consts/quote_keys.py +29 -0
- tigeropen/common/consts/service_types.py +136 -0
- tigeropen/common/consts/tick_constants.py +94 -0
- tigeropen/common/exceptions.py +25 -0
- tigeropen/common/model.py +36 -0
- tigeropen/common/request.py +32 -0
- tigeropen/common/response.py +28 -0
- tigeropen/common/util/__init__.py +6 -0
- tigeropen/common/util/account_util.py +17 -0
- tigeropen/common/util/common_utils.py +53 -0
- tigeropen/common/util/contract_utils.py +110 -0
- tigeropen/common/util/order_utils.py +242 -0
- tigeropen/common/util/price_util.py +90 -0
- tigeropen/common/util/signature_utils.py +107 -0
- tigeropen/common/util/string_utils.py +33 -0
- tigeropen/common/util/tick_util.py +27 -0
- tigeropen/common/util/web_utils.py +39 -0
- tigeropen/examples/__init__.py +6 -0
- tigeropen/examples/ai/mcp_server/MANIFEST.in +2 -0
- tigeropen/examples/ai/mcp_server/README.md +84 -0
- tigeropen/examples/ai/mcp_server/__init__.py +3 -0
- tigeropen/examples/ai/mcp_server/dist/tigermcp-0.1.7.tar.gz +0 -0
- tigeropen/examples/ai/mcp_server/setup.py +35 -0
- tigeropen/examples/ai/mcp_server/tigermcp/__init__.py +3 -0
- tigeropen/examples/ai/mcp_server/tigermcp/server.py +1264 -0
- tigeropen/examples/ai/mcp_server/tigermcp/version.py +1 -0
- tigeropen/examples/client_config.py +25 -0
- tigeropen/examples/financial_demo.py +135 -0
- tigeropen/examples/nasdaq100.py +309 -0
- tigeropen/examples/option_helpers/__init__.py +4 -0
- tigeropen/examples/option_helpers/extra_calculator.py +58 -0
- tigeropen/examples/option_helpers/helpers.py +308 -0
- tigeropen/examples/option_helpers/probability_calculator.py +270 -0
- tigeropen/examples/option_helpers/util.py +589 -0
- tigeropen/examples/push_client_demo.py +395 -0
- tigeropen/examples/push_client_stomp_demo.py +270 -0
- tigeropen/examples/quote_client_demo.py +323 -0
- tigeropen/examples/trade_client_demo.py +228 -0
- tigeropen/fundamental/__init__.py +1 -0
- tigeropen/fundamental/domain/__init__.py +1 -0
- tigeropen/fundamental/request/__init__.py +1 -0
- tigeropen/fundamental/request/model.py +363 -0
- tigeropen/fundamental/response/__init__.py +1 -0
- tigeropen/fundamental/response/corporate_dividend_response.py +31 -0
- tigeropen/fundamental/response/corporate_earnings_calendar_response.py +29 -0
- tigeropen/fundamental/response/corporate_split_response.py +28 -0
- tigeropen/fundamental/response/dataframe_response.py +27 -0
- tigeropen/fundamental/response/financial_daily_response.py +28 -0
- tigeropen/fundamental/response/financial_exchange_rate_response.py +29 -0
- tigeropen/fundamental/response/financial_report_response.py +29 -0
- tigeropen/fundamental/response/industry_response.py +62 -0
- tigeropen/push/__init__.py +28 -0
- tigeropen/push/network/__init__.py +0 -0
- tigeropen/push/network/connect.py +85 -0
- tigeropen/push/network/exception.py +26 -0
- tigeropen/push/network/listener.py +229 -0
- tigeropen/push/network/protocal.py +45 -0
- tigeropen/push/network/transport.py +818 -0
- tigeropen/push/network/utils.py +107 -0
- tigeropen/push/pb/AssetData.proto +23 -0
- tigeropen/push/pb/AssetData_pb2.py +36 -0
- tigeropen/push/pb/AssetData_pb2.pyi +35 -0
- tigeropen/push/pb/KlineData.proto +16 -0
- tigeropen/push/pb/KlineData_pb2.py +36 -0
- tigeropen/push/pb/KlineData_pb2.pyi +31 -0
- tigeropen/push/pb/OptionTopData.proto +40 -0
- tigeropen/push/pb/OptionTopData_pb2.py +42 -0
- tigeropen/push/pb/OptionTopData_pb2.pyi +69 -0
- tigeropen/push/pb/OrderStatusData.proto +53 -0
- tigeropen/push/pb/OrderStatusData_pb2.py +36 -0
- tigeropen/push/pb/OrderStatusData_pb2.pyi +100 -0
- tigeropen/push/pb/OrderTransactionData.proto +24 -0
- tigeropen/push/pb/OrderTransactionData_pb2.py +36 -0
- tigeropen/push/pb/OrderTransactionData_pb2.pyi +43 -0
- tigeropen/push/pb/PositionData.proto +29 -0
- tigeropen/push/pb/PositionData_pb2.py +36 -0
- tigeropen/push/pb/PositionData_pb2.pyi +53 -0
- tigeropen/push/pb/PushData.proto +32 -0
- tigeropen/push/pb/PushData_pb2.py +60 -0
- tigeropen/push/pb/PushData_pb2.pyi +57 -0
- tigeropen/push/pb/QuoteBBOData.proto +20 -0
- tigeropen/push/pb/QuoteBBOData_pb2.py +38 -0
- tigeropen/push/pb/QuoteBBOData_pb2.pyi +29 -0
- tigeropen/push/pb/QuoteBasicData.proto +38 -0
- tigeropen/push/pb/QuoteBasicData_pb2.py +40 -0
- tigeropen/push/pb/QuoteBasicData_pb2.pyi +57 -0
- tigeropen/push/pb/QuoteData.proto +57 -0
- tigeropen/push/pb/QuoteData_pb2.py +39 -0
- tigeropen/push/pb/QuoteData_pb2.pyi +85 -0
- tigeropen/push/pb/QuoteDepthData.proto +20 -0
- tigeropen/push/pb/QuoteDepthData_pb2.py +38 -0
- tigeropen/push/pb/QuoteDepthData_pb2.pyi +31 -0
- tigeropen/push/pb/Request.proto +28 -0
- tigeropen/push/pb/Request_pb2.py +42 -0
- tigeropen/push/pb/Request_pb2.pyi +47 -0
- tigeropen/push/pb/Response.proto +15 -0
- tigeropen/push/pb/Response_pb2.py +88 -0
- tigeropen/push/pb/Response_pb2.pyi +35 -0
- tigeropen/push/pb/SocketCommon.proto +46 -0
- tigeropen/push/pb/SocketCommon_pb2.py +42 -0
- tigeropen/push/pb/SocketCommon_pb2.pyi +72 -0
- tigeropen/push/pb/StockTopData.proto +20 -0
- tigeropen/push/pb/StockTopData_pb2.py +40 -0
- tigeropen/push/pb/StockTopData_pb2.pyi +32 -0
- tigeropen/push/pb/TickData.proto +19 -0
- tigeropen/push/pb/TickData_pb2.py +38 -0
- tigeropen/push/pb/TickData_pb2.pyi +35 -0
- tigeropen/push/pb/TradeTickData.proto +26 -0
- tigeropen/push/pb/TradeTickData_pb2.py +38 -0
- tigeropen/push/pb/TradeTickData_pb2.pyi +45 -0
- tigeropen/push/pb/__init__.py +7 -0
- tigeropen/push/pb/readme.md +5 -0
- tigeropen/push/pb/trade_tick.py +28 -0
- tigeropen/push/pb/util.py +251 -0
- tigeropen/push/protobuf_push_client.py +556 -0
- tigeropen/push/push_client.py +399 -0
- tigeropen/push/stomp_push_client.py +609 -0
- tigeropen/push/thread_pool.py +59 -0
- tigeropen/quote/__init__.py +6 -0
- tigeropen/quote/domain/__init__.py +6 -0
- tigeropen/quote/domain/bar.py +22 -0
- tigeropen/quote/domain/capital_distribution.py +19 -0
- tigeropen/quote/domain/filter.py +243 -0
- tigeropen/quote/domain/market_status.py +20 -0
- tigeropen/quote/domain/option_analysis.py +51 -0
- tigeropen/quote/domain/quote_brief.py +60 -0
- tigeropen/quote/domain/stock_broker.py +33 -0
- tigeropen/quote/domain/tick.py +18 -0
- tigeropen/quote/domain/timeline.py +17 -0
- tigeropen/quote/quote_client.py +3266 -0
- tigeropen/quote/request/__init__.py +8 -0
- tigeropen/quote/request/model.py +1614 -0
- tigeropen/quote/response/__init__.py +6 -0
- tigeropen/quote/response/broker_hold_response.py +23 -0
- tigeropen/quote/response/capital_distribution_response.py +24 -0
- tigeropen/quote/response/capital_flow_response.py +27 -0
- tigeropen/quote/response/fund_contracts_response.py +28 -0
- tigeropen/quote/response/future_briefs_response.py +36 -0
- tigeropen/quote/response/future_contract_response.py +44 -0
- tigeropen/quote/response/future_depth_response.py +31 -0
- tigeropen/quote/response/future_exchange_response.py +37 -0
- tigeropen/quote/response/future_history_main_contract_response.py +28 -0
- tigeropen/quote/response/future_quote_bar_response.py +42 -0
- tigeropen/quote/response/future_quote_ticks_response.py +25 -0
- tigeropen/quote/response/future_trading_times_response.py +42 -0
- tigeropen/quote/response/kline_quota_response.py +21 -0
- tigeropen/quote/response/market_scanner_response.py +48 -0
- tigeropen/quote/response/market_status_response.py +51 -0
- tigeropen/quote/response/option_analysis_response.py +64 -0
- tigeropen/quote/response/option_briefs_response.py +69 -0
- tigeropen/quote/response/option_chains_response.py +42 -0
- tigeropen/quote/response/option_depth_response.py +37 -0
- tigeropen/quote/response/option_expirations_response.py +34 -0
- tigeropen/quote/response/option_quote_bar_response.py +60 -0
- tigeropen/quote/response/option_quote_ticks_response.py +54 -0
- tigeropen/quote/response/option_symbols_response.py +25 -0
- tigeropen/quote/response/option_timeline_response.py +57 -0
- tigeropen/quote/response/quote_bar_response.py +36 -0
- tigeropen/quote/response/quote_brief_response.py +53 -0
- tigeropen/quote/response/quote_dataframe_response.py +35 -0
- tigeropen/quote/response/quote_delay_briefs_response.py +28 -0
- tigeropen/quote/response/quote_depth_response.py +31 -0
- tigeropen/quote/response/quote_grab_permission_response.py +21 -0
- tigeropen/quote/response/quote_hour_trading_timeline_response.py +66 -0
- tigeropen/quote/response/quote_overnight_response.py +19 -0
- tigeropen/quote/response/quote_ticks_response.py +66 -0
- tigeropen/quote/response/quote_timeline_history_response.py +65 -0
- tigeropen/quote/response/quote_timeline_response.py +81 -0
- tigeropen/quote/response/stock_briefs_response.py +36 -0
- tigeropen/quote/response/stock_broker_response.py +46 -0
- tigeropen/quote/response/stock_details_response.py +135 -0
- tigeropen/quote/response/stock_short_interest_response.py +43 -0
- tigeropen/quote/response/stock_trade_meta_response.py +39 -0
- tigeropen/quote/response/symbol_names_response.py +24 -0
- tigeropen/quote/response/symbols_response.py +23 -0
- tigeropen/quote/response/trade_rank_response.py +28 -0
- tigeropen/quote/response/trading_calendar_response.py +21 -0
- tigeropen/quote/response/warrant_briefs_response.py +24 -0
- tigeropen/quote/response/warrant_filter_response.py +27 -0
- tigeropen/tiger_open_client.py +278 -0
- tigeropen/tiger_open_config.py +574 -0
- tigeropen/trade/__init__.py +6 -0
- tigeropen/trade/domain/__init__.py +6 -0
- tigeropen/trade/domain/account.py +269 -0
- tigeropen/trade/domain/contract.py +164 -0
- tigeropen/trade/domain/option_exercise.py +94 -0
- tigeropen/trade/domain/order.py +314 -0
- tigeropen/trade/domain/position.py +78 -0
- tigeropen/trade/domain/prime_account.py +149 -0
- tigeropen/trade/domain/profile.py +20 -0
- tigeropen/trade/domain/transfer.py +157 -0
- tigeropen/trade/request/__init__.py +6 -0
- tigeropen/trade/request/model.py +2162 -0
- tigeropen/trade/response/__init__.py +14 -0
- tigeropen/trade/response/account_profile_response.py +29 -0
- tigeropen/trade/response/aggregate_assets_response.py +18 -0
- tigeropen/trade/response/analytics_asset_response.py +29 -0
- tigeropen/trade/response/assets_response.py +93 -0
- tigeropen/trade/response/contracts_response.py +42 -0
- tigeropen/trade/response/forex_order_response.py +20 -0
- tigeropen/trade/response/fund_details_response.py +29 -0
- tigeropen/trade/response/funding_history_response.py +30 -0
- tigeropen/trade/response/option_exercise_response.py +98 -0
- tigeropen/trade/response/order_id_response.py +43 -0
- tigeropen/trade/response/order_preview_response.py +17 -0
- tigeropen/trade/response/orders_response.py +163 -0
- tigeropen/trade/response/positions_response.py +79 -0
- tigeropen/trade/response/prime_assets_response.py +36 -0
- tigeropen/trade/response/segment_fund_response.py +69 -0
- tigeropen/trade/response/transactions_response.py +44 -0
- tigeropen/trade/response/transfer_response.py +104 -0
- tigeropen/trade/trade_client.py +1761 -0
- tigeropen-3.5.9.dist-info/METADATA +820 -0
- tigeropen-3.5.9.dist-info/RECORD +468 -0
- tigeropen-3.5.9.dist-info/WHEEL +5 -0
- tigeropen-3.5.9.dist-info/entry_points.txt +2 -0
- tigeropen-3.5.9.dist-info/top_level.txt +3 -0
|
Binary file
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Account commands: info, assets, analytics.
|
|
4
|
+
"""
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from tigeropen.cli.client_factory import get_trade_client
|
|
8
|
+
from tigeropen.cli.formatting import render, to_records, is_empty
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@click.group('account')
|
|
12
|
+
def account():
|
|
13
|
+
"""Account information and assets."""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@account.command('info')
|
|
18
|
+
@click.pass_context
|
|
19
|
+
def account_info(ctx):
|
|
20
|
+
"""Show account information."""
|
|
21
|
+
client = get_trade_client(ctx.obj)
|
|
22
|
+
accounts = client.get_managed_accounts()
|
|
23
|
+
if not is_empty(accounts):
|
|
24
|
+
data = to_records(accounts)
|
|
25
|
+
render(data, ctx.obj['format'])
|
|
26
|
+
else:
|
|
27
|
+
click.echo('No account info available.')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _segment_to_dict(segment):
|
|
31
|
+
"""Serialize a Segment object to a plain dict."""
|
|
32
|
+
d = {k: v for k, v in segment.__dict__.items() if not k.startswith('_')}
|
|
33
|
+
d['currency_assets'] = {
|
|
34
|
+
currency: {k: v for k, v in ca.__dict__.items() if not k.startswith('_')}
|
|
35
|
+
for currency, ca in segment.currency_assets.items()
|
|
36
|
+
}
|
|
37
|
+
return d
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _portfolio_account_to_dict(account):
|
|
41
|
+
"""Serialize a PortfolioAccount object to a plain dict."""
|
|
42
|
+
return {
|
|
43
|
+
'account': account.account,
|
|
44
|
+
'update_timestamp': account.update_timestamp,
|
|
45
|
+
'segments': {
|
|
46
|
+
key: _segment_to_dict(seg)
|
|
47
|
+
for key, seg in account._segments.items()
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@account.command('assets')
|
|
53
|
+
@click.option('--currency', default=None, help='Currency filter (USD, HKD, etc.).')
|
|
54
|
+
@click.pass_context
|
|
55
|
+
def account_assets(ctx, currency):
|
|
56
|
+
"""Show account asset summary."""
|
|
57
|
+
client = get_trade_client(ctx.obj)
|
|
58
|
+
kwargs = {}
|
|
59
|
+
if currency:
|
|
60
|
+
kwargs['base_currency'] = currency
|
|
61
|
+
result = client.get_prime_assets(**kwargs)
|
|
62
|
+
if not is_empty(result):
|
|
63
|
+
render(_portfolio_account_to_dict(result), ctx.obj['format'])
|
|
64
|
+
else:
|
|
65
|
+
click.echo('No asset data available.')
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@account.command('analytics')
|
|
69
|
+
@click.option('--start-date', default=None, help='Start date (YYYY-MM-DD).')
|
|
70
|
+
@click.option('--end-date', default=None, help='End date (YYYY-MM-DD).')
|
|
71
|
+
@click.pass_context
|
|
72
|
+
def account_analytics(ctx, start_date, end_date):
|
|
73
|
+
"""Show asset analytics."""
|
|
74
|
+
client = get_trade_client(ctx.obj)
|
|
75
|
+
kwargs = {}
|
|
76
|
+
if start_date:
|
|
77
|
+
kwargs['start_date'] = start_date
|
|
78
|
+
if end_date:
|
|
79
|
+
kwargs['end_date'] = end_date
|
|
80
|
+
result = client.get_analytics_asset(**kwargs)
|
|
81
|
+
if not is_empty(result):
|
|
82
|
+
render(result, ctx.obj['format'])
|
|
83
|
+
else:
|
|
84
|
+
click.echo('No analytics data available.')
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Client factory module for CLI.
|
|
4
|
+
Handles config resolution, private key loading, and lazy client creation.
|
|
5
|
+
"""
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from tigeropen import __VERSION__
|
|
10
|
+
from tigeropen.common.util.signature_utils import read_private_key
|
|
11
|
+
from tigeropen.tiger_open_config import TigerOpenClientConfig
|
|
12
|
+
|
|
13
|
+
DEFAULT_CONFIG_DIR = str(Path.home() / '.tigeropen')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def resolve_private_key(key_input):
|
|
17
|
+
"""
|
|
18
|
+
Resolve private key from file path, PEM string, or raw key string.
|
|
19
|
+
|
|
20
|
+
:param key_input: file path, PEM string with headers, or raw key string
|
|
21
|
+
:return: stripped private key string (no PEM headers)
|
|
22
|
+
"""
|
|
23
|
+
if not key_input:
|
|
24
|
+
return ''
|
|
25
|
+
|
|
26
|
+
# If it's an existing file path, read from file
|
|
27
|
+
if os.path.isfile(key_input):
|
|
28
|
+
return read_private_key(key_input)
|
|
29
|
+
|
|
30
|
+
# If it's a full PEM string with headers, strip them
|
|
31
|
+
if '-----BEGIN RSA PRIVATE KEY-----' in key_input:
|
|
32
|
+
return key_input.replace('-----BEGIN RSA PRIVATE KEY-----', '') \
|
|
33
|
+
.replace('-----END RSA PRIVATE KEY-----', '') \
|
|
34
|
+
.replace('\n', '').strip()
|
|
35
|
+
|
|
36
|
+
# Otherwise treat as raw key string
|
|
37
|
+
return key_input
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def build_config(ctx_obj):
|
|
41
|
+
"""
|
|
42
|
+
Build TigerOpenClientConfig from CLI context dict.
|
|
43
|
+
|
|
44
|
+
:param ctx_obj: dict with optional keys: config_path, tiger_id, account, private_key, language
|
|
45
|
+
:return: TigerOpenClientConfig
|
|
46
|
+
"""
|
|
47
|
+
config_path = ctx_obj.get('config_path') or DEFAULT_CONFIG_DIR
|
|
48
|
+
config = TigerOpenClientConfig(enable_dynamic_domain=False,
|
|
49
|
+
props_path=config_path)
|
|
50
|
+
config._channel = f"tigercli"
|
|
51
|
+
|
|
52
|
+
if ctx_obj.get('tiger_id'):
|
|
53
|
+
config.tiger_id = ctx_obj['tiger_id']
|
|
54
|
+
if ctx_obj.get('account'):
|
|
55
|
+
config.account = ctx_obj['account']
|
|
56
|
+
if ctx_obj.get('private_key'):
|
|
57
|
+
config.private_key = resolve_private_key(ctx_obj['private_key'])
|
|
58
|
+
if ctx_obj.get('language'):
|
|
59
|
+
config.language = ctx_obj['language']
|
|
60
|
+
|
|
61
|
+
return config
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_quote_client(ctx_obj):
|
|
65
|
+
"""Lazily create and cache a QuoteClient."""
|
|
66
|
+
if '_quote_client' not in ctx_obj:
|
|
67
|
+
from tigeropen.quote.quote_client import QuoteClient
|
|
68
|
+
config = build_config(ctx_obj)
|
|
69
|
+
ctx_obj['_quote_client'] = QuoteClient(config, is_grab_permission=False)
|
|
70
|
+
return ctx_obj['_quote_client']
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_trade_client(ctx_obj):
|
|
74
|
+
"""Lazily create and cache a TradeClient."""
|
|
75
|
+
if '_trade_client' not in ctx_obj:
|
|
76
|
+
from tigeropen.trade.trade_client import TradeClient
|
|
77
|
+
config = build_config(ctx_obj)
|
|
78
|
+
ctx_obj['_trade_client'] = TradeClient(config)
|
|
79
|
+
return ctx_obj['_trade_client']
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Config commands: init, show, set, path.
|
|
4
|
+
"""
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from jproperties import Properties
|
|
9
|
+
|
|
10
|
+
from tigeropen.tiger_open_config import DEFAULT_PROPS_FILE
|
|
11
|
+
from tigeropen.cli.client_factory import resolve_private_key, DEFAULT_CONFIG_DIR
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@click.group('config')
|
|
15
|
+
def config():
|
|
16
|
+
"""Manage tigeropen configuration."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@config.command('init')
|
|
21
|
+
@click.pass_context
|
|
22
|
+
def config_init(ctx):
|
|
23
|
+
"""Interactive configuration setup."""
|
|
24
|
+
tiger_id = click.prompt('Tiger ID')
|
|
25
|
+
account = click.prompt('Account')
|
|
26
|
+
|
|
27
|
+
# Private key: support multi-line paste (PEM or raw base64).
|
|
28
|
+
# Read lines until an empty line signals end of input.
|
|
29
|
+
click.echo('Private Key (file path or key content, blank line to finish):')
|
|
30
|
+
lines = []
|
|
31
|
+
stdin = click.get_text_stream('stdin')
|
|
32
|
+
while True:
|
|
33
|
+
line = stdin.readline()
|
|
34
|
+
if not line: # EOF
|
|
35
|
+
break
|
|
36
|
+
line = line.rstrip('\n').rstrip('\r')
|
|
37
|
+
if not line and lines:
|
|
38
|
+
break
|
|
39
|
+
if line:
|
|
40
|
+
lines.append(line)
|
|
41
|
+
private_key = ''.join(lines)
|
|
42
|
+
|
|
43
|
+
if not private_key:
|
|
44
|
+
raise click.ClickException('Private key cannot be empty.')
|
|
45
|
+
|
|
46
|
+
secret_key = click.prompt('Secret Key (institutional only, press Enter to skip)',
|
|
47
|
+
default='', show_default=False)
|
|
48
|
+
config_dir = click.prompt('Config directory', default=DEFAULT_CONFIG_DIR)
|
|
49
|
+
|
|
50
|
+
os.makedirs(config_dir, exist_ok=True)
|
|
51
|
+
props_file = os.path.join(config_dir, DEFAULT_PROPS_FILE)
|
|
52
|
+
|
|
53
|
+
p = Properties()
|
|
54
|
+
p['tiger_id'] = tiger_id
|
|
55
|
+
p['account'] = account
|
|
56
|
+
p['private_key_pk8'] = resolve_private_key(private_key)
|
|
57
|
+
|
|
58
|
+
if secret_key:
|
|
59
|
+
p['secret_key'] = secret_key
|
|
60
|
+
|
|
61
|
+
with open(props_file, 'wb') as f:
|
|
62
|
+
p.store(f, encoding='utf-8')
|
|
63
|
+
|
|
64
|
+
click.echo(f'Config written to {props_file}')
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@config.command('show')
|
|
68
|
+
@click.pass_context
|
|
69
|
+
def config_show(ctx):
|
|
70
|
+
"""Display current configuration (private key masked)."""
|
|
71
|
+
config_path = ctx.obj.get('config_path') if ctx.obj else None
|
|
72
|
+
config_path = config_path or DEFAULT_CONFIG_DIR
|
|
73
|
+
if os.path.isdir(config_path):
|
|
74
|
+
props_file = os.path.join(config_path, DEFAULT_PROPS_FILE)
|
|
75
|
+
else:
|
|
76
|
+
props_file = config_path
|
|
77
|
+
|
|
78
|
+
if not os.path.exists(props_file):
|
|
79
|
+
click.echo(f'Config file not found: {props_file}')
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
p = Properties()
|
|
83
|
+
with open(props_file, 'rb') as f:
|
|
84
|
+
p.load(f, 'utf-8')
|
|
85
|
+
|
|
86
|
+
for key in p:
|
|
87
|
+
value = p[key].data
|
|
88
|
+
if 'private_key' in key and value:
|
|
89
|
+
# Mask: show first 4 and last 4 chars
|
|
90
|
+
if len(value) > 8:
|
|
91
|
+
value = value[:4] + '****' + value[-4:]
|
|
92
|
+
else:
|
|
93
|
+
value = '****'
|
|
94
|
+
click.echo(f'{key}={value}')
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@config.command('set')
|
|
98
|
+
@click.argument('key')
|
|
99
|
+
@click.argument('value')
|
|
100
|
+
@click.pass_context
|
|
101
|
+
def config_set(ctx, key, value):
|
|
102
|
+
"""Set a configuration value."""
|
|
103
|
+
config_path = ctx.obj.get('config_path') if ctx.obj else None
|
|
104
|
+
config_path = config_path or DEFAULT_CONFIG_DIR
|
|
105
|
+
if os.path.isdir(config_path):
|
|
106
|
+
props_file = os.path.join(config_path, DEFAULT_PROPS_FILE)
|
|
107
|
+
else:
|
|
108
|
+
props_file = config_path
|
|
109
|
+
|
|
110
|
+
p = Properties()
|
|
111
|
+
if os.path.exists(props_file):
|
|
112
|
+
with open(props_file, 'rb') as f:
|
|
113
|
+
p.load(f, 'utf-8')
|
|
114
|
+
|
|
115
|
+
p[key] = value
|
|
116
|
+
|
|
117
|
+
os.makedirs(os.path.dirname(props_file) if os.path.dirname(props_file) else '.', exist_ok=True)
|
|
118
|
+
with open(props_file, 'wb') as f:
|
|
119
|
+
p.store(f, encoding='utf-8')
|
|
120
|
+
|
|
121
|
+
click.echo(f'Set {key}={value}')
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@config.command('path')
|
|
125
|
+
@click.pass_context
|
|
126
|
+
def config_path(ctx):
|
|
127
|
+
"""Print the config directory path."""
|
|
128
|
+
config_dir = ctx.obj.get('config_path') if ctx.obj else None
|
|
129
|
+
click.echo(config_dir or DEFAULT_CONFIG_DIR)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Output formatting module for CLI.
|
|
4
|
+
Supports table, json, csv formats.
|
|
5
|
+
"""
|
|
6
|
+
import csv
|
|
7
|
+
import io
|
|
8
|
+
import json
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
import pandas as pd
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_empty(data):
|
|
15
|
+
"""Check if data is empty (None, empty DataFrame, empty list, etc.)."""
|
|
16
|
+
if data is None:
|
|
17
|
+
return True
|
|
18
|
+
if isinstance(data, pd.DataFrame):
|
|
19
|
+
return data.empty
|
|
20
|
+
if isinstance(data, (list, dict)):
|
|
21
|
+
return len(data) == 0
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def to_records(items):
|
|
26
|
+
"""Convert a list of domain objects to a list of dicts for rendering.
|
|
27
|
+
If items is already a DataFrame, return it as-is.
|
|
28
|
+
Handles objects with __dict__, __slots__ + to_dict(), or plain dicts."""
|
|
29
|
+
if isinstance(items, pd.DataFrame):
|
|
30
|
+
return items
|
|
31
|
+
records = []
|
|
32
|
+
for item in items:
|
|
33
|
+
if hasattr(item, 'to_dict'):
|
|
34
|
+
d = item.to_dict()
|
|
35
|
+
elif hasattr(item, '__dict__'):
|
|
36
|
+
d = item.__dict__
|
|
37
|
+
else:
|
|
38
|
+
records.append(item)
|
|
39
|
+
continue
|
|
40
|
+
# Flatten nested objects to str for display
|
|
41
|
+
flat = {}
|
|
42
|
+
for k, v in d.items():
|
|
43
|
+
if v is not None and hasattr(v, '__dict__') and not isinstance(v, (str, int, float, bool)):
|
|
44
|
+
flat[k] = str(v)
|
|
45
|
+
elif isinstance(v, list) and v and hasattr(v[0], '__dict__'):
|
|
46
|
+
flat[k] = str(v)
|
|
47
|
+
else:
|
|
48
|
+
flat[k] = v
|
|
49
|
+
records.append(flat)
|
|
50
|
+
return records
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def render(data, fmt='table', file=None):
|
|
54
|
+
"""
|
|
55
|
+
Render data in the specified format.
|
|
56
|
+
|
|
57
|
+
:param data: DataFrame, dict, list of dicts, string, or None
|
|
58
|
+
:param fmt: 'table', 'json', or 'csv'
|
|
59
|
+
:param file: output file object, defaults to sys.stdout
|
|
60
|
+
"""
|
|
61
|
+
if file is None:
|
|
62
|
+
file = sys.stdout
|
|
63
|
+
|
|
64
|
+
if data is None:
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
if isinstance(data, str):
|
|
68
|
+
file.write(data + '\n')
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
if fmt == 'json':
|
|
72
|
+
_render_json(data, file)
|
|
73
|
+
elif fmt == 'csv':
|
|
74
|
+
_render_csv(data, file)
|
|
75
|
+
else:
|
|
76
|
+
_render_table(data, file)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _render_table(data, file):
|
|
80
|
+
if isinstance(data, pd.DataFrame):
|
|
81
|
+
if data.empty:
|
|
82
|
+
return
|
|
83
|
+
file.write(data.to_string(index=False) + '\n')
|
|
84
|
+
elif isinstance(data, list) and data and isinstance(data[0], dict):
|
|
85
|
+
df = pd.DataFrame(data)
|
|
86
|
+
file.write(df.to_string(index=False) + '\n')
|
|
87
|
+
elif isinstance(data, dict):
|
|
88
|
+
for k, v in data.items():
|
|
89
|
+
file.write(f'{k}: {v}\n')
|
|
90
|
+
else:
|
|
91
|
+
file.write(str(data) + '\n')
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _render_json(data, file):
|
|
95
|
+
if isinstance(data, pd.DataFrame):
|
|
96
|
+
records = data.to_dict(orient='records')
|
|
97
|
+
file.write(json.dumps(records, ensure_ascii=False, indent=2) + '\n')
|
|
98
|
+
elif isinstance(data, (dict, list)):
|
|
99
|
+
file.write(json.dumps(data, ensure_ascii=False, indent=2, default=str) + '\n')
|
|
100
|
+
else:
|
|
101
|
+
file.write(json.dumps(str(data), ensure_ascii=False) + '\n')
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _render_csv(data, file):
|
|
105
|
+
if isinstance(data, pd.DataFrame):
|
|
106
|
+
if data.empty:
|
|
107
|
+
return
|
|
108
|
+
file.write(data.to_csv(index=False))
|
|
109
|
+
elif isinstance(data, list) and data and isinstance(data[0], dict):
|
|
110
|
+
writer = csv.DictWriter(file, fieldnames=data[0].keys())
|
|
111
|
+
writer.writeheader()
|
|
112
|
+
writer.writerows(data)
|
|
113
|
+
elif isinstance(data, dict):
|
|
114
|
+
writer = csv.writer(file)
|
|
115
|
+
writer.writerow(['key', 'value'])
|
|
116
|
+
for k, v in data.items():
|
|
117
|
+
writer.writerow([k, v])
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Main CLI entry point for tigeropen.
|
|
4
|
+
"""
|
|
5
|
+
import sys
|
|
6
|
+
import traceback
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from tigeropen import __VERSION__
|
|
11
|
+
from tigeropen.common.exceptions import ApiException
|
|
12
|
+
from tigeropen.cli.config_cmd import config
|
|
13
|
+
from tigeropen.cli.quote_cmd import quote
|
|
14
|
+
from tigeropen.cli.trade_cmd import trade
|
|
15
|
+
from tigeropen.cli.account_cmd import account
|
|
16
|
+
|
|
17
|
+
CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help'])
|
|
18
|
+
|
|
19
|
+
GETTING_STARTED = """\
|
|
20
|
+
TigerOpen CLI v{version} — Tiger Brokers Open API
|
|
21
|
+
|
|
22
|
+
Getting started:
|
|
23
|
+
|
|
24
|
+
1. Run interactive setup:
|
|
25
|
+
$ tigeropen config init
|
|
26
|
+
|
|
27
|
+
2. Or set environment variables:
|
|
28
|
+
$ export TIGEROPEN_TIGER_ID="your_tiger_id"
|
|
29
|
+
$ export TIGEROPEN_PRIVATE_KEY="your_private_key"
|
|
30
|
+
$ export TIGEROPEN_ACCOUNT="your_account"
|
|
31
|
+
|
|
32
|
+
3. Query market data:
|
|
33
|
+
$ tigeropen quote briefs AAPL TSLA
|
|
34
|
+
$ tigeropen quote bars AAPL --period day --limit 10
|
|
35
|
+
|
|
36
|
+
4. Manage orders:
|
|
37
|
+
$ tigeropen trade order list
|
|
38
|
+
$ tigeropen trade position list
|
|
39
|
+
|
|
40
|
+
5. View account:
|
|
41
|
+
$ tigeropen account assets
|
|
42
|
+
|
|
43
|
+
Run 'tigeropen -h' to see all commands and options.
|
|
44
|
+
Run 'tigeropen <command> -h' for help on a specific command.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TigerCLI(click.Group):
|
|
49
|
+
"""Custom click Group that catches API/generic exceptions and shows
|
|
50
|
+
a getting-started guide when invoked with no arguments."""
|
|
51
|
+
|
|
52
|
+
def invoke(self, ctx):
|
|
53
|
+
try:
|
|
54
|
+
return super().invoke(ctx)
|
|
55
|
+
except click.exceptions.Exit:
|
|
56
|
+
raise
|
|
57
|
+
except click.exceptions.Abort:
|
|
58
|
+
click.echo('Aborted.', err=True)
|
|
59
|
+
sys.exit(1)
|
|
60
|
+
except ApiException as e:
|
|
61
|
+
click.echo(f'API Error [{e.code}]: {e.msg}')
|
|
62
|
+
if ctx.obj and ctx.obj.get('verbose'):
|
|
63
|
+
traceback.print_exc()
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
click.echo(f'Error: {e}')
|
|
67
|
+
if ctx.obj and ctx.obj.get('verbose'):
|
|
68
|
+
traceback.print_exc()
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
|
|
71
|
+
# Global options that can appear anywhere in the command line
|
|
72
|
+
_GLOBAL_OPTIONS = {
|
|
73
|
+
'-f': 1, '--format': 1,
|
|
74
|
+
'-c': 1, '--config-path': 1,
|
|
75
|
+
'-l': 1, '--language': 1,
|
|
76
|
+
'-v': 0, '--verbose': 0,
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def parse_args(self, ctx, args):
|
|
80
|
+
if not args:
|
|
81
|
+
click.echo(GETTING_STARTED.format(version=__VERSION__))
|
|
82
|
+
ctx.exit(0)
|
|
83
|
+
# Move global options to the front so Click's group parser can see them.
|
|
84
|
+
# This allows e.g. `tigeropen quote briefs AAPL -f json` to work.
|
|
85
|
+
front = []
|
|
86
|
+
rest = []
|
|
87
|
+
i = 0
|
|
88
|
+
args = list(args)
|
|
89
|
+
while i < len(args):
|
|
90
|
+
arg = args[i]
|
|
91
|
+
if arg in self._GLOBAL_OPTIONS:
|
|
92
|
+
n_values = self._GLOBAL_OPTIONS[arg]
|
|
93
|
+
front.append(arg)
|
|
94
|
+
for j in range(n_values):
|
|
95
|
+
if i + 1 + j < len(args):
|
|
96
|
+
front.append(args[i + 1 + j])
|
|
97
|
+
i += 1 + n_values
|
|
98
|
+
else:
|
|
99
|
+
rest.append(arg)
|
|
100
|
+
i += 1
|
|
101
|
+
return super().parse_args(ctx, front + rest)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@click.group(cls=TigerCLI, context_settings=CONTEXT_SETTINGS)
|
|
105
|
+
@click.option('--config-path', '-c', type=click.Path(), envvar='TIGEROPEN_PROPS_PATH',
|
|
106
|
+
help='Path to config directory or properties file.')
|
|
107
|
+
@click.option('--format', '-f', 'output_format',
|
|
108
|
+
type=click.Choice(['table', 'json', 'csv'], case_sensitive=False),
|
|
109
|
+
default='json', help='Output format.')
|
|
110
|
+
@click.option('--language', '-l',
|
|
111
|
+
type=click.Choice(['en_US', 'zh_CN', 'zh_TW'], case_sensitive=False),
|
|
112
|
+
default='en_US', help='Language.')
|
|
113
|
+
@click.option('--verbose', '-v', is_flag=True, help='Enable debug logging.')
|
|
114
|
+
@click.pass_context
|
|
115
|
+
def cli(ctx, config_path, output_format, language, verbose):
|
|
116
|
+
"""TigerOpen CLI - Tiger Brokers Open API command line tool."""
|
|
117
|
+
ctx.ensure_object(dict)
|
|
118
|
+
ctx.obj['config_path'] = config_path
|
|
119
|
+
ctx.obj['format'] = output_format
|
|
120
|
+
ctx.obj['language'] = language
|
|
121
|
+
ctx.obj['verbose'] = verbose
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@cli.command('version')
|
|
125
|
+
def version():
|
|
126
|
+
"""Print the SDK version."""
|
|
127
|
+
click.echo(__VERSION__)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@cli.command('uninstall')
|
|
131
|
+
@click.option('--remove-config', is_flag=True, help='Also remove ~/.tigeropen/ configuration directory.')
|
|
132
|
+
@click.option('-y', '--yes', is_flag=True, help='Skip confirmation prompts.')
|
|
133
|
+
def uninstall(remove_config, yes):
|
|
134
|
+
"""Uninstall tigeropen and optionally remove configuration."""
|
|
135
|
+
import subprocess
|
|
136
|
+
import shutil
|
|
137
|
+
from pathlib import Path
|
|
138
|
+
|
|
139
|
+
if not yes:
|
|
140
|
+
msg = 'Uninstall tigeropen?'
|
|
141
|
+
if remove_config:
|
|
142
|
+
msg += ' (including ~/.tigeropen/ config directory)'
|
|
143
|
+
if not click.confirm(msg):
|
|
144
|
+
raise SystemExit(0)
|
|
145
|
+
|
|
146
|
+
# Remove config directory first (before pip uninstall removes the CLI itself)
|
|
147
|
+
if remove_config:
|
|
148
|
+
config_dir = Path.home() / '.tigeropen'
|
|
149
|
+
if config_dir.exists():
|
|
150
|
+
shutil.rmtree(config_dir)
|
|
151
|
+
click.echo(f'Removed {config_dir}')
|
|
152
|
+
else:
|
|
153
|
+
click.echo(f'{config_dir} not found, skipping.')
|
|
154
|
+
|
|
155
|
+
# Run pip uninstall with the same Python interpreter
|
|
156
|
+
click.echo('Uninstalling tigeropen...')
|
|
157
|
+
result = subprocess.run(
|
|
158
|
+
[sys.executable, '-m', 'pip', 'uninstall', 'tigeropen', '-y'],
|
|
159
|
+
capture_output=True, text=True,
|
|
160
|
+
)
|
|
161
|
+
if result.returncode == 0:
|
|
162
|
+
click.echo('tigeropen has been uninstalled.')
|
|
163
|
+
else:
|
|
164
|
+
click.echo(f'pip uninstall failed:\n{result.stderr.strip()}', err=True)
|
|
165
|
+
sys.exit(1)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
cli.add_command(config)
|
|
169
|
+
cli.add_command(quote)
|
|
170
|
+
cli.add_command(trade)
|
|
171
|
+
cli.add_command(account)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def _load_push_command():
|
|
175
|
+
"""Lazy-load push command to avoid importing protobuf at startup."""
|
|
176
|
+
try:
|
|
177
|
+
from tigeropen.cli.push_cmd import push
|
|
178
|
+
cli.add_command(push)
|
|
179
|
+
except ImportError:
|
|
180
|
+
@cli.command('push')
|
|
181
|
+
@click.argument('args', nargs=-1)
|
|
182
|
+
def push_unavailable(args):
|
|
183
|
+
"""Real-time data streaming (requires protobuf>=5.28)."""
|
|
184
|
+
click.echo('Error: push command requires protobuf>=5.28.\n'
|
|
185
|
+
'Please upgrade: pip install --upgrade protobuf')
|
|
186
|
+
sys.exit(1)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
_load_push_command()
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def main():
|
|
193
|
+
cli()
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
if __name__ == '__main__':
|
|
197
|
+
main()
|