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