summaryrefslogtreecommitdiff
path: root/scripts/paper-trade-test.scm
diff options
context:
space:
mode:
authorDan Rostovtsev <dan@rostovtsev.org>2026-04-06 15:48:14 -0400
committerDan Rostovtsev <dan@rostovtsev.org>2026-04-06 15:48:14 -0400
commit0716a22d1fab76a18606ec031d33914ccbc56633 (patch)
treed63cc7ee4fd73bac935a776d09dc0ec78c8335e2 /scripts/paper-trade-test.scm
parent2c6cf786c151118232533a6cfbc769ce1514aa9e (diff)
First working IBKR API implementation.HEADmain
* doc/ibkr.org: Docs for using the IBKR API. * manifest.scm: Guix Manifest of all project dependencies. * scripts/run-gateway.bash: A script for building and deploying the IBKR Client Gateway. * src/ibkr/api.scm: Support for specific endpoints, and generic tools for using the IBKR API. * src/ibkr/types.scm: Basic types for the IBKR endpoints. Orders, positions, securities, etc. * test/*.json: IBKR response and request examples for testing. * test/api.scm: Response handling and endpoint construction. * test/types.scm: Tests JSON parsing of IBKR requests and responses.
Diffstat (limited to 'scripts/paper-trade-test.scm')
-rw-r--r--scripts/paper-trade-test.scm95
1 files changed, 95 insertions, 0 deletions
diff --git a/scripts/paper-trade-test.scm b/scripts/paper-trade-test.scm
new file mode 100644
index 0000000..29d1218
--- /dev/null
+++ b/scripts/paper-trade-test.scm
@@ -0,0 +1,95 @@
+(use-modules (ibkr api) (ibkr types)
+ (json)
+ (srfi srfi-1) (srfi srfi-64)
+ (ice-9 format))
+
+;; connect
+(display "connecting to ibkr api\n")
+(define base "http://localhost:5000")
+(format #t "connected: ~a\n" (auth-status-connected (auth-status base)))
+(sleep 1)
+
+;; check mkt
+(display "querying market data\n")
+(define ibkr (contract-for-stock-ticker base "IBKR" "NASDAQ"))
+(define ibkr-id (contract-id ibkr))
+(define ibkr-ref-px (contract-snapshot base ibkr-id 'last-trade))
+(format #t "contract-id(IBKR)=~a\n" ibkr-id)
+(format #t "last-trade(IBKR)=$~a\n" ibkr-ref-px)
+(sleep 1)
+
+;; acct info
+(display "querying account info\n")
+(define accts (accounts base))
+(define acct (car accts))
+(define acct-id (account-id acct))
+(define pos-t0 (positions base acct-id))
+(define bal-t0 (ledger-cash-balance (ledger base acct-id "USD")))
+(format #t "balance(t0)=$~a\n" bal-t0)
+(format #t "account-type=~a\n" (account-type acct))
+(if (not (equal? (account-type acct) "DEMO"))
+ (exit 1))
+(sleep 1)
+
+;; buy
+(define buy (make-order acct-id ibkr-id "MARKET" "BUY" "IOC" 10.0))
+(format #t "buy-order=~a\n" buy)
+(define buy-preview (order-preview base acct-id buy))
+(format #t "buy-preview=~a\n" buy-preview)
+(define buy-warning (order-submit base acct-id buy)) ;; no market data?
+(format #t "buy-warning=~a\n" buy-warning)
+(define buy-reply (order-confirm base (order-warning-id buy-warning)))
+(format #t "buy-reply=~a\n" buy-reply)
+(define buy-oid (order-reply-order-id buy-reply))
+(format #t "buy-oid=~a\n" buy-oid)
+(format #t "sleeping to wait for fill\n") (sleep 5) ;; wait for fill
+(define buy-status (order-status base buy-oid))
+(format #t "buy-status=~a\n" buy-status)
+(define buy-px (string->number (order-status-average-price buy-status)))
+(define buy-qty (string->number (order-status-quantity buy-status)))
+(define pos-t1 (positions base acct-id))
+(define bal-t1 (ledger-cash-balance (ledger base acct-id "USD")))
+
+(display "sleeping to avoid rate limit\n") (sleep 5)
+
+;; sell
+(define sell (make-order acct-id ibkr-id "MARKET" "SELL" "IOC" 10.0))
+(format #t "sell-order=~a\n" sell)
+(define sell-preview (order-preview base acct-id sell))
+(format #t "sell-preview=~a\n" sell-preview)
+(define sell-warning (order-submit base acct-id sell)) ;; no market data?
+(format #t "sell-warning=~a\n" sell-warning)
+(define sell-reply (order-confirm base (order-warning-id sell-warning)))
+(format #t "sell-reply=~a\n" sell-reply)
+(define sell-oid (order-reply-order-id sell-reply))
+(format #t "sell-oid=~a\n" sell-oid)
+(format #t "sleeping to wait for fill\n") (sleep 5) ;; wait for fill
+(define sell-status (order-status base sell-oid))
+(format #t "sell-status=~a\n" buy-status)
+(define sell-px (string->number (order-status-average-price sell-status)))
+(define sell-qty (string->number (order-status-quantity sell-status)))
+(define pos-t2 (positions base acct-id))
+(define bal-t2 (ledger-cash-balance (ledger base acct-id "USD")))
+
+;; checks
+(define (position poslst contract-id)
+ (find
+ (lambda (p)
+ (equal? (string->number (position-contract-id p)) contract-id))
+ poslst))
+(define (abs-rel-diff a b) (/ (* 2 (abs (- a b))) (+ (abs a) (abs b))))
+(define (within-n-percent n a b) (< (* 100 (abs-rel-diff a b)) n))
+(test-begin "paper-trade")
+(test-approximate "ibkr-qty-t0" 0 (position-quantity (position pos-t0 ibkr-id)) 1e-3)
+(test-approximate "ibkr-qty-t1" 10 (position-quantity (position pos-t1 ibkr-id)) 1e-3)
+(test-approximate "ibkr-qty-t2" 0 (position-quantity (position pos-t2 ibkr-id)) 1e-3)
+(test-approximate "ibkr-buy-qty" 10 buy-qty 1e-3)
+(test-approximate "ibkr-sell-qty" 10 sell-qty 1e-3)
+(test-assert "buy-px-close-to-ref-px" (within-n-percent 5 buy-px ibkr-ref-px))
+(test-assert "sell-px-close-to-ref-px" (within-n-percent 5 sell-px ibkr-ref-px))
+
+;; don't forget commission!
+(test-approximate "bal-t1-expected" (- bal-t0 (* 10.0 buy-px)) bal-t1 1.05)
+(test-approximate "bal-t2-expected" (+ bal-t1 (* 10.0 sell-px)) bal-t2 1.05)
+
+(test-end "paper-trade")