Funds Sdk
Software Development Kit for interacting with Symmetry Funds Program
Init
Loads FundsProgram, TekenInfos supported in funds, and Prism Aggregator
import { FundsSDK } from "@symmetry-hq/funds-sdk";
let fundsSDK: FundsSDK = await FundsSDK.init(
// rpc connection
connection: Connection,
// wallet (optional | can be provided later, using funds.setWallet
wallet: Wallet,
);
// load list of supported tokens
let tokens: TokenInfoData[] = fundsSdk.getTokenInfoData();
// get tokenId(tokenId in symmetry supported tokens) from mint
let tokenId = fundsSdk.tokenIdFromMint("So11111111111111111111111111111111111111112");
// set wallet
fundsSdk.setWallet(wallet: Wallet);
Create/Edit/Close Fund
Create Fund with specific settings and rules
let fund: Fund = await fundsSdk.createFund(params: CreateFundParams);
let fund: Fund = await fundsSdk.createFund({
name: string,
symbol: string,
description: string,
hostPlatform: PublicKey,
hostPlatformFee: number,
manager: PublicKey,
managerFee: number,
activelyManaged: boolean,
assetPool: number[],
refilterInterval: number,
reweightInterval: number,
rebalanceInterval: number,
rebalanceThreshold: number,
rebalanceSlippage: number,
lpOffsetThreshold: number,
rules: {
filterBy: FilterType,
filterDays: FilterTime,
sortBy: SortBy,
totalWeight: number,
fixedAsset: number,
numAssets: number,
weightBy: WeightType,
weightDays: WeightTime,
weightExpo: number,
excludeAssets: number[]
}[],
});
Edit Fund (Only for actively managed funds)
Fund name and symbol can not be changed.
await fundsSdk
.editFund(fund: Fund, params: CreateFundParams);
Close Fund (Fund TVL == 0 & no active BuyStates)
await fundsSdk.closeFund(fund: Fund);
Load Fund(s)
Load specific fund from pubkey
let fund: Fund = await fundsSDK.loadFromPubkey(pubkey: PublicKey);
Load funds array based on filters (manager/host platform pubkey)
let funds: Fund[] = await fundsSDK.findFunds(filters: FilterOption[]);
let funds: Fund[] = await fundsSDK
.findFunds([{
filterType: "manager"|"host",
filterPubkey: PublicKey
}]);
Buy Fund
Step 1: Contribute USDC and create BuyState
let buyState: BuyState = await fundsSdk
.buyFund(fund: Fund, amountUsdc: number);
Step 2: Rebalance BuyState: Buy underlying assets in a fund. (Optional)
let txs: TransactionSignature[] = await fundsSdk
.rebalanceBuyState(buyState: BuyState);
Step 3: Mint Fund Tokens & Close Buy State
let tx: TransactionSignature = await fundsSdk
.mintFund(buyState: BuyState);
Helper: Find active BuyStates for user that have been created after Step 1
let buyStates: BuyState[] = await fundsSdk
.findActiveBuyStates(user: PublicKey);
Sell Fund
Step 1: Burn Fund Tokens and create SellState
(Create new Fund where user has authority to claim all tokens)
let sellState: Fund = await fundsSdk
.sellFund(fund: Fund, amount: number, rebalance: boolean);
Step 2: Rebalance SellState(Fund): Rebalance underlying assets to USDC
(Optional, only when rebalance == true)
let txs: TransactionSignature[] = await fundsSdk
.rebalanceFund(sellState: Fund);
Step 3: Claim underlying toknes from SellState
let txs: TransactionSignature[] = await fundsSdk
.claimTokens(sellState: Fund);
Helper: Find active SellStates for user that have been created after Step 1
let sellStates: Fund[] = await fundsSdk
.findActiveSellStates(user: PublicKey);
Automation
Automation on all funds (Refilter/Reweight/Rebalance), as well as monitoring
active Buy/Sell states and rebalancing them is done by Symmetry.
Platforms and Developers can run their own automation for their funds by using example bellow
Swap using Symmetry Funds Liquidity
Users can swap and check available swap liquidity for a specific fund
Find out more at @symmetry-hq/liquidity-sdk
Full Example
Example shows how to create/buy/sell/rebalance/refilter/reweight a fund
Creating fund with following settings:
-
Manager fee : 0.1% - Users pay 0.1% to fund manager on fund buy
-
Is actively managed - Manager can edit fund settings and rules
-
Asset Pool consists of 4 tokens - [USDC, SOL, BTC, ETH]
-
Fund refilters underlying assets every week
-
Fund reweights underlying assets every day
-
Fund rebalances every 2 hours
-
Fund rebalances when target and current weights differ by at least 5%
-
Maximum allowed slippage on rebalance is 3% (compared to pyth price)
-
Fund provides swap liquidity for a token if current weight is within
5% (rebalance) 50%(lpOffset) = 2.5% of target weight
-
Fund earns 0.05% of swap amount on liquidity providing
-
Fund has 1 rule:
-
Select TOP 3 assets by 1 week average MarketCap,
-
Weight chosen assets by square root of 1 day performance
import {
FundsSDK,
Fund,
BuyState,
FilterType,
FilterTime,
SortBy,
WeightType,
WeightTime
} from "@symmetry-hq/funds-sdk";
/* init funds sdk */
let fundsSDK: FundsSDK = await FundsSDK.init(
connection,
wallet
);
/* Create a fund (CreateFund + SetRules + Refilter&Reweight) */
let fund: Fund = await fundsSdk.createFund({
name: "Solana Bitcoin Etherium",
symbol: "SOLBTCETH",
description: "Actively Managed Fund with SOL BTC ETH" +
"weighted by square root of 1 day performance",
hostPlatform: wallet.publicKey,
hostPlatformFee: 0,
manager: wallet.publicKey,
managerFee: 10, // fee in bps: 10 = 0.1%
activelyManaged: true, // actively managed
assetPool: [0, 1, 2, 3], // USDC, SOL, BTC, ETH
refilterInterval: 7 * 24 * 3600, // 1 week
reweightInterval: 24 * 3600, // 1 day
rebalanceInterval: 2 * 3600, // 2 hours
rebalanceThreshold: 500, // 5%
rebalanceSlippage: 300, // 3%
lpOffsetThreshold: 5000, // 50% of rebalance threshold
rules: [{
filterBy: FilterType.MarketCap, // filter assets by marketcap
filterDays: FilterTime.Week, // 1 week average of marketcap
sortBy: SortBy.DescendingOrder, // select top coins by marketcap
totalWeight: 100,
fixedAsset: 0,
numAssets: 3, // select 3 assets
weightBy: WeightType.Performance, // weight by performance
weightDays: WeightTime.Day, // 1 day performance
weightExpo: 0.5, // square root of performance
excludeAssets: [0], // Exclude USDC
}],
});
/* 1. buy a fund with 50 USDC
2. rebalance buyState (buy underlying assets)
3. mint fund tokens */
let buyState1: BuyState = await fundsSdk.buyFund(fund, 50);
let txsRebalanceBuyState1 = await fundsSdk.rebalanceBuyState(buyState1);
let txMintFund1 = await fundsSdk.mintFund(buyState1);
/* 1. buy a fund with 50 USDC without rebalancing
unspent amount will have *penalty* and counted as if rebalance
happend with maximum slippage (rebalanceSlippage = 3%)
2. mint fund tokens */
let buyState2: BuyState = await fundsSdk.buyFund(fund, 50);
let txMintFund2 = await fundsSdk.mintFund(buyState2);
/* 1. sell a fund
2. rebalance fund (to USDC)
3. claim tokens (USDC + tokens for which rebalance failed) */
let sellState1: Fund = await fundsSdk.sellFund(fund, 0.4, true);
let txsRebalanceSellState = await fundsSdk.rebalanceFund(sellState1);
let txsClaimTokens1 = await fundsSdk.claimTokens(sellState);
/* 1. sell a fund without rebalancing
2. claim tokens (all tokens in a fund) */
let sellState2: Fund = await fundsSdk.sellFund(fund, 0.4, false);
let txsClaimTokens2 = await fundsSdk.claimTokens(sellState2);
/* 1. Refilter Fund (RefilterInterval seconds since last refilter)
2. Reweight Fund (ReweightInterval seconds since last reweight)
3. Rebalance Fund (RebalanceInterval seconds since last rebalanace) */
let txRefilter: TransactionSignature = await fundsSdk.refilterFund(fund);
let txReweight: TransactionSignature = await fundsSdk.reweightFund(fund);
let txsRebalance = await fundsSdk.rebalanceFund(fund);
/* In some cases, when fund decides to remove token after refiltering,
and rebalance function sells it to USDC, fund might end up with
small amount of that token, for which it is unable to rebalance and
sell the full amount. Although it's possible to remove the "dust"
if user swaps equivalent amount of usdc (based on pyth price) for
the token, which is stuck in a fund. Meaning that fund will receive
USDC and user will receive token. RemoveDust will be triggered for
tokens with equivalent USD value < 0.005.
Amount of usdc to be swapped is computed in a following way:
max(tokenAmount * tokenPrice * 1.1 (for slippage purposes), 0.000001) */
let txsRemoveDust = await fund.removeDust();