euclid-clob-sdk 0.1.0__tar.gz

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.
Files changed (303) hide show
  1. euclid_clob_sdk-0.1.0/LICENSE +21 -0
  2. euclid_clob_sdk-0.1.0/PKG-INFO +567 -0
  3. euclid_clob_sdk-0.1.0/README.md +529 -0
  4. euclid_clob_sdk-0.1.0/pyproject.toml +111 -0
  5. euclid_clob_sdk-0.1.0/setup.cfg +4 -0
  6. euclid_clob_sdk-0.1.0/src/euclid/__init__.py +56 -0
  7. euclid_clob_sdk-0.1.0/src/euclid/_generated/__init__.py +11 -0
  8. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/__init__.py +1 -0
  9. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/__init__.py +1 -0
  10. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/get_account_balance.py +178 -0
  11. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/get_account_hold.py +178 -0
  12. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/get_account_transaction_by_hash.py +180 -0
  13. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/get_account_withdrawal.py +192 -0
  14. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/get_explorer_block.py +159 -0
  15. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/get_explorer_transaction.py +159 -0
  16. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/get_explorer_transaction_by_hash.py +161 -0
  17. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_account_balances.py +215 -0
  18. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_account_deposits.py +245 -0
  19. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_account_fees.py +290 -0
  20. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_account_holds.py +245 -0
  21. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_account_transactions.py +250 -0
  22. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_account_transfers.py +235 -0
  23. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_account_withdrawals.py +275 -0
  24. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_explorer_block_transactions.py +212 -0
  25. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_explorer_blocks.py +209 -0
  26. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/account_indexer/list_explorer_transactions.py +261 -0
  27. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/__init__.py +1 -0
  28. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/confirm_totp.py +169 -0
  29. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/create_api_key.py +169 -0
  30. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/create_delegated_session.py +173 -0
  31. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/disable_totp.py +169 -0
  32. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/get_mfa_status.py +137 -0
  33. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/get_my_account_profile.py +137 -0
  34. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/get_public_account_profile.py +161 -0
  35. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/issue_nonce.py +164 -0
  36. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/list_api_keys.py +199 -0
  37. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/list_sessions.py +219 -0
  38. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/logout.py +164 -0
  39. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/recover_totp.py +169 -0
  40. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/refresh_session.py +164 -0
  41. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/regenerate_recovery_codes.py +169 -0
  42. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/revoke_api_key.py +169 -0
  43. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/revoke_session.py +185 -0
  44. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/setup_totp.py +169 -0
  45. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/verify_privy.py +164 -0
  46. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/auth/verify_wallet_signature.py +164 -0
  47. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/__init__.py +1 -0
  48. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/cancel_all_orders.py +169 -0
  49. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/cancel_order.py +173 -0
  50. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/cancel_replace.py +169 -0
  51. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/create_transfer.py +185 -0
  52. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/engine_get_depth.py +180 -0
  53. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/get_account.py +199 -0
  54. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/get_balance.py +178 -0
  55. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/get_effective_pair_fee.py +169 -0
  56. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/get_exchange_info.py +198 -0
  57. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/get_leaderboard.py +279 -0
  58. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/get_my_fee_tier.py +137 -0
  59. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/get_server_time.py +132 -0
  60. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/issue_account_transactions_ticket.py +177 -0
  61. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/list_assets.py +194 -0
  62. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/list_balances.py +200 -0
  63. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/list_fee_tiers.py +194 -0
  64. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/list_pair_fee_overrides.py +194 -0
  65. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/list_pairs.py +179 -0
  66. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/list_user_trades.py +215 -0
  67. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/place_order.py +169 -0
  68. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/place_signed_order.py +164 -0
  69. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/engine/request_withdrawal.py +185 -0
  70. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/__init__.py +1 -0
  71. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/get_global_stats.py +132 -0
  72. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/get_symbol.py +159 -0
  73. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/get_symbol_stats.py +159 -0
  74. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/get_ticker.py +159 -0
  75. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/list_klines.py +210 -0
  76. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/list_symbol_stats.py +179 -0
  77. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/list_symbols.py +179 -0
  78. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/list_tickers.py +198 -0
  79. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/list_trades.py +210 -0
  80. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/market_data/market_data_get_depth.py +180 -0
  81. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/oms/__init__.py +1 -0
  82. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/oms/get_order.py +164 -0
  83. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/oms/list_open_orders.py +215 -0
  84. euclid_clob_sdk-0.1.0/src/euclid/_generated/api/oms/list_order_history.py +235 -0
  85. euclid_clob_sdk-0.1.0/src/euclid/_generated/client.py +268 -0
  86. euclid_clob_sdk-0.1.0/src/euclid/_generated/errors.py +16 -0
  87. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/__init__.py +361 -0
  88. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/accepted_order.py +89 -0
  89. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/account_balance.py +80 -0
  90. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/account_profile.py +115 -0
  91. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/api_key.py +196 -0
  92. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/asset.py +80 -0
  93. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/balance.py +116 -0
  94. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/book_ticker.py +107 -0
  95. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_all_orders_request.py +62 -0
  96. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_all_orders_response.py +116 -0
  97. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_order_request.py +71 -0
  98. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_order_response.py +80 -0
  99. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_replace_cancel_response.py +80 -0
  100. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_replace_mode.py +10 -0
  101. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_replace_request.py +153 -0
  102. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancel_replace_response.py +140 -0
  103. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/cancelled_order.py +71 -0
  104. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/command_kind.py +31 -0
  105. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/commission_rates.py +89 -0
  106. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/confirm_totp_request.py +70 -0
  107. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/confirm_totp_response.py +72 -0
  108. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/create_api_key_request.py +173 -0
  109. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/create_api_key_response.py +188 -0
  110. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/create_delegated_session_request.py +71 -0
  111. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/create_delegated_session_response.py +131 -0
  112. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/create_transfer_request_type_0.py +61 -0
  113. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/create_transfer_request_type_1.py +61 -0
  114. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/create_transfer_response.py +120 -0
  115. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/deposit.py +161 -0
  116. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/depth_diff.py +137 -0
  117. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/depth_snapshot.py +119 -0
  118. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/disable_totp_request.py +61 -0
  119. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/disable_totp_response.py +61 -0
  120. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/engine_get_depth_response.py +119 -0
  121. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/engine_price_level.py +71 -0
  122. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/engine_trade.py +134 -0
  123. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/exchange_symbol.py +208 -0
  124. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/explorer_block.py +305 -0
  125. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/explorer_block_transaction.py +98 -0
  126. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/explorer_transaction.py +291 -0
  127. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/fee_configuration.py +71 -0
  128. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/fee_posting.py +134 -0
  129. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/fee_tier.py +134 -0
  130. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/fee_tier_assignment.py +107 -0
  131. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/fill.py +107 -0
  132. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_account_balance_response.py +93 -0
  133. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_account_hold_response.py +93 -0
  134. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_account_response.py +226 -0
  135. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_account_transaction_by_hash_response.py +93 -0
  136. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_account_withdrawal_response.py +93 -0
  137. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_balance_response.py +75 -0
  138. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_effective_pair_fee_response.py +157 -0
  139. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_exchange_info_response.py +97 -0
  140. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_explorer_block_response.py +92 -0
  141. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_explorer_transaction_by_hash_response.py +92 -0
  142. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_explorer_transaction_response.py +92 -0
  143. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_global_stats_response.py +97 -0
  144. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_leaderboard_response.py +116 -0
  145. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_mfa_status_response.py +61 -0
  146. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_my_account_profile_response.py +74 -0
  147. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_my_fee_tier_response.py +119 -0
  148. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_order_response.py +75 -0
  149. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_public_account_profile_response.py +74 -0
  150. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_server_time_response.py +62 -0
  151. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_symbol_response.py +74 -0
  152. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_symbol_stats_response.py +74 -0
  153. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/get_ticker_response.py +74 -0
  154. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/google_rpc_error_info.py +93 -0
  155. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/google_rpc_error_info_metadata.py +47 -0
  156. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/google_rpc_status.py +98 -0
  157. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/issue_account_transactions_ticket_request.py +47 -0
  158. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/issue_account_transactions_ticket_response.py +71 -0
  159. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/issue_nonce_request.py +61 -0
  160. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/issue_nonce_response.py +78 -0
  161. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/kline.py +134 -0
  162. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/kline_update.py +111 -0
  163. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/leaderboard_cursor.py +71 -0
  164. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/leaderboard_facts.py +188 -0
  165. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/leaderboard_item.py +102 -0
  166. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/leaderboard_metric.py +12 -0
  167. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/leaderboard_profile.py +89 -0
  168. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/leaderboard_window.py +10 -0
  169. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ledger_balance.py +80 -0
  170. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_account_balances_response.py +107 -0
  171. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_account_deposits_response.py +107 -0
  172. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_account_fees_response.py +107 -0
  173. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_account_holds_response.py +107 -0
  174. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_account_transactions_response.py +107 -0
  175. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_account_transfers_response.py +107 -0
  176. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_account_withdrawals_response.py +107 -0
  177. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_api_keys_response.py +88 -0
  178. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_assets_response.py +89 -0
  179. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_balances_response.py +89 -0
  180. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_explorer_block_transactions_response.py +106 -0
  181. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_explorer_blocks_response.py +106 -0
  182. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_explorer_transactions_response.py +106 -0
  183. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_fee_tiers_response.py +89 -0
  184. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_klines_response.py +106 -0
  185. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_open_orders_response.py +89 -0
  186. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_order_history_response.py +89 -0
  187. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_pair_fee_overrides_response.py +98 -0
  188. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_pairs_response.py +89 -0
  189. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_sessions_response.py +89 -0
  190. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_symbol_stats_response.py +88 -0
  191. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_symbols_response.py +88 -0
  192. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_tickers_response.py +88 -0
  193. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_trades_response.py +97 -0
  194. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/list_user_trades_response.py +98 -0
  195. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/logout_request.py +61 -0
  196. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/logout_response.py +45 -0
  197. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/market_data_get_depth_response.py +118 -0
  198. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/market_data_price_level.py +71 -0
  199. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/market_data_trade.py +116 -0
  200. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/order.py +187 -0
  201. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/order_details.py +142 -0
  202. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/order_hold.py +134 -0
  203. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/order_status.py +13 -0
  204. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/order_type.py +12 -0
  205. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/pair.py +161 -0
  206. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/pair_fee_override.py +84 -0
  207. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/place_order_request.py +127 -0
  208. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/place_order_response.py +148 -0
  209. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/place_signed_order_request.py +163 -0
  210. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/place_signed_order_response.py +148 -0
  211. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/projection_freshness.py +94 -0
  212. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/recover_totp_request.py +61 -0
  213. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/recover_totp_response.py +78 -0
  214. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/refresh_session_request.py +61 -0
  215. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/refresh_session_response.py +121 -0
  216. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/regenerate_recovery_codes_request.py +61 -0
  217. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/regenerate_recovery_codes_response.py +72 -0
  218. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/request_withdrawal_request.py +108 -0
  219. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/request_withdrawal_response.py +166 -0
  220. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/revoke_api_key_request.py +61 -0
  221. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/revoke_api_key_response.py +45 -0
  222. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/revoke_session_request.py +62 -0
  223. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/revoke_session_response.py +47 -0
  224. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/session.py +141 -0
  225. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/session_token_type.py +10 -0
  226. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/setup_totp_request.py +45 -0
  227. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/setup_totp_response.py +79 -0
  228. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/symbol.py +98 -0
  229. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/symbol_stats.py +134 -0
  230. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ticker_24h.py +224 -0
  231. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/trade_fee.py +91 -0
  232. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/transaction_order.py +124 -0
  233. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/transaction_trade.py +152 -0
  234. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/transfer.py +126 -0
  235. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/transfer_direction.py +10 -0
  236. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/transfer_policy.py +10 -0
  237. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/user_balance.py +84 -0
  238. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/user_trade.py +143 -0
  239. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/verify_privy_request.py +61 -0
  240. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/verify_privy_response.py +157 -0
  241. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/verify_wallet_signature_request.py +70 -0
  242. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/verify_wallet_signature_response.py +121 -0
  243. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/wallet.py +174 -0
  244. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/withdraw_destination.py +71 -0
  245. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/withdraw_policy.py +10 -0
  246. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/withdrawal.py +286 -0
  247. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_ack.py +81 -0
  248. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_channel.py +13 -0
  249. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_client_message_type_0.py +67 -0
  250. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_client_message_type_1.py +67 -0
  251. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_error.py +80 -0
  252. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_server_message_type_0.py +67 -0
  253. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_server_message_type_1.py +67 -0
  254. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_server_message_type_2.py +67 -0
  255. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_server_message_type_3.py +67 -0
  256. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_server_message_type_4.py +67 -0
  257. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_server_message_type_5.py +67 -0
  258. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_server_message_type_6.py +67 -0
  259. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_subscribe.py +115 -0
  260. euclid_clob_sdk-0.1.0/src/euclid/_generated/models/ws_unsubscribe.py +62 -0
  261. euclid_clob_sdk-0.1.0/src/euclid/_generated/types.py +54 -0
  262. euclid_clob_sdk-0.1.0/src/euclid/_numbers.py +85 -0
  263. euclid_clob_sdk-0.1.0/src/euclid/_ops.py +1347 -0
  264. euclid_clob_sdk-0.1.0/src/euclid/_version.py +1 -0
  265. euclid_clob_sdk-0.1.0/src/euclid/client.py +954 -0
  266. euclid_clob_sdk-0.1.0/src/euclid/config.py +139 -0
  267. euclid_clob_sdk-0.1.0/src/euclid/enums.py +145 -0
  268. euclid_clob_sdk-0.1.0/src/euclid/errors.py +252 -0
  269. euclid_clob_sdk-0.1.0/src/euclid/models.py +784 -0
  270. euclid_clob_sdk-0.1.0/src/euclid/py.typed +0 -0
  271. euclid_clob_sdk-0.1.0/src/euclid/streams/__init__.py +20 -0
  272. euclid_clob_sdk-0.1.0/src/euclid/streams/_market_frames.py +153 -0
  273. euclid_clob_sdk-0.1.0/src/euclid/streams/_retry.py +122 -0
  274. euclid_clob_sdk-0.1.0/src/euclid/streams/_transport.py +103 -0
  275. euclid_clob_sdk-0.1.0/src/euclid/streams/account.py +321 -0
  276. euclid_clob_sdk-0.1.0/src/euclid/streams/blocking.py +210 -0
  277. euclid_clob_sdk-0.1.0/src/euclid/streams/market_data.py +404 -0
  278. euclid_clob_sdk-0.1.0/src/euclid/streams/order_book.py +246 -0
  279. euclid_clob_sdk-0.1.0/src/euclid_clob_sdk.egg-info/PKG-INFO +567 -0
  280. euclid_clob_sdk-0.1.0/src/euclid_clob_sdk.egg-info/SOURCES.txt +301 -0
  281. euclid_clob_sdk-0.1.0/src/euclid_clob_sdk.egg-info/dependency_links.txt +1 -0
  282. euclid_clob_sdk-0.1.0/src/euclid_clob_sdk.egg-info/requires.txt +14 -0
  283. euclid_clob_sdk-0.1.0/src/euclid_clob_sdk.egg-info/top_level.txt +1 -0
  284. euclid_clob_sdk-0.1.0/tests/test_account.py +179 -0
  285. euclid_clob_sdk-0.1.0/tests/test_account_history.py +615 -0
  286. euclid_clob_sdk-0.1.0/tests/test_build_spec.py +206 -0
  287. euclid_clob_sdk-0.1.0/tests/test_check_distribution.py +123 -0
  288. euclid_clob_sdk-0.1.0/tests/test_check_release.py +147 -0
  289. euclid_clob_sdk-0.1.0/tests/test_config.py +192 -0
  290. euclid_clob_sdk-0.1.0/tests/test_edge_cases.py +162 -0
  291. euclid_clob_sdk-0.1.0/tests/test_errors.py +172 -0
  292. euclid_clob_sdk-0.1.0/tests/test_examples.py +897 -0
  293. euclid_clob_sdk-0.1.0/tests/test_market_data.py +317 -0
  294. euclid_clob_sdk-0.1.0/tests/test_orders.py +508 -0
  295. euclid_clob_sdk-0.1.0/tests/test_pair_rules.py +165 -0
  296. euclid_clob_sdk-0.1.0/tests/test_parity.py +151 -0
  297. euclid_clob_sdk-0.1.0/tests/test_route_coverage.py +138 -0
  298. euclid_clob_sdk-0.1.0/tests/test_stream_account.py +814 -0
  299. euclid_clob_sdk-0.1.0/tests/test_stream_blocking.py +222 -0
  300. euclid_clob_sdk-0.1.0/tests/test_stream_market_data.py +716 -0
  301. euclid_clob_sdk-0.1.0/tests/test_stream_order_book.py +384 -0
  302. euclid_clob_sdk-0.1.0/tests/test_stream_transport.py +105 -0
  303. euclid_clob_sdk-0.1.0/tests/test_sync_specs.py +259 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Euclid Protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,567 @@
