| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent efc2f5c commit 3ce7b9c
24 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -18,9 +18,9 @@ jobs: | |||
| 18 | 18 | runs-on: ubuntu-latest | |
| 19 | 19 | timeout-minutes: 5 | |
| 20 | 20 | steps: | |
| 21 | - - uses: actions/checkout@v4 | ||
| 21 | + - uses: actions/checkout@v5 | ||
| 22 | 22 | - name: Set up Python | |
| 23 | - uses: actions/setup-python@v5 | ||
| 23 | + uses: actions/setup-python@v6 | ||
| 24 | 24 | with: | |
| 25 | 25 | python-version: '3.9' | |
| 26 | 26 | - name: Install Ruff | |
@@ -31,49 +31,49 @@ jobs: | |||
| 31 | 31 | run: ruff format --check . | |
| 32 | 32 | continue-on-error: true | |
| 33 | 33 | ||
| 34 | - build: | ||
| 34 | + test: | ||
| 35 | 35 | needs: lint | |
| 36 | 36 | runs-on: ubuntu-22.04 | |
| 37 | 37 | timeout-minutes: 40 | |
| 38 | + strategy: | ||
| 39 | + max-parallel: 2 | ||
| 40 | + matrix: | ||
| 41 | + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] | ||
| 38 | 42 | env: | |
| 39 | - PROXY: "http://51.83.140.52:16301" | ||
| 43 | + PROXY: "http://188.245.226.105:8911" | ||
| 40 | 44 | TEST_TESTNET: "true" | |
| 41 | 45 | TEST_API_KEY: "u4L8MG2DbshTfTzkx2Xm7NfsHHigvafxeC29HrExEmah1P8JhxXkoOu6KntLICUc" | |
| 42 | 46 | TEST_API_SECRET: "hBZEqhZUUS6YZkk7AIckjJ3iLjrgEFr5CRtFPp5gjzkrHKKC9DAv4OH25PlT6yq5" | |
| 43 | 47 | TEST_FUTURES_API_KEY: "227719da8d8499e8d3461587d19f259c0b39c2b462a77c9b748a6119abd74401" | |
| 44 | 48 | TEST_FUTURES_API_SECRET: "b14b935f9cfacc5dec829008733c40da0588051f29a44625c34967b45c11d73c" | |
| 45 | - strategy: | ||
| 46 | - max-parallel: 1 | ||
| 47 | - matrix: | ||
| 48 | - python: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] | ||
| 49 | 49 | steps: | |
| 50 | - - uses: actions/checkout@v4 | ||
| 50 | + - uses: actions/checkout@v5 | ||
| 51 | 51 | - name: Checking env | |
| 52 | 52 | run: | | |
| 53 | 53 | echo "PROXY: $PROXY" | |
| 54 | - env | ||
| 55 | - - name: Set up Python ${{ matrix.python }} | ||
| 56 | - uses: actions/setup-python@v5 | ||
| 54 | + echo "Python version: ${{ matrix.python-version }}" | ||
| 55 | + - name: Set up Python ${{ matrix.python-version }} | ||
| 56 | + uses: actions/setup-python@v6 | ||
| 57 | 57 | with: | |
| 58 | - python-version: ${{ matrix.python }} | ||
| 58 | + python-version: ${{ matrix.python-version }} | ||
| 59 | 59 | cache: 'pip' | |
| 60 | 60 | - name: Install dependencies | |
| 61 | 61 | run: | | |
| 62 | 62 | python -m pip install --upgrade pip | |
| 63 | 63 | pip install pytest pytest-cov pyright tox | |
| 64 | 64 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi | |
| 65 | 65 | if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi | |
| 66 | - - name: Type check with pyright | ||
| 66 | + - name: Type check with pyright (Python 3.12 only) | ||
| 67 | + if: matrix.python-version == '3.12' | ||
| 67 | 68 | run: pyright | |
| 68 | 69 | - name: Test with tox | |
| 69 | 70 | run: tox -e py | |
| 70 | 71 | - name: Coveralls Parallel | |
| 71 | 72 | uses: coverallsapp/github-action@v2 | |
| 72 | 73 | with: | |
| 73 | - flag-name: run-${{ join(matrix.*, '-') }} | ||
| 74 | 74 | parallel: true | |
| 75 | 75 | finish: | |
| 76 | - needs: build | ||
| 76 | + needs: test | ||
| 77 | 77 | if: ${{ always() }} | |
| 78 | 78 | runs-on: ubuntu-latest | |
| 79 | 79 | timeout-minutes: 5 | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -63,7 +63,8 @@ Features | |||
| 63 | 63 | ||
| 64 | 64 | - Implementation of all General, Market Data and Account endpoints. | |
| 65 | 65 | - Asyncio implementation | |
| 66 | - - Testnet support for Spot, Futures and Vanilla Options | ||
| 66 | + - Demo trading support (by providing demo=True) | ||
| 67 | + - Testnet support for Spot, Futures and Vanilla Options (deprecated) | ||
| 67 | 68 | - Simple handling of authentication include RSA and EDDSA keys | |
| 68 | 69 | - No need to generate timestamps yourself, the wrapper does it for you | |
| 69 | 70 | - RecvWindow sent by default | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,6 +31,7 @@ def __init__( | |||
| 31 | 31 | tld: str = "com", | |
| 32 | 32 | base_endpoint: str = BaseClient.BASE_ENDPOINT_DEFAULT, | |
| 33 | 33 | testnet: bool = False, | |
| 34 | + demo: bool = False, | ||
| 34 | 35 | loop=None, | |
| 35 | 36 | session_params: Optional[Dict[str, Any]] = None, | |
| 36 | 37 | private_key: Optional[Union[str, Path]] = None, | |
@@ -41,13 +42,23 @@ def __init__( | |||
| 41 | 42 | self.https_proxy = https_proxy | |
| 42 | 43 | self.loop = loop or get_loop() | |
| 43 | 44 | self._session_params: Dict[str, Any] = session_params or {} | |
| 45 | + | ||
| 46 | + # Convert https_proxy to requests_params format for BaseClient | ||
| 47 | + if https_proxy and requests_params is None: | ||
| 48 | + requests_params = {'proxies': {'http': https_proxy, 'https': https_proxy}} | ||
| 49 | + elif https_proxy and requests_params is not None: | ||
| 50 | + if 'proxies' not in requests_params: | ||
| 51 | + requests_params['proxies'] = {} | ||
| 52 | + requests_params['proxies'].update({'http': https_proxy, 'https': https_proxy}) | ||
| 53 | + | ||
| 44 | 54 | super().__init__( | |
| 45 | 55 | api_key, | |
| 46 | 56 | api_secret, | |
| 47 | 57 | requests_params, | |
| 48 | 58 | tld, | |
| 49 | 59 | base_endpoint, | |
| 50 | 60 | testnet, | |
| 61 | + demo, | ||
| 51 | 62 | private_key, | |
| 52 | 63 | private_key_pass, | |
| 53 | 64 | time_unit=time_unit, | |
@@ -62,6 +73,7 @@ async def create( | |||
| 62 | 73 | tld: str = "com", | |
| 63 | 74 | base_endpoint: str = BaseClient.BASE_ENDPOINT_DEFAULT, | |
| 64 | 75 | testnet: bool = False, | |
| 76 | + demo: bool = False, | ||
| 65 | 77 | loop=None, | |
| 66 | 78 | session_params: Optional[Dict[str, Any]] = None, | |
| 67 | 79 | private_key: Optional[Union[str, Path]] = None, | |
@@ -76,6 +88,7 @@ async def create( | |||
| 76 | 88 | tld, | |
| 77 | 89 | base_endpoint, | |
| 78 | 90 | testnet, | |
| 91 | + demo, | ||
| 79 | 92 | loop, | |
| 80 | 93 | session_params, | |
| 81 | 94 | private_key, | |
@@ -151,6 +164,9 @@ async def _request( | |||
| 151 | 164 | url_encoded_data = urlencode(dict_data) | |
| 152 | 165 | data = f"{url_encoded_data}&signature={signature}" | |
| 153 | 166 | ||
| 167 | + # Remove proxies from kwargs since aiohttp uses 'proxy' parameter instead | ||
| 168 | + kwargs.pop('proxies', None) | ||
| 169 | + | ||
| 154 | 170 | async with getattr(self.session, method)( | |
| 155 | 171 | yarl.URL(uri, encoded=True), | |
| 156 | 172 | proxy=self.https_proxy, | |
@@ -1876,6 +1892,77 @@ async def futures_create_order(self, **params): | |||
| 1876 | 1892 | params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() | |
| 1877 | 1893 | return await self._request_futures_api("post", "order", True, data=params) | |
| 1878 | 1894 | ||
| 1895 | + async def futures_limit_order(self, **params): | ||
| 1896 | + """Send in a new futures limit order. | ||
| 1897 | + | ||
| 1898 | + https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api | ||
| 1899 | + | ||
| 1900 | + """ | ||
| 1901 | + if "newClientOrderId" not in params: | ||
| 1902 | + params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() | ||
| 1903 | + params["type"] = "LIMIT" | ||
| 1904 | + return await self._request_futures_api("post", "order", True, data=params) | ||
| 1905 | + | ||
| 1906 | + async def futures_market_order(self, **params): | ||
| 1907 | + """Send in a new futures market order. | ||
| 1908 | + | ||
| 1909 | + https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api | ||
| 1910 | + | ||
| 1911 | + """ | ||
| 1912 | + if "newClientOrderId" not in params: | ||
| 1913 | + params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() | ||
| 1914 | + params["type"] = "MARKET" | ||
| 1915 | + return await self._request_futures_api("post", "order", True, data=params) | ||
| 1916 | + | ||
| 1917 | + | ||
| 1918 | + async def futures_limit_buy_order(self, **params): | ||
| 1919 | + """Send in a new futures limit buy order. | ||
| 1920 | + | ||
| 1921 | + https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api | ||
| 1922 | + | ||
| 1923 | + """ | ||
| 1924 | + if "newClientOrderId" not in params: | ||
| 1925 | + params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() | ||
| 1926 | + params["side"] = "BUY" | ||
| 1927 | + params["type"] = "LIMIT" | ||
| 1928 | + return await self._request_futures_api("post", "order", True, data=params) | ||
| 1929 | + | ||
| 1930 | + async def futures_limit_sell_order(self, **params): | ||
| 1931 | + """Send in a new futures limit sell order. | ||
| 1932 | + | ||
| 1933 | + https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api | ||
| 1934 | + | ||
| 1935 | + """ | ||
| 1936 | + if "newClientOrderId" not in params: | ||
| 1937 | + params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() | ||
| 1938 | + params["side"] = "SELL" | ||
| 1939 | + params["type"] = "LIMIT" | ||
| 1940 | + return await self._request_futures_api("post", "order", True, data=params) | ||
| 1941 | + | ||
| 1942 | + async def futures_market_buy_order(self, **params): | ||
| 1943 | + """Send in a new futures market buy order. | ||
| 1944 | + | ||
| 1945 | + https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api | ||
| 1946 | + | ||
| 1947 | + """ | ||
| 1948 | + if "newClientOrderId" not in params: | ||
| 1949 | + params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() | ||
| 1950 | + params["side"] = "BUY" | ||
| 1951 | + params["type"] = "MARKET" | ||
| 1952 | + return await self._request_futures_api("post", "order", True, data=params) | ||
| 1953 | + | ||
| 1954 | + async def futures_market_sell_order(self, **params): | ||
| 1955 | + """Send in a new futures market sell order. | ||
| 1956 | + | ||
| 1957 | + https://developers.binance.com/docs/derivatives/usds-margined-futures/trade/rest-api | ||
| 1958 | + | ||
| 1959 | + """ | ||
| 1960 | + if "newClientOrderId" not in params: | ||
| 1961 | + params["newClientOrderId"] = self.CONTRACT_ORDER_PREFIX + self.uuid22() | ||
| 1962 | + params["side"] = "SELL" | ||
| 1963 | + params["type"] = "MARKET" | ||
| 1964 | + return await self._request_futures_api("post", "order", True, data=params) | ||
| 1965 | + | ||
| 1879 | 1966 | async def futures_modify_order(self, **params): | |
| 1880 | 1967 | """Modify an existing order. Currently only LIMIT order modification is supported. | |
| 1881 | 1968 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,23 +22,28 @@ | |||
| 22 | 22 | class BaseClient: | |
| 23 | 23 | API_URL = "https://api{}.binance.{}/api" | |
| 24 | 24 | API_TESTNET_URL = "https://testnet.binance.vision/api" | |
| 25 | + API_DEMO_URL = "https://demo-api.binance.com/api" | ||
| 25 | 26 | MARGIN_API_URL = "https://api{}.binance.{}/sapi" | |
| 26 | 27 | WEBSITE_URL = "https://www.binance.{}" | |
| 27 | 28 | FUTURES_URL = "https://fapi.binance.{}/fapi" | |
| 28 | 29 | FUTURES_TESTNET_URL = "https://testnet.binancefuture.com/fapi" | |
| 30 | + FUTURES_DEMO_URL = "https://demo-fapi.binance.com/fapi" | ||
| 29 | 31 | FUTURES_DATA_URL = "https://fapi.binance.{}/futures/data" | |
| 30 | 32 | FUTURES_DATA_TESTNET_URL = "https://testnet.binancefuture.com/futures/data" | |
| 31 | 33 | FUTURES_COIN_URL = "https://dapi.binance.{}/dapi" | |
| 32 | 34 | FUTURES_COIN_TESTNET_URL = "https://testnet.binancefuture.com/dapi" | |
| 35 | + FUTURES_COIN_DEMO_URL = "https://demo-dapi.binance.com/dapi" | ||
| 33 | 36 | FUTURES_COIN_DATA_URL = "https://dapi.binance.{}/futures/data" | |
| 34 | 37 | FUTURES_COIN_DATA_TESTNET_URL = "https://testnet.binancefuture.com/futures/data" | |
| 35 | 38 | OPTIONS_URL = "https://eapi.binance.{}/eapi" | |
| 36 | 39 | OPTIONS_TESTNET_URL = "https://testnet.binanceops.{}/eapi" | |
| 37 | 40 | PAPI_URL = "https://papi.binance.{}/papi" | |
| 38 | 41 | WS_API_URL = "wss://ws-api.binance.{}/ws-api/v3" | |
| 39 | 42 | WS_API_TESTNET_URL = "wss://ws-api.testnet.binance.vision/ws-api/v3" | |
| 43 | + WS_API_DEMO_URL = "wss://demo-ws-api.binance.com/ws-api/v3" | ||
| 40 | 44 | WS_FUTURES_URL = "wss://ws-fapi.binance.{}/ws-fapi/v1" | |
| 41 | 45 | WS_FUTURES_TESTNET_URL = "wss://testnet.binancefuture.com/ws-fapi/v1" | |
| 46 | + WS_FUTURES_DEMO_URL = "wss://testnet.binancefuture.com/ws-fapi/v1" | ||
| 42 | 47 | PUBLIC_API_VERSION = "v3" | |
| 43 | 48 | PRIVATE_API_VERSION = "v3" | |
| 44 | 49 | MARGIN_API_VERSION = "v1" | |
@@ -157,6 +162,7 @@ def __init__( | |||
| 157 | 162 | tld: str = "com", | |
| 158 | 163 | base_endpoint: str = BASE_ENDPOINT_DEFAULT, | |
| 159 | 164 | testnet: bool = False, | |
| 165 | + demo: bool = False, | ||
| 160 | 166 | private_key: Optional[Union[str, Path]] = None, | |
| 161 | 167 | private_key_pass: Optional[str] = None, | |
| 162 | 168 | loop: Optional[asyncio.AbstractEventLoop] = None, | |
@@ -201,15 +207,27 @@ def __init__( | |||
| 201 | 207 | self._requests_params = requests_params | |
| 202 | 208 | self.response = None | |
| 203 | 209 | self.testnet = testnet | |
| 210 | + self.demo = demo | ||
| 204 | 211 | self.timestamp_offset = 0 | |
| 205 | - ws_api_url = self.WS_API_TESTNET_URL if testnet else self.WS_API_URL.format(tld) | ||
| 212 | + ws_api_url = self.WS_API_URL.format(tld) | ||
| 213 | + if testnet: | ||
| 214 | + ws_api_url = self.WS_API_TESTNET_URL | ||
| 215 | + elif demo: | ||
| 216 | + ws_api_url = self.WS_API_DEMO_URL | ||
| 206 | 217 | if self.TIME_UNIT: | |
| 207 | 218 | ws_api_url += f"?timeUnit={self.TIME_UNIT}" | |
| 208 | - self.ws_api = WebsocketAPI(url=ws_api_url, tld=tld) | ||
| 209 | - ws_future_url = ( | ||
| 210 | - self.WS_FUTURES_TESTNET_URL if testnet else self.WS_FUTURES_URL.format(tld) | ||
| 211 | - ) | ||
| 212 | - self.ws_future = WebsocketAPI(url=ws_future_url, tld=tld) | ||
| 219 | + # Extract proxy from requests_params for WebSocket connections | ||
| 220 | + https_proxy = None | ||
| 221 | + if requests_params and 'proxies' in requests_params: | ||
| 222 | + https_proxy = requests_params['proxies'].get('https') or requests_params['proxies'].get('http') | ||
| 223 | + | ||
| 224 | + self.ws_api = WebsocketAPI(url=ws_api_url, tld=tld, https_proxy=https_proxy) | ||
| 225 | + ws_future_url = self.WS_FUTURES_URL.format(tld) | ||
| 226 | + if testnet: | ||
| 227 | + ws_future_url = self.WS_FUTURES_TESTNET_URL | ||
| 228 | + elif demo: | ||
| 229 | + ws_future_url = self.WS_FUTURES_DEMO_URL | ||
| 230 | + self.ws_future = WebsocketAPI(url=ws_future_url, tld=tld, https_proxy=https_proxy) | ||
| 213 | 231 | self.loop = loop or get_loop() | |
| 214 | 232 | ||
| 215 | 233 | def _get_headers(self) -> Dict: | |
@@ -250,6 +268,8 @@ def _create_api_uri( | |||
| 250 | 268 | url = self.API_URL | |
| 251 | 269 | if self.testnet: | |
| 252 | 270 | url = self.API_TESTNET_URL | |
| 271 | + elif self.demo: | ||
| 272 | + url = self.API_DEMO_URL | ||
| 253 | 273 | v = self.PRIVATE_API_VERSION if signed else version | |
| 254 | 274 | return url + "/" + v + "/" + path | |
| 255 | 275 | ||
@@ -273,6 +293,8 @@ def _create_futures_api_uri(self, path: str, version: int = 1) -> str: | |||
| 273 | 293 | url = self.FUTURES_URL | |
| 274 | 294 | if self.testnet: | |
| 275 | 295 | url = self.FUTURES_TESTNET_URL | |
| 296 | + elif self.demo: | ||
| 297 | + url = self.FUTURES_DEMO_URL | ||
| 276 | 298 | options = { | |
| 277 | 299 | 1: self.FUTURES_API_VERSION, | |
| 278 | 300 | 2: self.FUTURES_API_VERSION2, | |
@@ -290,6 +312,8 @@ def _create_futures_coin_api_url(self, path: str, version: int = 1) -> str: | |||
| 290 | 312 | url = self.FUTURES_COIN_URL | |
| 291 | 313 | if self.testnet: | |
| 292 | 314 | url = self.FUTURES_COIN_TESTNET_URL | |
| 315 | + elif self.demo: | ||
| 316 | + url = self.FUTURES_COIN_DEMO_URL | ||
| 293 | 317 | options = { | |
| 294 | 318 | 1: self.FUTURES_API_VERSION, | |
| 295 | 319 | 2: self.FUTURES_API_VERSION2, | |
| Back | FazBrowse Home | New Git URL |
0 commit comments