jumptech-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 (348) hide show
  1. jumptech_sdk-0.1.0/.gitignore +16 -0
  2. jumptech_sdk-0.1.0/PKG-INFO +306 -0
  3. jumptech_sdk-0.1.0/README.md +295 -0
  4. jumptech_sdk-0.1.0/jumptech_sdk/__init__.py +61 -0
  5. jumptech_sdk-0.1.0/jumptech_sdk/__main__.py +7 -0
  6. jumptech_sdk-0.1.0/jumptech_sdk/_call.py +54 -0
  7. jumptech_sdk-0.1.0/jumptech_sdk/_generated/__init__.py +8 -0
  8. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/__init__.py +1 -0
  9. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/__init__.py +0 -0
  10. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/account_state.py +225 -0
  11. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/close_account.py +216 -0
  12. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/get_account.py +169 -0
  13. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/get_ledger.py +352 -0
  14. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/ledger_summary.py +239 -0
  15. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/list_accounts.py +525 -0
  16. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/reopen_account.py +216 -0
  17. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/accounts/update_account.py +276 -0
  18. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/__init__.py +0 -0
  19. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/change_my_password.py +231 -0
  20. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/create_role.py +203 -0
  21. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/create_user.py +177 -0
  22. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/create_view.py +175 -0
  23. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/delete_view.py +168 -0
  24. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/disable_my_2fa.py +195 -0
  25. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/force_disable_user_2fa.py +200 -0
  26. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/get_my_profile.py +171 -0
  27. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/get_role.py +167 -0
  28. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/get_user.py +170 -0
  29. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/list_audit_log.py +387 -0
  30. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/list_my_sessions.py +171 -0
  31. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/list_roles.py +199 -0
  32. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/list_settings.py +159 -0
  33. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/list_user_sessions.py +194 -0
  34. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/list_users.py +282 -0
  35. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/list_views.py +209 -0
  36. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/put_setting.py +234 -0
  37. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/reset_user_password.py +200 -0
  38. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/revoke_my_other_sessions.py +149 -0
  39. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/revoke_my_session.py +187 -0
  40. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/revoke_user_session.py +198 -0
  41. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/setup_my_2fa.py +165 -0
  42. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/update_my_profile.py +183 -0
  43. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/update_role.py +214 -0
  44. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/update_user.py +193 -0
  45. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/update_view.py +191 -0
  46. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/administration/verify_my_2fa.py +195 -0
  47. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/api_keys/__init__.py +0 -0
  48. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/api_keys/create_api_key.py +183 -0
  49. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/api_keys/list_api_keys.py +138 -0
  50. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/api_keys/revoke_api_key.py +188 -0
  51. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/__init__.py +0 -0
  52. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/branding.py +173 -0
  53. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/confirm_password_reset.py +199 -0
  54. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/customer_2fa.py +199 -0
  55. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/customer_autologin.py +193 -0
  56. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/customer_login.py +219 -0
  57. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/forgot_password.py +263 -0
  58. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/refresh.py +169 -0
  59. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/register.py +227 -0
  60. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/staff_login.py +199 -0
  61. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/staff_logout.py +161 -0
  62. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/auth/verify_reset_token.py +185 -0
  63. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/__init__.py +0 -0
  64. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/create_group.py +191 -0
  65. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/deactivate_group.py +180 -0
  66. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/get_group.py +168 -0
  67. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/get_instrument.py +191 -0
  68. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/get_instrument_profile.py +167 -0
  69. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/group_impact.py +180 -0
  70. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/list_group_instruments.py +202 -0
  71. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/list_groups.py +167 -0
  72. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/patch_group_instruments.py +275 -0
  73. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/stream_accounts.py +206 -0
  74. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/stream_positions.py +178 -0
  75. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/configuration/update_group.py +191 -0
  76. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/__init__.py +0 -0
  77. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/create_customer.py +213 -0
  78. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/dedupe_check.py +241 -0
  79. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/freeze_customer.py +221 -0
  80. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/get_customer.py +170 -0
  81. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/issue_password_reset.py +234 -0
  82. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/list_customer_accounts.py +226 -0
  83. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/list_customers.py +530 -0
  84. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/mint_autologin_token.py +214 -0
  85. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/open_account.py +221 -0
  86. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/reset_customer_2fa.py +194 -0
  87. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/unfreeze_customer.py +217 -0
  88. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/customers/update_customer.py +221 -0
  89. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/default/__init__.py +0 -0
  90. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/default/calendar_feed.py +256 -0
  91. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/events/__init__.py +0 -0
  92. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/events/list_events.py +255 -0
  93. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/events/stream_events.py +217 -0
  94. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/__init__.py +0 -0
  95. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/acknowledge_alert.py +210 -0
  96. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/delete_limit.py +167 -0
  97. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/exposure_history.py +235 -0
  98. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/exposure_stream.py +165 -0
  99. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/list_alerts.py +250 -0
  100. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/list_limits.py +155 -0
  101. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/live_exposure.py +161 -0
  102. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/symbol_concentration.py +222 -0
  103. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/symbol_exposure.py +167 -0
  104. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/exposure/upsert_limit.py +214 -0
  105. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/instruments/__init__.py +0 -0
  106. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/instruments/list_asset_classes.py +211 -0
  107. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/instruments/list_instruments.py +293 -0
  108. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/instruments/update_instrument.py +238 -0
  109. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/__init__.py +0 -0
  110. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/cancel_my_pending.py +167 -0
  111. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/cancel_withdrawal.py +168 -0
  112. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/change_password.py +239 -0
  113. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/close_my_position.py +213 -0
  114. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/get_me.py +141 -0
  115. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/logout.py +141 -0
  116. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/mark_all_read.py +178 -0
  117. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/mark_read.py +168 -0
  118. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/max_withdrawable.py +141 -0
  119. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/modify_my_pending.py +190 -0
  120. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/modify_my_position.py +198 -0
  121. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_accounts.py +171 -0
  122. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_history.py +190 -0
  123. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_instruments.py +273 -0
  124. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_kyc.py +141 -0
  125. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_notifications.py +245 -0
  126. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_pending.py +205 -0
  127. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_positions.py +221 -0
  128. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_prices.py +249 -0
  129. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_transactions.py +250 -0
  130. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_transfer.py +223 -0
  131. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_transfer_preview.py +223 -0
  132. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/my_unread_count.py +165 -0
  133. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/place_my_pending.py +191 -0
  134. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/place_my_position.py +203 -0
  135. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/request_deposit.py +255 -0
  136. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/request_password_reset.py +189 -0
  137. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/request_support.py +259 -0
  138. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/select_account.py +199 -0
  139. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/set_language.py +175 -0
  140. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/totp_backup_codes.py +141 -0
  141. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/totp_disable.py +175 -0
  142. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/totp_setup.py +141 -0
  143. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/totp_verify.py +187 -0
  144. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/update_me.py +141 -0
  145. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/me/withdraw.py +199 -0
  146. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/__init__.py +0 -0
  147. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/cancel_pending.py +202 -0
  148. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/close_all_positions.py +227 -0
  149. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/close_position.py +234 -0
  150. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/correct_open_price.py +268 -0
  151. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/get_history_item.py +194 -0
  152. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/get_position.py +194 -0
  153. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/get_request_status.py +189 -0
  154. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/list_history.py +490 -0
  155. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/list_pending.py +454 -0
  156. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/list_positions.py +490 -0
  157. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/modify_pending.py +236 -0
  158. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/modify_position.py +228 -0
  159. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/place_pending.py +201 -0
  160. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/positions/place_position.py +217 -0
  161. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/presence/__init__.py +0 -0
  162. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/presence/online_accounts.py +163 -0
  163. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/presence/ping.py +173 -0
  164. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/presence/user_presence.py +188 -0
  165. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/reports/__init__.py +0 -0
  166. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/reports/cashier_report.py +169 -0
  167. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/reports/customers_report.py +173 -0
  168. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/reports/orders_report.py +289 -0
  169. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/reports/simulate.py +238 -0
  170. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/reports/transactions_report.py +197 -0
  171. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/stopouts/__init__.py +0 -0
  172. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/stopouts/list_stopout_events.py +285 -0
  173. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/__init__.py +0 -0
  174. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/approve.py +249 -0
  175. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/chargeback.py +229 -0
  176. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/create_transaction.py +205 -0
  177. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/create_transfer.py +205 -0
  178. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/decline.py +205 -0
  179. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/get_transaction.py +170 -0
  180. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/list_transactions.py +569 -0
  181. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/transactions/transfer_preview.py +225 -0
  182. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/__init__.py +0 -0
  183. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/create_webhook.py +189 -0
  184. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/delete_webhook.py +168 -0
  185. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/get_webhook.py +170 -0
  186. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/list_failures.py +216 -0
  187. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/list_webhooks.py +227 -0
  188. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/rotate_secret.py +190 -0
  189. jumptech_sdk-0.1.0/jumptech_sdk/_generated/api/webhooks/update_webhook.py +201 -0
  190. jumptech_sdk-0.1.0/jumptech_sdk/_generated/client.py +286 -0
  191. jumptech_sdk-0.1.0/jumptech_sdk/_generated/errors.py +16 -0
  192. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/__init__.py +287 -0
  193. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/account_create.py +102 -0
  194. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/account_detail.py +513 -0
  195. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/account_exposure_entry.py +95 -0
  196. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/account_row.py +445 -0
  197. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/account_state_response.py +199 -0
  198. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/account_summary.py +185 -0
  199. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/account_update.py +124 -0
  200. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/acknowledge_alert_body.py +56 -0
  201. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/api_key_create.py +81 -0
  202. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/autologin_token_response.py +95 -0
  203. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/bulk_close_failure.py +79 -0
  204. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/bulk_close_response.py +104 -0
  205. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/cancel_pending_order_response.py +87 -0
  206. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/chargeback_request.py +104 -0
  207. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/chargeback_response.py +103 -0
  208. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/close_account_request.py +81 -0
  209. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/close_account_response.py +95 -0
  210. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/confirm_password_reset_body.py +56 -0
  211. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/correct_open_price_request.py +91 -0
  212. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/correct_open_price_response.py +193 -0
  213. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/create_group_body.py +56 -0
  214. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/create_role_body.py +56 -0
  215. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/create_user_body.py +56 -0
  216. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/create_view_body.py +56 -0
  217. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/customer_2fa_reset_response.py +87 -0
  218. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/customer_create.py +159 -0
  219. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/customer_login_request.py +79 -0
  220. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/customer_register_request.py +170 -0
  221. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/customer_summary.py +404 -0
  222. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/customer_summary_trading_accounts_item.py +56 -0
  223. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/customer_update.py +213 -0
  224. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/dedupe_check_request.py +123 -0
  225. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/dedupe_check_response.py +88 -0
  226. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/dedupe_match.py +151 -0
  227. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/deposit_request.py +103 -0
  228. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/disable_my_2_fa_body.py +56 -0
  229. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/economic_event_response.py +211 -0
  230. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/event_row.py +149 -0
  231. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/event_row_data.py +56 -0
  232. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/execution_status_response.py +254 -0
  233. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/execution_status_response_detail_type_0.py +56 -0
  234. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/exposure_alert_response.py +230 -0
  235. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/exposure_limit_response.py +122 -0
  236. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/exposure_snapshot_response.py +105 -0
  237. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/failure_row.py +138 -0
  238. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/filter_view_response.py +109 -0
  239. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/filter_view_response_filter_state.py +56 -0
  240. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/forgot_password_request.py +71 -0
  241. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/freeze_request.py +71 -0
  242. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/group_instrument_response.py +275 -0
  243. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/group_response.py +251 -0
  244. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/http_validation_error.py +89 -0
  245. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/instrument_row.py +761 -0
  246. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/ledger_entry.py +346 -0
  247. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/ledger_summary_response.py +151 -0
  248. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/list_history_pnl_type_0.py +9 -0
  249. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/list_positions_pnl_type_0.py +9 -0
  250. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/list_views_page.py +11 -0
  251. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/modify_my_pending_body.py +56 -0
  252. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/modify_my_position_body.py +56 -0
  253. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/modify_pending_order_request.py +164 -0
  254. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/modify_sltp_request.py +123 -0
  255. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/modify_sltp_response.py +96 -0
  256. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/my_transfer_body.py +56 -0
  257. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/open_order_response.py +327 -0
  258. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/order_history_response.py +374 -0
  259. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/order_queued_response.py +82 -0
  260. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_account_exposure_entry.py +125 -0
  261. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_account_row.py +125 -0
  262. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_customer_summary.py +125 -0
  263. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_economic_event_response.py +125 -0
  264. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_event_row.py +125 -0
  265. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_exposure_alert_response.py +125 -0
  266. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_exposure_limit_response.py +125 -0
  267. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_exposure_snapshot_response.py +125 -0
  268. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_failure_row.py +125 -0
  269. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_filter_view_response.py +125 -0
  270. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_group_instrument_response.py +125 -0
  271. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_group_response.py +125 -0
  272. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_instrument_row.py +125 -0
  273. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_ledger_entry.py +125 -0
  274. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_role_response.py +125 -0
  275. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_stopout_event_row.py +125 -0
  276. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_transaction_row.py +125 -0
  277. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_user_response.py +125 -0
  278. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/page_webhook_row.py +125 -0
  279. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/pagedict.py +125 -0
  280. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/pagedict_data_item.py +56 -0
  281. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/pagestr.py +109 -0
  282. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/password_change.py +86 -0
  283. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/password_reset_issued_response.py +103 -0
  284. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/patch_group_instruments_body.py +56 -0
  285. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/pending_order_response.py +291 -0
  286. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/place_my_pending_body.py +56 -0
  287. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/place_my_position_body.py +56 -0
  288. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/place_order_request.py +177 -0
  289. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/place_pending_order_request.py +237 -0
  290. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/put_setting_body.py +56 -0
  291. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/reopen_account_response.py +87 -0
  292. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/role_response.py +145 -0
  293. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/rotate_secret_response.py +83 -0
  294. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/select_account_body.py +56 -0
  295. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/set_language_body.py +56 -0
  296. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/simulate_body.py +56 -0
  297. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/staff_password_change.py +85 -0
  298. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/stopout_event_row.py +190 -0
  299. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/support_request.py +85 -0
  300. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/totp_disable_body.py +56 -0
  301. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/totp_verify_body.py +56 -0
  302. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_approve_request.py +84 -0
  303. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_create_manual.py +321 -0
  304. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_create_manual_payment_data_type_0.py +56 -0
  305. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_create_manual_status.py +9 -0
  306. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_create_manual_type.py +22 -0
  307. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_response.py +614 -0
  308. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_response_payment_data_type_0.py +56 -0
  309. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transaction_row.py +531 -0
  310. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transfer_create_crm.py +115 -0
  311. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transfer_preview_response.py +143 -0
  312. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/transfer_response.py +165 -0
  313. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/two_fa_login_verify_request.py +79 -0
  314. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/unfreeze_request.py +93 -0
  315. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/update_group_body.py +56 -0
  316. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/update_instrument_body.py +56 -0
  317. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/update_role_body.py +56 -0
  318. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/update_user_body.py +56 -0
  319. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/update_view_body.py +56 -0
  320. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/upsert_limit_body.py +56 -0
  321. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/user_login_request.py +101 -0
  322. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/user_response.py +180 -0
  323. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/v1_error_detail.py +102 -0
  324. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/v1_error_envelope.py +84 -0
  325. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/validation_error.py +102 -0
  326. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/verify_my_2_fa_body.py +56 -0
  327. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/webhook_create.py +122 -0
  328. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/webhook_created_response.py +207 -0
  329. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/webhook_row.py +196 -0
  330. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/webhook_update.py +176 -0
  331. jumptech_sdk-0.1.0/jumptech_sdk/_generated/models/withdraw_body.py +56 -0
  332. jumptech_sdk-0.1.0/jumptech_sdk/_generated/types.py +54 -0
  333. jumptech_sdk-0.1.0/jumptech_sdk/_spec.py +8 -0
  334. jumptech_sdk-0.1.0/jumptech_sdk/_transport.py +166 -0
  335. jumptech_sdk-0.1.0/jumptech_sdk/aio.py +498 -0
  336. jumptech_sdk-0.1.0/jumptech_sdk/client.py +294 -0
  337. jumptech_sdk-0.1.0/jumptech_sdk/doctor.py +201 -0
  338. jumptech_sdk-0.1.0/jumptech_sdk/errors.py +211 -0
  339. jumptech_sdk-0.1.0/jumptech_sdk/events.py +245 -0
  340. jumptech_sdk-0.1.0/jumptech_sdk/idempotency.py +268 -0
  341. jumptech_sdk-0.1.0/jumptech_sdk/models.py +17 -0
  342. jumptech_sdk-0.1.0/jumptech_sdk/resources/__init__.py +6 -0
  343. jumptech_sdk-0.1.0/jumptech_sdk/resources/positions.py +288 -0
  344. jumptech_sdk-0.1.0/jumptech_sdk/resources/transactions.py +160 -0
  345. jumptech_sdk-0.1.0/jumptech_sdk/signing.py +61 -0
  346. jumptech_sdk-0.1.0/jumptech_sdk/stores.py +217 -0
  347. jumptech_sdk-0.1.0/jumptech_sdk/webhooks.py +161 -0
  348. jumptech_sdk-0.1.0/pyproject.toml +55 -0