1
+ Metadata-Version: 2.4
2
+ Name: euclid-clob-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the Euclid exchange API
5
+ Author: Euclid Protocol
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/EuclidProtocol/euclid-python-sdk
8
+ Project-URL: Repository, https://github.com/EuclidProtocol/euclid-python-sdk
9
+ Project-URL: Issues, https://github.com/EuclidProtocol/euclid-python-sdk/issues
10
+ Project-URL: Changelog, https://github.com/EuclidProtocol/euclid-python-sdk/blob/main/CHANGELOG.md
11
+ Keywords: euclid,exchange,trading,orderbook,clob,market-data,websocket,sdk
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: Topic :: Office/Business :: Financial :: Investment
16
+ Classifier: Framework :: AsyncIO
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Requires-Dist: httpx2<3,>=2.13.1
27
+ Requires-Dist: attrs>=22.2.0
28
+ Requires-Dist: websockets<18,>=15
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest>=8; extra == "test"
31
+ Requires-Dist: pytest-cov>=5; extra == "test"
32
+ Provides-Extra: codegen
33
+ Requires-Dist: openapi-python-client==0.30.0; extra == "codegen"
34
+ Requires-Dist: ruff==0.17.4; extra == "codegen"
35
+ Provides-Extra: typecheck
36
+ Requires-Dist: mypy==2.4.1; extra == "typecheck"
37
+ Dynamic: license-file
38
+
39
+ # Euclid Python SDK
40
+
41
+ A Python client for the Euclid exchange API: market data, account and balances, and order
42
+ placement, with a blocking client and an asyncio client that share one implementation, plus
43
+ asyncio WebSocket streams for live market data, a local order book, and your own account events.
44
+
45
+ > **Status: pre-release (`0.x`).** The API of this package may change between minor versions;
46
+ > [`CHANGELOG.md`](CHANGELOG.md) says what each release changed.
47
+
48
+ ## Install
49
+
50
+ Python 3.11 or newer.
51
+
52
+ ```bash
53
+ pip install euclid-clob-sdk
54
+ ```
55
+
56
+ The distribution is named `euclid-clob-sdk`; the package you import is `euclid`.
57
+
58
+ ## Getting started
59
+
60
+ The example scripts are in this repository, not in the installed package, so start from a
61
+ checkout:
62
+
63
+ ```bash
64
+ git clone https://github.com/EuclidProtocol/euclid-python-sdk
65
+ cd euclid-python-sdk
66
+ python -m venv .venv
67
+ source .venv/bin/activate
68
+ pip install -e .
69
+ export EUCLID_BASE_URL=https://your-euclid-host.example
70
+ export EUCLID_API_KEY="ek1.<key-id>.<secret>" # optional: leave it out for public data only
71
+ python examples/check_setup.py
72
+ ```
73
+
74
+ `check_setup.py` prints one line per check: the configuration, the server, market data, the
75
+ WebSocket and, when a key is set, one read per kind of private data, with the scopes the key is
76
+ missing. It changes nothing and never prints the key. From there,
77
+ [`examples/`](examples/README.md) is a path from reading an order book to a small quoting loop
78
+ that only trades when you pass `--live`.
79
+
80
+ The SDK and the examples read real environment variables. Nothing loads a `.env` file for you.
81
+ [`.env.example`](.env.example) lists the variable names; to use a copy of it from your shell:
82
+
83
+ ```bash
84
+ cp .env.example .env # then edit .env; it is ignored by git
85
+ set -a; source .env; set +a
86
+ ```
87
+
88
+ ## Quick start
89
+
90
+ ```python
91
+ from decimal import Decimal
92
+ from euclid import EuclidClient, OrderType, Side
93
+
94
+ with EuclidClient("ek1.<key-id>.<secret>", base_url="https://<your-euclid-host>") as client:
95
+ book = client.get_order_book("ETH-USDC", limit=5)
96
+ print(book.bids[0].price, book.asks[0].price)
97
+
98
+ order = client.place_order(
99
+ pair_id="ETH-USDC",
100
+ side=Side.BUY,
101
+ order_type=OrderType.POST_ONLY,
102
+ price=Decimal("1950.25"),
103
+ base_size=Decimal("0.5"),
104
+ )
105
+ client.cancel_order(pair_id="ETH-USDC", order_id=order.order_id)
106
+ ```
107
+
108
+ `AsyncEuclidClient` has the same methods with the same arguments; `await` them and use
109
+ `async with`. Live data is under [WebSocket streams](#websocket-streams). Runnable scripts are
110
+ in [`examples/`](examples/README.md).
111
+
112
+ ## Configuration
113
+
114
+ The API is five services that may be on different hosts. This package contains no hostnames;
115
+ you supply them.
116
+
117
+ ```python
118
+ from euclid import Environment, EuclidClient
119
+
120
+ # One origin for everything:
121
+ client = EuclidClient(api_key, base_url="https://gateway.example")
122
+
123
+ # Or one URL per service:
124
+ client = EuclidClient(
125
+ api_key,
126
+ environment=Environment(
127
+ engine="https://...", # orders, balances, pairs, assets
128
+ market_data="https://...", # order book, klines, tickers, trades
129
+ order_history="https://...", # open orders and order history
130
+ account_indexer="https://...", # account history: transactions, deposits, withdrawals, transfers
131
+ auth="https://...", # not used by any wrapped method yet
132
+ ),
133
+ )
134
+
135
+ # Or from the environment: EUCLID_API_KEY, EUCLID_BASE_URL, and optionally
136
+ # EUCLID_ENGINE_URL, EUCLID_MARKET_DATA_URL, EUCLID_ORDER_HISTORY_URL,
137
+ # EUCLID_ACCOUNT_INDEXER_URL, EUCLID_AUTH_URL.
138
+ client = EuclidClient.from_env()
139
+ ```
140
+
141
+ The WebSocket URLs are derived from these: the market-data stream is `/ws` on the market-data
142
+ URL and the account stream is `/ws/account/transactions` on the engine URL, with `https`
143
+ becoming `wss`. If your deployment serves them elsewhere, set
144
+ `Environment(market_data_stream="wss://...", account_stream="wss://...")` or
145
+ `EUCLID_MARKET_DATA_WS_URL` and `EUCLID_ACCOUNT_WS_URL`.
146
+
147
+ - The API key is optional. Without one, the public methods work and the others raise
148
+ `ConfigurationError` before sending anything.
149
+ - The key is sent only on routes that require it, as `Authorization: Bearer <key>`. It is never
150
+ sent to the market-data service.
151
+ - Plain `http://` and `ws://` are refused except for localhost, unless you pass
152
+ `allow_insecure_http=True`.
153
+ - `client.has_api_key` says whether the client has a credential, and
154
+ `client.environment.configured` lists the URLs that are set.
155
+
156
+ ## Amounts are `Decimal`
157
+
158
+ Every price, size and balance is a `decimal.Decimal` in human units, in both directions:
159
+ `Decimal("0.5")` ETH, never a scaled integer and never a float. Arguments accept a `Decimal`,
160
+ an `int` or a `str`. A `float` raises `TypeError`, because `0.1 + 0.2` is not `0.3`.
161
+
162
+ The API itself returns most amounts as integers scaled by a per-pair or per-asset number of
163
+ decimal places. The SDK reads those scales from the exchange and converts for you. It caches
164
+ them for `metadata_ttl` seconds (default 60); `client.forget_metadata()` drops the cache.
165
+
166
+ `str()` of a very small `Decimal` uses scientific notation (`1E-8`). Print with the `f` format
167
+ to avoid it: `f"{balance.available:f}"`.
168
+
169
+ An order's price must be a whole number of the pair's `tick_size` and its size a whole number
170
+ of `lot_size`. A `Pair` rounds for you, exactly:
171
+
172
+ ```python
173
+ pair = next(p for p in client.list_pairs() if p.pair_id == "ETH-USDC")
174
+ bid = pair.round_price(mid - offset) # down to the tick
175
+ ask = pair.round_price(mid + offset, up=True) # up to the tick
176
+ size = pair.round_size("0.12345") # down to the lot
177
+ smallest = pair.min_size_at(bid) # smallest order the pair accepts at that price
178
+ ```
179
+
180
+ Do not use `Decimal.quantize` for this. It fixes the number of decimal places, which is the
181
+ same thing only when the tick is a power of ten: with a tick of `0.05`,
182
+ `Decimal("100.02").quantize(Decimal("0.05"))` is still `100.02`.
183
+
184
+ ## Errors
185
+
186
+ A failed call raises; nothing returns an error object. Everything derives from `EuclidError`.
187
+
188
+ | Exception | When |
189
+ | --- | --- |
190
+ | `ValidationError` | 400: malformed request or a trading rule was broken |
191
+ | `AuthenticationError` | 401: missing, invalid or expired credential |
192
+ | `PermissionDeniedError` | 403: valid credential, not allowed |
193
+ | `InsufficientScopeError` | 403 with reason `INSUFFICIENT_SCOPE` (a `PermissionDeniedError`) |
194
+ | `NotFoundError` | 404: unknown order, pair or symbol |
195
+ | `ConflictError` | 409 |
196
+ | `RateLimitError` | 429; `retry_after` holds the `Retry-After` seconds when sent |
197
+ | `ServerError` | 5xx |
198
+ | `NetworkError`, `RequestTimeoutError` | no HTTP response arrived |
199
+ | `UnexpectedResponseError` | a success response the SDK could not read |
200
+ | `ConfigurationError`, `UnknownPairError` | raised locally, before or without a request |
201
+ | `SubscriptionError` | a stream subscription the server refused for good (`UNKNOWN_SYMBOL`, ...) |
202
+ | `StreamError` | a stream gave up reconnecting, or could not be brought back in sync |
203
+ | `StreamTimeoutError` | nothing arrived within the `timeout` given to `receive` (also a built-in `TimeoutError`) |
204
+
205
+ Server errors are `APIError` subclasses and carry `status`, `reason` (such as
206
+ `VALIDATION_FAILED`), `domain` (such as `engine.euclid`), `message`, `error_code` (the stable
207
+ business code, such as `POST_ONLY_WOULD_CROSS`), `metadata` and `body`.
208
+
209
+ ```python
210
+ from euclid import RateLimitError, ValidationError
211
+
212
+ try:
213
+ client.place_order(pair_id="ETH-USDC", side="buy", price="1950.25", base_size="0.5")
214
+ except ValidationError as error:
215
+ print(error.error_code, error.message)
216
+ except RateLimitError as error:
217
+ print("retry in", error.retry_after)
218
+ ```
219
+
220
+ After a `NetworkError` on a call that changes state, the SDK cannot know whether the server
221
+ applied it. Check `list_open_orders()` before retrying an order. The SDK never retries by itself.
222
+
223
+ ## Account history
224
+
225
+ ```python
226
+ page = client.list_account_transactions(page_size=50) # newest first
227
+ for tx in page.items:
228
+ print(tx.engine_seq, tx.kind, [t.base_amount for t in tx.trades])
229
+
230
+ # Everything after a known point, oldest first, across all pages:
231
+ for tx in client.paginate(client.list_account_transactions, after_engine_seq=1200):
232
+ ...
233
+
234
+ # At most 250 items, asking for no page that is not needed:
235
+ recent = list(client.paginate(client.list_my_trades, limit=250))
236
+
237
+ # Page by page, when you want what a page carries besides its items:
238
+ for page in client.pages(client.list_deposits, page_size=100):
239
+ print(len(page.items), page.freshness)
240
+
241
+ found = client.get_account_transaction(transaction_hash="...")
242
+ deposits = client.list_deposits(asset="USDC")
243
+ withdrawals = client.list_withdrawals(status="reserved")
244
+ transfers = client.list_transfers(direction="sent")
245
+ ```
246
+
247
+ With `AsyncEuclidClient`, `paginate` is an async iterator: `async for tx in client.paginate(...)`.
248
+ A `page_token` remembers the position and direction of a walk, so `after_engine_seq` and
249
+ `before_engine_seq` are ignored when a token is given.
250
+
251
+ **This data is a projection and can trail the engine.** It is served by the account indexer,
252
+ which stores what the engine commits a moment after the engine commits it. Three consequences:
253
+
254
+ - Every page and lookup carries a `freshness`: `freshness.is_current`, `freshness.state`, and
255
+ `freshness.lag` (how many engine sequences behind, when known).
256
+ - A lookup returns a `Lookup`. `result.item is None` means the indexer has no such record.
257
+ Treat that as "does not exist" only when `result.known_absent` is true; otherwise the
258
+ indexer may not have caught up yet and the lookup is worth repeating.
259
+ - For anything you act on, ask the authoritative service: balances from `get_balances`
260
+ (the engine ledger), open orders from `list_open_orders`. The account indexer's own copies of
261
+ balances and order holds are deliberately not wrapped.
262
+
263
+ Scopes an API key needs: `funds:read` for transactions, `deposits:read`, `withdrawals:read` and
264
+ `transfers:read` for the others.
265
+
266
+ ## WebSocket streams
267
+
268
+ The streams are asyncio and are built on an `AsyncEuclidClient`, which they use for the URLs
269
+ and for the REST calls they need. Each one is an async iterator. Blocking code can read its
270
+ account's events with [`BlockingAccountStream`](#from-blocking-code); the market-data streams
271
+ have no blocking form.
272
+
273
+ `websockets` is the WebSocket library: it is the most widely used asyncio implementation, is
274
+ pure Python with no dependencies of its own, and answers pings and detects dead connections
275
+ without help.
276
+
277
+ ### Market data
278
+
279
+ ```python
280
+ from euclid import AsyncEuclidClient, MarketDataStream
281
+
282
+ async with AsyncEuclidClient(base_url="https://<your-euclid-host>") as client:
283
+ async with MarketDataStream(client) as stream:
284
+ async for trade in stream.trades("ETH-USDC"):
285
+ print(trade.price, trade.size)
286
+ ```
287
+
288
+ | Method | Yields |
289
+ | --- | --- |
290
+ | `stream.depth(symbol, depth=20, speed_ms=100)` | `OrderBook`: a full snapshot that replaces the last |
291
+ | `stream.depth_diff(symbol)` | `DepthDiff`: changed levels; size zero removes a level |
292
+ | `stream.trades(symbol)` | `PublicTrade` |
293
+ | `stream.book_ticker(symbol)` | `BookTicker`: best bid and ask |
294
+ | `stream.klines(symbol, interval="1m")` | `KlineUpdate` |
295
+ | `stream.order_book(symbol)` | `BookUpdate`, from a `LocalOrderBook` |
296
+
297
+ The server allows one subscription per connection, so every subscription has its own
298
+ connection; `MarketDataStream` keeps track of them and closes them together. Iterate several
299
+ subscriptions from separate tasks. Closing a subscription (`await subscription.aclose()`, or
300
+ leaving its `async with`) sends an unsubscribe, which ends that connection for good. It waits
301
+ at most `close_timeout` seconds (default 5, set on `MarketDataStream`) for the server to
302
+ acknowledge, then closes anyway. No API key is sent.
303
+
304
+ To wait for one event with a time limit, instead of looping:
305
+
306
+ ```python
307
+ book = await stream.depth("ETH-USDC").receive(timeout=10)
308
+ ```
309
+
310
+ `receive` raises `StreamTimeoutError` if nothing arrives in time, and the message says why
311
+ connecting failed if it did. The subscription can still be read afterwards.
312
+
313
+ ### A local order book
314
+
315
+ ```python
316
+ async with stream.order_book("ETH-USDC", depth=1000) as book:
317
+ async for update in book:
318
+ print(book.best_bid, book.best_ask, book.bids(5), update.resynced)
319
+ ```
320
+
321
+ `LocalOrderBook` subscribes to the depth-diff channel, loads a snapshot over REST and applies
322
+ each diff on top. Every diff names the range of book updates it covers. A diff that is already
323
+ included is ignored. A diff that skips an update is a gap: the book stops, loads a fresh
324
+ snapshot and continues from there, and it does the same after every reconnect. It never applies
325
+ a diff out of sequence. `book.gaps` and `book.resyncs` count what happened.
326
+
327
+ The server sends the latest change roughly every 200 ms, not every change, so on a busy pair
328
+ gaps are normal and the book is rebuilt from snapshots often (at most once per
329
+ `min_resync_interval`, default one second). If you only need the top of a busy book,
330
+ `stream.depth(symbol, depth=20, speed_ms=100)` is simpler and just as fresh.
331
+
332
+ ### Your account's events
333
+
334
+ ```python
335
+ from euclid import AccountStream
336
+
337
+ async with AsyncEuclidClient(api_key, base_url="https://<your-euclid-host>") as client:
338
+ async with AccountStream(client) as stream:
339
+ async for event in stream:
340
+ print(event.kind, event.transaction_hash, event.orders, event.trades)
341
+ ```
342
+
343
+ Each event is one committed transaction that touched your account, as the same
344
+ `AccountTransaction` record that [`list_account_transactions`](#account-history) returns: `kind`
345
+ (`"place_order"`, `"cancel_order"`, ...), `engine_seq`, the order changes it made and the trades
346
+ it executed, with amounts as `Decimal`. `event.raw` is the transaction exactly as the server
347
+ sent it, and `event.unparsed` names any amount the SDK could not convert because its pair or
348
+ asset is not in the exchange's lists. The stream authenticates with a 60-second ticket that it
349
+ fetches itself; the API key needs the `funds:read` scope.
350
+
351
+ To continue where an earlier run stopped, save `stream.cursor` and pass
352
+ `AccountStream(client, cursor=saved)`. Without a cursor the stream starts at the present;
353
+ `from_start=True` replays the account's history first.
354
+
355
+ After a disconnect the stream reads what it missed over HTTP, from the account history, and
356
+ yields it in order before it reconnects, so events keep arriving even while the socket cannot
357
+ be re-established. You can also ask for this yourself:
358
+ `await stream.backfill(after_engine_seq=n)` queues everything after sequence `n`, to be yielded
359
+ next. History is read by `engine_seq`. The stream's `cursor` and the history's `page_token` are
360
+ different tokens and are never exchanged for one another. If the history cannot be read, the
361
+ stream reconnects anyway and the server replays from the cursor; the reason is kept in
362
+ `stream.last_backfill_error`. Pass `backfill_on_reconnect=False` to switch this off.
363
+
364
+ ### From blocking code
365
+
366
+ ```python
367
+ from euclid import BlockingAccountStream, EuclidClient
368
+
369
+ with EuclidClient(api_key, base_url="https://<your-euclid-host>") as client:
370
+ with BlockingAccountStream(client) as events:
371
+ events.wait_until_connected(timeout=10)
372
+ order = client.place_order(...)
373
+ for event in events.drain(): # what has arrived so far; never waits
374
+ ...
375
+ event = events.get(timeout=5) # wait for one; None if none came
376
+ for event in events: # or block until each arrives
377
+ ...
378
+ ```
379
+
380
+ `BlockingAccountStream` is an `AccountStream` running on a background thread, with its own
381
+ connection built from the client's key and URLs. It takes the same options (`cursor`,
382
+ `max_reconnect_attempts`, ...), delivers the same events, and reconnects and catches up the
383
+ same way. Events wait in memory until you read them. If the stream fails for good, the next
384
+ read raises the error it failed with. Call `wait_until_connected` before doing something whose
385
+ result you want to see: a stream started without a cursor delivers what happens after it
386
+ connects.
387
+
388
+ ### Reconnecting
389
+
390
+ All streams reconnect by themselves. The wait starts at half a second and doubles up to 30
391
+ seconds, with random jitter, and starts over once a connection works; pass `backoff=Backoff(...)`
392
+ to change it. A refused connection that names a `Retry-After` waits at least that long. By
393
+ default a stream retries for ever; `max_reconnect_attempts=n` makes it raise `StreamError` after
394
+ `n` failures in a row. Failures that retrying cannot fix are raised straight away:
395
+ `SubscriptionError` for an unknown symbol or a rejected subscription, `AuthenticationError` or
396
+ `InsufficientScopeError` when a ticket is refused.
397
+
398
+ After a reconnect:
399
+
400
+ - a market-data subscription subscribes again, and `trades` first fetches the trades it missed
401
+ over REST and yields them in order;
402
+ - a `LocalOrderBook` rebuilds from a snapshot;
403
+ - an `AccountStream` first yields what it missed from the HTTP history, then gets a new ticket
404
+ and resumes after the last cursor it received, dropping any transaction it has already
405
+ delivered (by `transaction_hash`).
406
+
407
+ ### What the streams do not guarantee
408
+
409
+ The server describes its WebSockets as best-effort, and so is this layer.
410
+
411
+ - **The stream alone is not a durable record.** A process that restarts has to catch up itself:
412
+ save `event.engine_seq` as you process events and, on start, walk
413
+ `list_account_transactions(after_engine_seq=saved)` before or alongside the stream.
414
+ - **The history the backfill reads can itself be behind.** It comes from the account indexer,
415
+ which trails the engine by a moment; `stream.last_backfill_freshness` says by how much. A
416
+ transaction too new for the indexer arrives later, over the socket or the next backfill.
417
+ - **An account stream started without a cursor can miss events** if it disconnects before the
418
+ server has told it a position (the server sends one at most 20 seconds after connecting).
419
+ - **Duplicates are dropped within one `AccountStream` object only**, across the last 10,000
420
+ transactions it saw (`dedupe_window`).
421
+ - **Depth, book-ticker and kline streams are samples.** After a reconnect you get the current
422
+ state, not what changed in between.
423
+ - **A local order book is only as deep as its last snapshot** (`depth` levels per side, at most
424
+ 5000), and between a gap and the next snapshot it is briefly behind.
425
+ - **Do not act on money from a stream event alone.** Confirm an order's state with `get_order`
426
+ or `list_open_orders` first.
427
+ - Prices and sizes are converted with the pair's scales as read when the subscription
428
+ connected. If a pair is reconfigured, close and reopen its subscriptions.
429
+
430
+ ## What is wrapped
431
+
432
+ | Area | Methods |
433
+ | --- | --- |
434
+ | Reference data | `get_server_time`, `list_pairs`, `list_assets`, `get_exchange_info` |
435
+ | Market data | `get_order_book`, `get_klines`, `get_symbol_stats`, `get_ticker`, `list_public_trades` |
436
+ | Account | `get_account`, `get_balances`, `get_balance`, `get_fee_tier`, `create_account_stream_ticket` |
437
+ | Trading | `place_order`, `cancel_order`, `cancel_all_orders`, `cancel_replace` |
438
+ | Orders and trades | `list_open_orders`, `list_order_history`, `get_order`, `list_my_trades` |
439
+ | Account history | `list_account_transactions`, `get_account_transaction`, `list_deposits`, `list_withdrawals`, `get_withdrawal`, `list_transfers` |
440
+ | Paging | `paginate` walks the items of every page of any method above that returns a `Page`, up to an optional `limit`; `pages` yields the pages themselves |
441
+ | Trading rules | `Pair.round_price`, `Pair.round_size`, `Pair.min_size_at` |
442
+ | Streams (asyncio) | `MarketDataStream`, `LocalOrderBook`, `AccountStream` |
443
+ | Streams (blocking) | `BlockingAccountStream` |
444
+
445
+ All arguments of the trading, order and history methods are keyword-only. You never pass a user
446
+ id: the SDK reads it once from the account route and reuses it.
447
+
448
+ Not wrapped yet: making a transfer or a withdrawal, wallet-signed orders, API-key and session
449
+ management (the whole auth service), leaderboards, fee-schedule discovery, the multi-symbol
450
+ ticker and stats lists, and from the account indexer its copies of balances and order holds
451
+ (the engine's `get_balances` and `list_open_orders` are the authority for those), raw fee
452
+ postings, and the public block explorer. The exact list, with a reason per route, is
453
+ `NOT_YET_WRAPPED` in [`src/euclid/_ops.py`](src/euclid/_ops.py); a test fails if the API gains a
454
+ route that is neither wrapped nor on that list.
455
+
456
+ ## How the package is built
457
+
458
+ ```
459
+ specs/ the five public OpenAPI documents, one per service, pruned (see below)
460
+ scripts/sync_specs.py refreshes specs/ from the server's public documents
461
+ scripts/build_spec.py merges them into one document with short schema names
462
+ src/euclid/_generated/ low-level client generated from the merged document (private)
463
+ src/euclid/ the hand-written public API on top of it
464
+ ```
465
+
466
+ **The generated code is checked in.** Installing the package needs no generator and no build
467
+ step, the exact code that ships is visible in review, and a diff of `_generated/` shows what an
468
+ API change does to the client. Nobody edits it by hand.
469
+
470
+ **The documents in `specs/` are pruned copies, not byte-identical ones.** The server's public
471
+ OpenAPI documents define more schemas than their routes use. Each file here is the server's
472
+ document with those removed: a schema is kept only if a route in the same document reaches it,
473
+ directly or through another schema, or if it is one of the market-data WebSocket messages
474
+ (`WsClientMessage`, `WsServerMessage` and what they reference). Paths, operations, descriptions,
475
+ security schemes and the kept schemas are exactly as the server publishes them. A test fails if
476
+ `specs/` ever holds a schema that is not reachable this way.
477
+
478
+ Never copy a document into `specs/` by hand. To refresh them, put the server's public documents
479
+ in a directory of their own and run:
480
+
481
+ ```bash
482
+ python scripts/sync_specs.py path/to/public/documents
483
+ pip install -e ".[codegen]"
484
+ python scripts/regenerate.py
485
+ ```
486
+
487
+ `sync_specs.py` uses the same rule as the merge to decide what to keep, needs only the standard
488
+ library, and writes nothing unless every file passes. It refuses a file not named
489
+ `euclid.<service>.v1.openapi.json` for one of the five services, and a document with an
490
+ `internal` path.
491
+
492
+ `python scripts/regenerate.py --check` exits non-zero if `_generated/` does not match `specs/`;
493
+ the test suite runs the same check when the generator is installed. The generator
494
+ (`openapi-python-client`) and the formatter (`ruff`) are pinned in `pyproject.toml` so the output
495
+ is reproducible.
496
+
497
+ Runtime dependencies: [`httpx2`](https://github.com/pydantic/httpx2) and `attrs`, which the
498
+ generated client uses for HTTP and models, and
499
+ [`websockets`](https://github.com/python-websockets/websockets) for the streams.
500
+
501
+ ## Development
502
+
503
+ ```bash
504
+ pip install -e ".[test,codegen,typecheck]"
505
+ pytest --cov # the tests, with coverage of the hand-written code
506
+ ruff check . && ruff format --check . # lint and formatting
507
+ mypy # types: strict for the package, default for examples and scripts
508
+ python scripts/regenerate.py --check # the generated client matches specs/
509
+ ```
510
+
511
+ The tests use a mocked HTTP transport and a scripted in-process WebSocket, with an injected
512
+ clock and sleep. They never touch the network and never wait. Coverage of the hand-written code
513
+ must stay at 95% or more; the generated client is left out of that number, because
514
+ `regenerate.py --check` is what guards it.
515
+
516
+ Every push to `main` and every pull request runs the same commands in
517
+ [GitHub Actions](.github/workflows/ci.yml):
518
+
519
+ - the tests on Python 3.11, 3.12, 3.13 and 3.14 on Linux, and on macOS;
520
+ - the tests again with the oldest version of each dependency that `pyproject.toml` allows;
521
+ - lint, formatting, types and the generated-client check;
522
+ - a build of the wheel and source archive, a check of what they contain
523
+ (`scripts/check_distribution.py`), and an install of the wheel into a clean environment.
524
+
525
+ Windows is not tested.
526
+
527
+ ### Releasing
528
+
529
+ A release is a git tag. Pushing a tag such as `v0.2.0` runs
530
+ [the release workflow](.github/workflows/release.yml), which checks, builds, publishes to PyPI
531
+ and creates the GitHub release with that version's changelog entry as its notes.
532
+
533
+ 1. On a branch: set the version in `src/euclid/_version.py`, add a `## <version>` section to
534
+ [`CHANGELOG.md`](CHANGELOG.md), and merge it to `main`.
535
+ `python scripts/check_release.py` tells you whether the two agree.
536
+ 2. Optional rehearsal: run the Release workflow by hand from the Actions tab. That builds the
537
+ same way and uploads to TestPyPI.
538
+ 3. Tag the merged commit and push the tag:
539
+
540
+ ```bash
541
+ git switch main && git pull
542
+ git tag v0.2.0
543
+ git push origin v0.2.0
544
+ ```
545
+
546
+ The workflow refuses to publish if the tag is not on `main`, if the tag and the version in the
547
+ code differ, if the changelog has no entry for the version, or if the tests or the package
548
+ checks fail. A version on PyPI is permanent: it can be hidden afterwards but never replaced, so
549
+ a mistake is fixed by releasing the next patch version.
550
+
551
+ Publishing uses PyPI's trusted publishing, so no token is stored in this repository. It needs a
552
+ one-time setup by an owner of the PyPI project: on pypi.org, under the project's publishing
553
+ settings (or "pending publisher" before the first release), add a GitHub publisher with
554
+
555
+ | Field | Value |
556
+ | --- | --- |
557
+ | Owner | `EuclidProtocol` |
558
+ | Repository | `euclid-python-sdk` |
559
+ | Workflow | `release.yml` |
560
+ | Environment | `pypi` |
561
+
562
+ and the same on test.pypi.org with the environment `testpypi`. To require a person to approve
563
+ each publish, add required reviewers to the `pypi` environment in this repository's settings.
564
+
565
+ ## Licence
566
+
567
+ [MIT](LICENSE).