(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")