@@ -0,0 +1,16 @@
1
+ # Local to sdk/python, so hatchling's sdist build finds THIS file rather than
2
+ # walking up to the repo root's — otherwise the whole repo .gitignore (worktree
3
+ # notes, deployment paths, credential-dump warnings) gets force-included
4
+ # verbatim into every `jumptech-sdk` sdist. See pyproject.toml's sdist `include`.
5
+ __pycache__/
6
+ *.py[cod]
7
+ *.egg-info/
8
+ .pytest_cache/
9
+ .coverage
10
+ htmlcov/
11
+ dist/
12
+ build/
13
+ .venv*/
14
+ # SqliteStore's default store_path — created the moment a JumpTech() client is
15
+ # constructed without an explicit path, holds no committable state.
16
+ jumptech.db
@@ -0,0 +1,306 @@
1
+ Metadata-Version: 2.4
2
+ Name: jumptech-sdk
3
+ Version: 0.1.0
4
+ Summary: Python client for the JumpTech engine /v1 API
5
+ License: Proprietary
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: attrs>=22.2
8
+ Requires-Dist: httpx>=0.27
9
+ Requires-Dist: python-dateutil>=2.8.1
10
+ Description-Content-Type: text/markdown
11
+
12
+ # jumptech-sdk
13
+
14
+ Python client for the JumpTech engine `/v1` API.
15
+
16
+ Money mutations are the reason this exists rather than a bare generated client:
17
+ a retry here is a **replay**, not a second deposit, and it stays a replay across
18
+ a process crash.
19
+
20
+ ```bash
21
+ pip install jumptech-sdk
22
+ ```
23
+
24
+ Python 3.11+. Three dependencies (`httpx`, `attrs`, `python-dateutil`) — the
25
+ durable store and the webhook verifier use only the standard library.
26
+
27
+ ---
28
+
29
+ ## Before you write any code
30
+
31
+ Two credentials come from the broker you are integrating with. Neither is
32
+ self-service; a broker operator issues them.
33
+
34
+ | You need | Looks like | Notes |
35
+ |---|---|---|
36
+ | **API key** | `jt_demo_…` / `jt_live_…` | Ask for a **`jt_demo_`** key first. Same API, same code path, no real money. |
37
+ | **Webhook secret** | `whsec_…` | Only if you want events pushed to you. Polling works without it. |
38
+
39
+ Also ask **which base URL** — each broker runs its own engine, so a partner
40
+ serving three brokers holds three keys against three URLs.
41
+
42
+ Your key may be **read-only**, in which case every non-`GET` returns
43
+ `403 readonly_key`. Worth confirming up front rather than discovering it on your
44
+ first deposit.
45
+
46
+ ## Check the connection first
47
+
48
+ Before writing anything, prove the credential and the network work:
49
+
50
+ ```bash
51
+ python -m jumptech_sdk doctor --base-url https://api.yourbroker.com --api-key jt_demo_...
52
+ ```
53
+
54
+ It names the actual problem — an unknown key, a read-only key, or an engine that
55
+ could not reach its own auth service (which is *not* a bad key, and rotating it
56
+ will not help). If you are receiving webhooks, point it at your own endpoint and
57
+ it will send one correctly-signed delivery:
58
+
59
+ ```bash
60
+ python -m jumptech_sdk doctor --base-url https://api.yourbroker.com --api-key jt_demo_... \
61
+ --probe https://crm.example/jt --secret whsec_...
62
+ ```
63
+
64
+ Rejected means the fault is in your verifier. Accepted means it is upstream.
65
+ That distinction is the classic first-day afternoon, and this answers it in a
66
+ second.
67
+
68
+ ## Your first call
69
+
70
+ ```python
71
+ from jumptech_sdk import JumpTech
72
+
73
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_demo_...")
74
+
75
+ page = jt.transactions.list(page_size=5)
76
+ for tx in page.data:
77
+ print(tx.id, tx.type, tx.amount, tx.account_currency)
78
+
79
+ jt.close()
80
+ ```
81
+
82
+ Every list returns the same envelope: `.data`, `.total`, and `.next_cursor` on
83
+ the append-only logs. Money values always arrive with their currency beside
84
+ them — read `account_currency`, never assume.
85
+
86
+ Use it as a context manager and you can forget `close()`:
87
+
88
+ ```python
89
+ with JumpTech("https://api.yourbroker.com", api_key="jt_demo_...") as jt:
90
+ print(jt.transactions.list(page_size=1).total)
91
+ ```
92
+
93
+ ---
94
+
95
+ ## What is typed in v0.1
96
+
97
+ Hand-written, typed wrappers: **`jt.transactions`** (create, get, list, approve,
98
+ decline, chargeback) and **`jt.positions`** (place, place_async, request_status,
99
+ list, get, close). Plus **`jt.events`** for the event log and **`jt.webhooks`**
100
+ for inbound deliveries.
101
+
102
+ Everything else on `/v1` — customers, accounts, transfers, pending orders,
103
+ groups, instruments, the audit log — is reachable but **untyped**: the generated
104
+ client under `jumptech_sdk._generated` covers the whole spec, and calls made
105
+ through it get **no** retry, idempotency-key or crash-recovery help from the
106
+ layer above. If you are moving money through one of those, mint and reuse a key
107
+ yourself, or wait for a typed wrapper.
108
+
109
+ Retries, the idempotency key, the event dedupe store, `recover()` and the webhook
110
+ verifier work identically for whichever resource you call.
111
+
112
+ ## Money in
113
+
114
+ Mutations carry an `Idempotency-Key` automatically, and a retry reuses it — so a
115
+ retried deposit is a replay, not a second deposit.
116
+
117
+ ```python
118
+ import uuid
119
+
120
+ from jumptech_sdk import JumpTech
121
+ from jumptech_sdk.models import TransactionCreateManual, TransactionCreateManualType
122
+
123
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_live_...")
124
+
125
+ tx = jt.transactions.create(TransactionCreateManual(
126
+ type=TransactionCreateManualType.DEPOSIT, # an UPPERCASE enum, not a string
127
+ account_id=uuid.UUID("3f7c0d1e-2b4a-4c8e-9a71-2d1f6b3e5c40"), # the account's
128
+ # UUID, not the login
129
+ amount="100.00",
130
+ account_amount="100.00", # deposits must be in the account's currency
131
+ currency="USD",
132
+ ))
133
+ jt.transactions.approve(tx.id)
134
+ ```
135
+
136
+ Two rules the API enforces and the SDK cannot guess for you: a deposit or
137
+ withdrawal **must** be in the account's own currency, and `account_id` is the
138
+ account's UUID — not its numeric login.
139
+
140
+ ## Handling failures
141
+
142
+ Catch the specific class when you can act on it, `JumpTechError` when you cannot.
143
+ Every one carries `.code`, `.message`, `.status` and `.request_id` — quote
144
+ `request_id` when you ask the broker about a call.
145
+
146
+ ```python
147
+ from jumptech_sdk import (
148
+ InsufficientMargin, JumpTechError, NotFound, RateLimited,
149
+ UnknownOutcome, ValidationError,
150
+ )
151
+
152
+ try:
153
+ jt.transactions.approve(tx_id)
154
+ except ValidationError as e:
155
+ print("the request was malformed:", e.fields) # per-field, from the API
156
+ except NotFound:
157
+ print("no such transaction")
158
+ except InsufficientMargin as e:
159
+ print("shortfall:", e.details) # {required, available, ...}
160
+ except RateLimited as e:
161
+ print("slow down for", e.retry_after, "seconds") # already honoured on retry
162
+ except UnknownOutcome as e:
163
+ print("MAY have committed — reconcile, do not retry:", e)
164
+ except JumpTechError as e:
165
+ print(e.status, e.code, e.message, e.request_id)
166
+ ```
167
+
168
+ **`UnknownOutcome` is the one to read twice.** It means the request left your
169
+ process and no answer came back, so the mutation may or may not have committed.
170
+ The SDK deliberately does **not** retry it — see *After a crash*.
171
+
172
+ Retries are automatic and already done by the time an exception reaches you:
173
+ 5 attempts, exponential backoff from 0.5s capped at 8s, on `429`, `502` and
174
+ `503` only. Tune it if you must:
175
+
176
+ ```python
177
+ from jumptech_sdk import JumpTech, RetryPolicy
178
+
179
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_live_...",
180
+ policy=RetryPolicy(max_attempts=3, base_delay=1.0))
181
+ ```
182
+
183
+ ## Orders
184
+
185
+ Placement is asynchronous — a 202 and a request id. `place()` polls it to a
186
+ terminal state; `place_async()` hands you the id.
187
+
188
+ ```python
189
+ from jumptech_sdk.models import PlaceOrderRequest
190
+
191
+ fill = jt.positions.place(PlaceOrderRequest(
192
+ symbol="EURUSD", cmd=0, volume=0.10, login=5001
193
+ ))
194
+ print(fill.order, fill.open_price) # cmd: 0=buy, 1=sell. Ticket is `order`.
195
+ ```
196
+
197
+ A rejection raises rather than returning a status: `MarginRejected` for
198
+ insufficient margin, `OrderRejected` otherwise, and `OrderRequestExpired` if the
199
+ request id aged out — which says nothing about whether the order executed, so
200
+ check the book before retrying.
201
+
202
+ ## Events
203
+
204
+ One handler, two delivery routes, deduped against each other because both share
205
+ the same store:
206
+
207
+ ```python
208
+ from fastapi import FastAPI
209
+
210
+ from jumptech_sdk import JumpTech
211
+
212
+ app = FastAPI()
213
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_live_...",
214
+ webhook_secret="whsec_...") # jt.webhooks is None without this
215
+
216
+ @jt.on_event
217
+ def handle(event):
218
+ print(event["type"], event["event_id"])
219
+
220
+ jt.webhooks.mount(app, "/jt") # push: verifies, dedupes, dispatches
221
+ jt.events.reconcile() # pull: same handler, same dedupe store
222
+ ```
223
+
224
+ Flask and Django get `jt.webhooks.flask_view()` and `jt.webhooks.django_view()`.
225
+ Use an adapter rather than verifying by hand: the signature is computed over the
226
+ **raw bytes**, and a handler that verifies against a re-serialised object rejects
227
+ every delivery.
228
+
229
+ Your handler runs **at most once per event** across both routes, and only counts
230
+ as handled once it returns — if it raises, nothing is recorded and the event is
231
+ redelivered.
232
+
233
+ Reconcile is one pass — schedule it with whatever you already use. Treat
234
+ `GET /v1/events` as the system of record and webhooks as an optimisation, not the
235
+ other way round.
236
+
237
+ ## After a crash
238
+
239
+ `recover()` finishes every pending intent with its ORIGINAL key, so each returns
240
+ the original answer instead of booking a second one. It hands back one
241
+ `(key, outcome)` per intent — the response, or the exception if that one did not
242
+ complete. An intent whose last outcome was unknown (a 5xx, or a read timeout: the
243
+ handler may have committed and the API does not cache either) is refused, and its
244
+ outcome says so. Reconcile those, then resolve them by hand.
245
+
246
+ ```python
247
+ for key, intent in jt.unresolved():
248
+ ... # reconcile against GET /v1/events or the
249
+ # resource itself, then:
250
+ jt.store.resolve_intent(key, "done")
251
+
252
+ for key, outcome in jt.recover():
253
+ if isinstance(outcome, Exception):
254
+ ... # this one did not finish; the others still ran
255
+ ```
256
+
257
+ Call `recover()` on startup. It is the payoff for the store being on disk.
258
+
259
+ ## Async
260
+
261
+ `AsyncJumpTech` in `jumptech_sdk.aio` is the same API with `await`, and
262
+ `jt.events.stream()` becomes an async generator:
263
+
264
+ ```python
265
+ from jumptech_sdk.aio import AsyncJumpTech
266
+
267
+ jt = AsyncJumpTech("https://api.yourbroker.com", api_key="jt_live_...")
268
+
269
+ async def consume():
270
+ async for event in jt.events.stream():
271
+ ...
272
+ ```
273
+
274
+ `stream()` yields **per event, not per page**, so persisting after each one costs
275
+ one duplicate on a crash instead of a page. It does not stop at the end of the
276
+ backlog — it keeps polling, so catch-up and live tail are the same loop.
277
+
278
+ ## State
279
+
280
+ A local sqlite file (`jumptech.db` by default) holds pending idempotency intents
281
+ and handled event ids. Point it somewhere durable in production:
282
+
283
+ ```python
284
+ jt = JumpTech(
285
+ "https://api.yourbroker.com", api_key="jt_live_...",
286
+ store_path="/var/lib/yourapp/jumptech.db",
287
+ )
288
+ ```
289
+
290
+ An in-memory store does not survive the crash it exists to protect against, so
291
+ `InMemoryStore` is for tests only. Implement the `Store` protocol to put this in
292
+ your own database instead.
293
+
294
+ Both clients close their connection pool and their store — `jt.close()`, or
295
+ `with JumpTech(...) as jt:` (`await jt.aclose()` / `async with` on the async
296
+ one).
297
+
298
+ ## Going to production
299
+
300
+ - [ ] Swap the `jt_demo_` key for `jt_live_`.
301
+ - [ ] Point `store_path` at durable, writable disk that survives a redeploy.
302
+ - [ ] Call `jt.recover()` on startup.
303
+ - [ ] Schedule `jt.events.reconcile()` — webhooks alone will miss events.
304
+ - [ ] Decide what `UnknownOutcome` does in your system. It is the only outcome
305
+ that needs a human or a reconciliation, and it will happen.
306
+ - [ ] Log `request_id` from every `JumpTechError`.
@@ -0,0 +1,295 @@
1
+ # jumptech-sdk
2
+
3
+ Python client for the JumpTech engine `/v1` API.
4
+
5
+ Money mutations are the reason this exists rather than a bare generated client:
6
+ a retry here is a **replay**, not a second deposit, and it stays a replay across
7
+ a process crash.
8
+
9
+ ```bash
10
+ pip install jumptech-sdk
11
+ ```
12
+
13
+ Python 3.11+. Three dependencies (`httpx`, `attrs`, `python-dateutil`) — the
14
+ durable store and the webhook verifier use only the standard library.
15
+
16
+ ---
17
+
18
+ ## Before you write any code
19
+
20
+ Two credentials come from the broker you are integrating with. Neither is
21
+ self-service; a broker operator issues them.
22
+
23
+ | You need | Looks like | Notes |
24
+ |---|---|---|
25
+ | **API key** | `jt_demo_…` / `jt_live_…` | Ask for a **`jt_demo_`** key first. Same API, same code path, no real money. |
26
+ | **Webhook secret** | `whsec_…` | Only if you want events pushed to you. Polling works without it. |
27
+
28
+ Also ask **which base URL** — each broker runs its own engine, so a partner
29
+ serving three brokers holds three keys against three URLs.
30
+
31
+ Your key may be **read-only**, in which case every non-`GET` returns
32
+ `403 readonly_key`. Worth confirming up front rather than discovering it on your
33
+ first deposit.
34
+
35
+ ## Check the connection first
36
+
37
+ Before writing anything, prove the credential and the network work:
38
+
39
+ ```bash
40
+ python -m jumptech_sdk doctor --base-url https://api.yourbroker.com --api-key jt_demo_...
41
+ ```
42
+
43
+ It names the actual problem — an unknown key, a read-only key, or an engine that
44
+ could not reach its own auth service (which is *not* a bad key, and rotating it
45
+ will not help). If you are receiving webhooks, point it at your own endpoint and
46
+ it will send one correctly-signed delivery:
47
+
48
+ ```bash
49
+ python -m jumptech_sdk doctor --base-url https://api.yourbroker.com --api-key jt_demo_... \
50
+ --probe https://crm.example/jt --secret whsec_...
51
+ ```
52
+
53
+ Rejected means the fault is in your verifier. Accepted means it is upstream.
54
+ That distinction is the classic first-day afternoon, and this answers it in a
55
+ second.
56
+
57
+ ## Your first call
58
+
59
+ ```python
60
+ from jumptech_sdk import JumpTech
61
+
62
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_demo_...")
63
+
64
+ page = jt.transactions.list(page_size=5)
65
+ for tx in page.data:
66
+ print(tx.id, tx.type, tx.amount, tx.account_currency)
67
+
68
+ jt.close()
69
+ ```
70
+
71
+ Every list returns the same envelope: `.data`, `.total`, and `.next_cursor` on
72
+ the append-only logs. Money values always arrive with their currency beside
73
+ them — read `account_currency`, never assume.
74
+
75
+ Use it as a context manager and you can forget `close()`:
76
+
77
+ ```python
78
+ with JumpTech("https://api.yourbroker.com", api_key="jt_demo_...") as jt:
79
+ print(jt.transactions.list(page_size=1).total)
80
+ ```
81
+
82
+ ---
83
+
84
+ ## What is typed in v0.1
85
+
86
+ Hand-written, typed wrappers: **`jt.transactions`** (create, get, list, approve,
87
+ decline, chargeback) and **`jt.positions`** (place, place_async, request_status,
88
+ list, get, close). Plus **`jt.events`** for the event log and **`jt.webhooks`**
89
+ for inbound deliveries.
90
+
91
+ Everything else on `/v1` — customers, accounts, transfers, pending orders,
92
+ groups, instruments, the audit log — is reachable but **untyped**: the generated
93
+ client under `jumptech_sdk._generated` covers the whole spec, and calls made
94
+ through it get **no** retry, idempotency-key or crash-recovery help from the
95
+ layer above. If you are moving money through one of those, mint and reuse a key
96
+ yourself, or wait for a typed wrapper.
97
+
98
+ Retries, the idempotency key, the event dedupe store, `recover()` and the webhook
99
+ verifier work identically for whichever resource you call.
100
+
101
+ ## Money in
102
+
103
+ Mutations carry an `Idempotency-Key` automatically, and a retry reuses it — so a
104
+ retried deposit is a replay, not a second deposit.
105
+
106
+ ```python
107
+ import uuid
108
+
109
+ from jumptech_sdk import JumpTech
110
+ from jumptech_sdk.models import TransactionCreateManual, TransactionCreateManualType
111
+
112
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_live_...")
113
+
114
+ tx = jt.transactions.create(TransactionCreateManual(
115
+ type=TransactionCreateManualType.DEPOSIT, # an UPPERCASE enum, not a string
116
+ account_id=uuid.UUID("3f7c0d1e-2b4a-4c8e-9a71-2d1f6b3e5c40"), # the account's
117
+ # UUID, not the login
118
+ amount="100.00",
119
+ account_amount="100.00", # deposits must be in the account's currency
120
+ currency="USD",
121
+ ))
122
+ jt.transactions.approve(tx.id)
123
+ ```
124
+
125
+ Two rules the API enforces and the SDK cannot guess for you: a deposit or
126
+ withdrawal **must** be in the account's own currency, and `account_id` is the
127
+ account's UUID — not its numeric login.
128
+
129
+ ## Handling failures
130
+
131
+ Catch the specific class when you can act on it, `JumpTechError` when you cannot.
132
+ Every one carries `.code`, `.message`, `.status` and `.request_id` — quote
133
+ `request_id` when you ask the broker about a call.
134
+
135
+ ```python
136
+ from jumptech_sdk import (
137
+ InsufficientMargin, JumpTechError, NotFound, RateLimited,
138
+ UnknownOutcome, ValidationError,
139
+ )
140
+
141
+ try:
142
+ jt.transactions.approve(tx_id)
143
+ except ValidationError as e:
144
+ print("the request was malformed:", e.fields) # per-field, from the API
145
+ except NotFound:
146
+ print("no such transaction")
147
+ except InsufficientMargin as e:
148
+ print("shortfall:", e.details) # {required, available, ...}
149
+ except RateLimited as e:
150
+ print("slow down for", e.retry_after, "seconds") # already honoured on retry
151
+ except UnknownOutcome as e:
152
+ print("MAY have committed — reconcile, do not retry:", e)
153
+ except JumpTechError as e:
154
+ print(e.status, e.code, e.message, e.request_id)
155
+ ```
156
+
157
+ **`UnknownOutcome` is the one to read twice.** It means the request left your
158
+ process and no answer came back, so the mutation may or may not have committed.
159
+ The SDK deliberately does **not** retry it — see *After a crash*.
160
+
161
+ Retries are automatic and already done by the time an exception reaches you:
162
+ 5 attempts, exponential backoff from 0.5s capped at 8s, on `429`, `502` and
163
+ `503` only. Tune it if you must:
164
+
165
+ ```python
166
+ from jumptech_sdk import JumpTech, RetryPolicy
167
+
168
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_live_...",
169
+ policy=RetryPolicy(max_attempts=3, base_delay=1.0))
170
+ ```
171
+
172
+ ## Orders
173
+
174
+ Placement is asynchronous — a 202 and a request id. `place()` polls it to a
175
+ terminal state; `place_async()` hands you the id.
176
+
177
+ ```python
178
+ from jumptech_sdk.models import PlaceOrderRequest
179
+
180
+ fill = jt.positions.place(PlaceOrderRequest(
181
+ symbol="EURUSD", cmd=0, volume=0.10, login=5001
182
+ ))
183
+ print(fill.order, fill.open_price) # cmd: 0=buy, 1=sell. Ticket is `order`.
184
+ ```
185
+
186
+ A rejection raises rather than returning a status: `MarginRejected` for
187
+ insufficient margin, `OrderRejected` otherwise, and `OrderRequestExpired` if the
188
+ request id aged out — which says nothing about whether the order executed, so
189
+ check the book before retrying.
190
+
191
+ ## Events
192
+
193
+ One handler, two delivery routes, deduped against each other because both share
194
+ the same store:
195
+
196
+ ```python
197
+ from fastapi import FastAPI
198
+
199
+ from jumptech_sdk import JumpTech
200
+
201
+ app = FastAPI()
202
+ jt = JumpTech("https://api.yourbroker.com", api_key="jt_live_...",
203
+ webhook_secret="whsec_...") # jt.webhooks is None without this
204
+
205
+ @jt.on_event
206
+ def handle(event):
207
+ print(event["type"], event["event_id"])
208
+
209
+ jt.webhooks.mount(app, "/jt") # push: verifies, dedupes, dispatches
210
+ jt.events.reconcile() # pull: same handler, same dedupe store
211
+ ```
212
+
213
+ Flask and Django get `jt.webhooks.flask_view()` and `jt.webhooks.django_view()`.
214
+ Use an adapter rather than verifying by hand: the signature is computed over the
215
+ **raw bytes**, and a handler that verifies against a re-serialised object rejects
216
+ every delivery.
217
+
218
+ Your handler runs **at most once per event** across both routes, and only counts
219
+ as handled once it returns — if it raises, nothing is recorded and the event is
220
+ redelivered.
221
+
222
+ Reconcile is one pass — schedule it with whatever you already use. Treat
223
+ `GET /v1/events` as the system of record and webhooks as an optimisation, not the
224
+ other way round.
225
+
226
+ ## After a crash
227
+
228
+ `recover()` finishes every pending intent with its ORIGINAL key, so each returns
229
+ the original answer instead of booking a second one. It hands back one
230
+ `(key, outcome)` per intent — the response, or the exception if that one did not
231
+ complete. An intent whose last outcome was unknown (a 5xx, or a read timeout: the
232
+ handler may have committed and the API does not cache either) is refused, and its
233
+ outcome says so. Reconcile those, then resolve them by hand.
234
+
235
+ ```python
236
+ for key, intent in jt.unresolved():
237
+ ... # reconcile against GET /v1/events or the
238
+ # resource itself, then:
239
+ jt.store.resolve_intent(key, "done")
240
+
241
+ for key, outcome in jt.recover():
242
+ if isinstance(outcome, Exception):
243
+ ... # this one did not finish; the others still ran
244
+ ```
245
+
246
+ Call `recover()` on startup. It is the payoff for the store being on disk.
247
+
248
+ ## Async
249
+
250
+ `AsyncJumpTech` in `jumptech_sdk.aio` is the same API with `await`, and
251
+ `jt.events.stream()` becomes an async generator:
252
+
253
+ ```python
254
+ from jumptech_sdk.aio import AsyncJumpTech
255
+
256
+ jt = AsyncJumpTech("https://api.yourbroker.com", api_key="jt_live_...")
257
+
258
+ async def consume():
259
+ async for event in jt.events.stream():
260
+ ...
261
+ ```
262
+
263
+ `stream()` yields **per event, not per page**, so persisting after each one costs
264
+ one duplicate on a crash instead of a page. It does not stop at the end of the
265
+ backlog — it keeps polling, so catch-up and live tail are the same loop.
266
+
267
+ ## State
268
+
269
+ A local sqlite file (`jumptech.db` by default) holds pending idempotency intents
270
+ and handled event ids. Point it somewhere durable in production:
271
+
272
+ ```python
273
+ jt = JumpTech(
274
+ "https://api.yourbroker.com", api_key="jt_live_...",
275
+ store_path="/var/lib/yourapp/jumptech.db",
276
+ )
277
+ ```
278
+
279
+ An in-memory store does not survive the crash it exists to protect against, so
280
+ `InMemoryStore` is for tests only. Implement the `Store` protocol to put this in
281
+ your own database instead.
282
+
283
+ Both clients close their connection pool and their store — `jt.close()`, or
284
+ `with JumpTech(...) as jt:` (`await jt.aclose()` / `async with` on the async
285
+ one).
286
+
287
+ ## Going to production
288
+
289
+ - [ ] Swap the `jt_demo_` key for `jt_live_`.
290
+ - [ ] Point `store_path` at durable, writable disk that survives a redeploy.
291
+ - [ ] Call `jt.recover()` on startup.
292
+ - [ ] Schedule `jt.events.reconcile()` — webhooks alone will miss events.
293
+ - [ ] Decide what `UnknownOutcome` does in your system. It is the only outcome
294
+ that needs a human or a reconciliation, and it will happen.
295
+ - [ ] Log `request_id` from every `JumpTechError`.
@@ -0,0 +1,61 @@
1
+ """Python client for the JumpTech engine /v1 API."""
2
+
3
+ from jumptech_sdk.aio import AsyncJumpTech
4
+ from jumptech_sdk.client import JumpTech, __version__
5
+ from jumptech_sdk.errors import (
6
+ AuthError,
7
+ Conflict,
8
+ InsufficientMargin,
9
+ JumpTechError,
10
+ MissingIdempotencyKey,
11
+ NotFound,
12
+ RateLimited,
13
+ Unavailable,
14
+ UnknownOutcome,
15
+ ValidationError,
16
+ )
17
+ from jumptech_sdk.idempotency import RetryPolicy
18
+ from jumptech_sdk.resources.positions import (
19
+ MarginRejected,
20
+ OrderRejected,
21
+ OrderRequestExpired,
22
+ )
23
+ from jumptech_sdk.signing import MAX_SKEW_S, verify
24
+ from jumptech_sdk.stores import InMemoryStore, SqliteStore, Store
25
+ from jumptech_sdk.webhooks import SignatureError
26
+
27
+ __all__ = [
28
+ # The two clients.
29
+ "JumpTech",
30
+ "AsyncJumpTech",
31
+ # The taxonomy an integrator writes `except` clauses against.
32
+ "AuthError",
33
+ "Conflict",
34
+ "InsufficientMargin",
35
+ "JumpTechError",
36
+ "MissingIdempotencyKey",
37
+ "NotFound",
38
+ "RateLimited",
39
+ "Unavailable",
40
+ "UnknownOutcome",
41
+ "ValidationError",
42
+ # Order rejections, including MarginRejected — the class `place()` raises
43
+ # for an async `insufficient_margin`, which was reachable but unexported.
44
+ "OrderRejected",
45
+ "MarginRejected",
46
+ "OrderRequestExpired",
47
+ "SignatureError",
48
+ # Stores, and the Protocol to implement a custom one against.
49
+ "InMemoryStore",
50
+ "SqliteStore",
51
+ "Store",
52
+ # `RetryPolicy` is a constructor argument, so it belongs on the front door.
53
+ "RetryPolicy",
54
+ # For a receiver that is not using `Webhooks` — the executable verifier
55
+ # docs/v1-api.md publishes, and the skew window it must enforce.
56
+ "verify",
57
+ "MAX_SKEW_S",
58
+ # On the front door because a bug report starts with it, and because
59
+ # `doctor` prints it. `client.py` derives it from installed metadata.
60
+ "__version__",
61
+ ]
@@ -0,0 +1,7 @@
1
+ """`python -m jumptech_sdk doctor …`"""
2
+
3
+ import sys
4
+
5
+ from jumptech_sdk.doctor import console_main
6
+
7
+ sys.exit(console_main())