Branch data Line data Source code
1 : : ;; Title: BME032 Strategy for scalar market hedging
2 : : ;; Synopsis:
3 : : ;; Provides a hook for scalar markets to run hedging function during cool down.
4 : : ;; Description:
5 : : ;; The idea is to create an algorithmic hedge fund - we assume the community
6 : : ;; surfaces the winning outcome and swap treasury tokens in response. The contractcalls the
7 : : ;; the treasury with the form/to token contracts and works out the direction bearish/bullish
8 : : ;; of the signal together with the signal strength strong/medium/weak.
9 : :
10 : : (impl-trait .hedge-trait.hedge-trait)
11 : : (use-trait ft-velar-token 'SP2AKWJYC7BNY18W1XXKPGP0YVEK63QJG4793Z2D4.sip-010-trait-ft-standard.sip-010-trait)
12 : : (impl-trait 'SP3JP0N1ZXGASRJ0F7QAHWFPGTVK9T2XNXDB908Z.extension-trait.extension-trait)
13 : :
14 : : (define-constant err-unauthorised (err u32000))
15 : : (define-constant err-already-hedged (err u32001))
16 : : (define-constant err-already-executed (err u32002))
17 : : (define-constant err-hedge-not-found (err u32003))
18 : : (define-constant err-token-in-incorrect (err u32004))
19 : : (define-constant err-token-out-incorrect (err u32004))
20 : : (define-constant err-pair-not-found (err u32005))
21 : : (define-constant err-cooldown (err u32006))
22 : : (define-constant err-amount-zero (err u32007))
23 : : (define-constant err-slippage (err u32008))
24 : : (define-constant err-invalid-amount (err u32009))
25 : : (define-constant err-wrong-market-contract (err u32010))
26 : : (define-constant err-token-in-out-equal (err u32011))
27 : : (define-constant err-token-in-equal (err u32012))
28 : : (define-constant err-token-out-equal (err u32012))
29 : : (define-constant err-token-incorrect (err u32013))
30 : :
31 : : (define-constant SCALE u1000000)
32 : :
33 : : (define-data-var hedge-market-contract principal .bme024-0-market-predicting)
34 : : (define-data-var hedge-scalar-contract principal .bme024-0-market-scalar-pyth)
35 : : (define-data-var hedge-multipliers (list 6 uint) (list u750 u500 u250 u250 u500 u750))
36 : : (define-data-var max-hedge-bips uint u1000) ;; hard cap per trade = 10% of balance
37 : : (define-data-var max-hedge-abs uint u0) ;; optional absolute cap (0 = disabled)
38 : : (define-data-var min-trade uint u0) ;; optional minimum trade size (0 = disabled)
39 : : (define-data-var per-market-cooldown uint u144) ;; ~1 day (tune as needed)
40 : : (define-data-var per-trade-slippage uint u300) ;; 3% default for hedges (stricter than treasury 5%)
41 : :
42 : : (define-map last-hedge-height {market-id:uint} uint) ;; anti-replay
43 : :
44 : : (define-map swap-token-pairs (buff 32) {token-in: principal, token-out: principal, token0: principal, token1: principal})
45 : : (define-map hedges
46 : : uint
47 : : {executed: bool, feed-id: (buff 32)}
48 : : )
49 : :
50 : 17 : (define-public (is-dao-or-extension)
51 [ + ][ + + ]: 174 : (ok (asserts! (or (is-eq tx-sender .bigmarket-dao) (contract-call? .bigmarket-dao is-extension contract-caller)) err-unauthorised)))
52 : :
53 : 16 : (define-public (set-hedge-multipliers (multipliers (list 6 uint)))
54 : 18 : (begin
55 : 18 : (try! (is-dao-or-extension))
56 : 16 : (var-set hedge-multipliers multipliers)
57 : 16 : (print {event: "hedge-multipliers", multipliers: multipliers})
58 : 16 : (ok true)
59 : : )
60 : : )
61 : :
62 : 16 : (define-public (set-hedge-market-contract (market-contract principal))
63 : 20 : (begin
64 : 20 : (try! (is-dao-or-extension))
65 : 18 : (var-set hedge-market-contract market-contract)
66 : 18 : (print {event: "hedge-market-contract", market-contract: market-contract})
67 : 18 : (ok true)
68 : : )
69 : : )
70 : :
71 : 16 : (define-public (set-hedge-scalar-contract (market-contract principal))
72 : 20 : (begin
73 : 20 : (try! (is-dao-or-extension))
74 : 18 : (var-set hedge-scalar-contract market-contract)
75 : 18 : (print {event: "hedge-scalar-contract", market-contract: market-contract})
76 : 18 : (ok true)
77 : : )
78 : : )
79 : :
80 : 16 : (define-public (set-hedge-caps (bips uint) (abs uint))
81 : 18 : (begin (try! (is-dao-or-extension))
82 [ - ]: 16 : (asserts! (<= bips u5000) err-invalid-amount) ;; <=50%
83 : 16 : (var-set max-hedge-bips bips)
84 : 16 : (var-set max-hedge-abs abs)
85 : 16 : (ok true)))
86 : :
87 : 16 : (define-public (set-hedge-min-trade (min uint))
88 : 18 : (begin (try! (is-dao-or-extension)) (var-set min-trade min) (ok true)))
89 : :
90 : 16 : (define-public (set-hedge-cooldown (blocks uint))
91 : 18 : (begin (try! (is-dao-or-extension)) (var-set per-market-cooldown blocks) (ok true)))
92 : :
93 : 16 : (define-public (set-hedge-slippage (bips uint))
94 : 16 : (begin (try! (is-dao-or-extension))
95 [ - ][ + + ]: 16 : (asserts! (and (>= bips u1) (<= bips u3000)) err-invalid-amount)
96 : 16 : (var-set per-trade-slippage bips) (ok true)))
97 : :
98 : 17 : (define-public (set-swap-token-pair
99 : : (feed-id (buff 32))
100 : : (token-a principal)
101 : : (token-b principal)
102 : : (token-in principal)
103 : : (token-out principal))
104 : 39 : (begin
105 : 39 : (try! (is-dao-or-extension))
106 : : ;; Check token-in is one of token-a or token-b
107 [ + ][ + + ]: 37 : (asserts! (or (is-eq token-in token-a) (is-eq token-in token-b)) err-token-in-equal)
108 : : ;; Check token-out is the other one
109 [ + ][ + + ]: 36 : (asserts! (or (is-eq token-out token-a) (is-eq token-out token-b)) err-token-out-equal)
110 [ + ]: 35 : (asserts! (not (is-eq token-in token-out)) err-token-in-out-equal)
111 : :
112 : : ;; Lexicographic ordering
113 : 34 : (let (
114 : 34 : (buff-a (unwrap! (to-consensus-buff? token-a) err-token-incorrect))
115 : 34 : (buff-b (unwrap! (to-consensus-buff? token-b) err-token-incorrect))
116 [ + - ]: 34 : (token0 (if (< buff-a buff-b) token-a token-b))
117 [ + - ]: 34 : (token1 (if (< buff-a buff-b) token-b token-a))
118 : :
119 : : )
120 : 34 : (map-set swap-token-pairs
121 : 34 : feed-id
122 : : {
123 : 34 : token-in: token-in,
124 : 34 : token-out: token-out,
125 : 34 : token0: token0,
126 : 34 : token1: token1
127 : : })
128 : 34 : (print {event: "swap-token-pair", feed-id: feed-id, token-in: token-in, token-out: token-out, token0: token0, token1: token1})
129 : 34 : (ok true)
130 : : )
131 : : )
132 : : )
133 : :
134 : 1 : (define-read-only (get-swap-token-pair (feed-id (buff 32)))
135 : 1 : (map-get? swap-token-pairs feed-id)
136 : : )
137 : :
138 : 6 : (define-public (perform-swap-hedge
139 : : (market-id uint) (predicted-index uint) (feed-id (buff 32))
140 : : (token0 <ft-velar-token>) (token1 <ft-velar-token>)
141 : : (token-in <ft-velar-token>) (token-out <ft-velar-token>)
142 : : )
143 : 8 : (let (
144 : 8 : (pair (unwrap! (map-get? swap-token-pairs feed-id) err-pair-not-found))
145 : 4 : (is-bearish (< predicted-index u3))
146 [ + - ]: 4 : (expected-in (if is-bearish (get token-in pair) (get token-out pair)))
147 [ + - ]: 4 : (expected-out (if is-bearish (get token-out pair) (get token-in pair)))
148 : 4 : (lh (default-to u0 (map-get? last-hedge-height {market-id: market-id})))
149 : 4 : (cool (var-get per-market-cooldown))
150 : : )
151 : : ;; auth + source contract check
152 : 4 : (try! (is-dao-or-extension))
153 [ + ]: 2 : (asserts! (is-eq contract-caller (var-get hedge-scalar-contract)) err-wrong-market-contract)
154 : :
155 : : ;; one-shot safety: refuse if this market was already hedged here
156 [ - ]: 1 : (asserts! (is-none (map-get? hedges market-id)) err-already-executed)
157 : :
158 : : ;; cooldown even if another component tries to hedge repeatedly
159 : 1 : (if (> lh u0)
160 [ - ][ - ]: 0 : (asserts! (> stacks-block-height (+ lh cool)) err-cooldown)
161 [ + ]: 1 : true
162 : : )
163 : :
164 : : ;; pair validation: enforce tokens match configured direction
165 [ - ]: 1 : (asserts! (is-eq (contract-of token-in) expected-in) err-token-in-incorrect)
166 [ - ]: 1 : (asserts! (is-eq (contract-of token-out) expected-out) err-token-out-incorrect)
167 : :
168 : : ;; compute bounded amount
169 : 1 : (let (
170 : 1 : (balance (unwrap! (contract-call? token-in get-balance .bme006-0-treasury) err-unauthorised))
171 : 1 : (bips-mult (unwrap! (element-at? (var-get hedge-multipliers) predicted-index) err-token-incorrect)) ;; e.g., 750 = 7.5%
172 : 1 : (cap-bips (var-get max-hedge-bips))
173 [ - + ]: 1 : (bips (if (> bips-mult cap-bips) cap-bips bips-mult))
174 : 1 : (raw-scaled (/ (* (* balance bips) SCALE) u10000))
175 : 1 : (raw (/ raw-scaled SCALE))
176 : :
177 : 1 : (abs-cap (var-get max-hedge-abs))
178 [ - + ][ + + ]: 1 : (amt (if (and (> abs-cap u0) (> raw abs-cap)) abs-cap raw))
179 : 1 : (min-size (var-get min-trade))
180 : : )
181 [ + ]: 1 : (asserts! (> amt u0) err-amount-zero)
182 [ - - ][ - ]: 0 : (if (> min-size u0) (asserts! (>= amt min-size) err-amount-zero) true)
183 : :
184 : : ;; do the swap with stricter slippage than treasury default
185 : 0 : (let ((slip (var-get per-trade-slippage)))
186 [ - ][ - - ]: 0 : (asserts! (and (>= slip u1) (<= slip u3000)) err-slippage)
187 : 0 : (try! (contract-call? .bme006-0-treasury swap-tokens-with-slippage
188 : 0 : token0 token1 token-in token-out amt slip))
189 : : )
190 : :
191 : : ;; record hedge + height
192 : 0 : (map-set hedges market-id {executed: true, feed-id: feed-id})
193 : 0 : (map-set last-hedge-height {market-id: market-id} stacks-block-height)
194 : 0 : (print {event:"perform-swap-hedge", market-id: market-id, predicted: predicted-index, feed-id: feed-id, amount: amt})
195 : 0 : (ok true)
196 : : )
197 : : )
198 : : )
199 : :
200 : : ;; can be implemented in later contract
201 : 3 : (define-public (perform-custom-hedge (market-id uint) (predicted-index uint))
202 : 3 : (begin
203 : : ;; caller must be both an ACTIVE extension and sepecifically the scalar prediction market
204 : 3 : (try! (is-dao-or-extension))
205 [ + ]: 2 : (asserts! (is-eq contract-caller (var-get hedge-market-contract)) err-wrong-market-contract)
206 : 1 : (print {event: "perform-custom-hedge", market-id: market-id, predicted: predicted-index})
207 : 1 : (ok true)
208 : : )
209 : : )
210 : :
211 : 1 : (define-private (compute-swap-amount (token <ft-velar-token>) (index uint))
212 : 1 : (let (
213 : 1 : (balance (unwrap! (contract-call? token get-balance .bme006-0-treasury) err-unauthorised))
214 : 1 : (bips (unwrap! (element-at? (var-get hedge-multipliers) index) err-token-incorrect))
215 : 1 : (amt-scaled (/ (* (* balance bips) SCALE) u10000))
216 : 1 : (amt (/ amt-scaled SCALE))
217 : : )
218 : 1 : (ok amt)
219 : : )
220 : : )
221 : :
222 : : ;; --- Extension callback
223 : :
224 : 1 : (define-public (callback (sender principal) (memo (buff 34)))
225 : 1 : (ok true)
226 : : )
|