Skip to content

Commit

Permalink
Merge pull request #279 from primitivefinance/feat/multi-strategy-sims
Browse files Browse the repository at this point in the history
Feat/multi strategy sims
  • Loading branch information
kinrezC authored Jan 22, 2024
2 parents 2d80cb8 + 1b97552 commit 7071751
Show file tree
Hide file tree
Showing 58 changed files with 19,243 additions and 4,196 deletions.
1 change: 1 addition & 0 deletions .rustfmt.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ use_field_init_shorthand = true
wrap_comments = true
normalize_comments = true
comment_width = 80
edition = "2021"

ignore = ["crates/bindings/*"]
30 changes: 16 additions & 14 deletions configs/v3/static.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ decimals = 18
# Price process parameters
[agent.price_changer.PriceChanger]
# True is we want to use historical daily average price data
backtest = true
backtest = false
# The seed to use (optional)
seed = 5
# The number of steps in the process
num_steps = 1000
# num_steps = 1000
num_steps = 1825
# The number of distinct paths to use
num_paths = 1
# The initial price of the asset
Expand All @@ -32,21 +33,21 @@ t_0.fixed = 0.0
t_n.fixed = 1.0
## Parameters for the type of process to use.
# # The drift for GBM
# process.GBM.drift.fixed = 0.0
process.GBM.drift.fixed = 0.1
# # The volatility for GBM
# process.GBM.volatility.fixed = 0.1
process.OU.mean.fixed = 1.0
process.GBM.volatility.fixed = 0.35
# process.OU.mean.fixed = 1.0
# The volatility for GBM
process.OU.volatility.fixed = 0.1
#process.OU.volatility.fixed = 0.1
# The mean reversion rate for OU
process.OU.theta.fixed = 10
#process.OU.theta.fixed = 10

# Rmm portfolio manager params
[agent.portfolio_manager.VolatilityTargetingSubmitter]
sigma.fixed = 1.0
tau.fixed = 100000.0
tau.fixed = 1.0
strike_price.fixed = 1.0
fee.fixed = 5
fee.fixed = 30
## Volatility targeting specialty settings
# The frequency which weights are updated
# update_frequency = 450 # 1 update per 30 timestep day
Expand All @@ -56,17 +57,18 @@ specialty.VolatilityTargeting.update_frequency.fixed = 75 # 1 update per 5 times
# The target volatility
specialty.VolatilityTargeting.target_volatility.fixed = 0.15
# The sensitivity factor of the vol differential
specialty.VolatilityTargeting.sensitivity.fixed = 0.0
specialty.VolatilityTargeting.sensitivity.fixed = 0.0115
# Max strike update
specialty.VolatilityTargeting.max_strike_change.fixed = 0.0
specialty.VolatilityTargeting.max_strike_change.fixed = 0.06

# Liquidity provider settings
[agent.lp.LiquidityProvider]
# The amount of `token_x` to provide in ether
x_liquidity.fixed = 10
# The initial price of the pair
initial_price.fixed = 2345.2607
initial_price.fixed = 1.0
# inital parameters
sigma.fixed = 2.0
sigma.fixed = 1.0
tau.fixed = 1.0
strike_price.fixed = 2300.0
strike_price.fixed = 1.0
wx.fixed = 0.5
2 changes: 1 addition & 1 deletion configs/v3/sweep.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ process.GBM.volatility.fixed = 0.35
# Rmm portfolio manager params
[agent.portfolio_manager.VolatilityTargetingSubmitter]
sigma.fixed = 1.0
tau.fixed = 100000.0
tau.fixed = 1.0
strike_price.fixed = 1.0
fee.fixed = 30
## Volatility targeting specialty settings
Expand Down
4 changes: 1 addition & 3 deletions crates/app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -380,9 +380,7 @@ impl App {
/// synchronization.
fn sync_model(&mut self) -> Command<Message> {
let model = self.model.clone();
// todo: fix this clunky provider
// let provider = Arc::new(client.provider().clone());
let provider = self.client.sandbox().clone();
let provider = self.client.get_client();
Command::perform(
async move {
let mut model = model;
Expand Down
112 changes: 96 additions & 16 deletions crates/app/src/controller/portfolio/monolithic/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ mod view;

use alloy_primitives::utils::format_ether;
use arbiter_bindings::bindings::liquid_exchange::LiquidExchange;
use bindings::log_normal_solver::{LogNormalSolver, PublicParams as LogNormalParameters};
use cfmm_math::trading_functions::rmm::{
compute_value_function, compute_x_given_l_rust, compute_y_given_x_rust,
};
use clients::protocol::{LogNormalF64, PoolInitParamsF64, PoolParams};
use datatypes::portfolio::coin::Coin;
use iced::{futures::TryFutureExt, subscription, Padding};
use sim::{from_ethers_u256, to_ethers_address, to_ethers_u256};
Expand All @@ -20,7 +25,6 @@ use self::{
use super::*;
use crate::{
components::system::{ExcaliburChart, ExcaliburContainer},
middleware::Protocol,
model::portfolio::{AlloyAddress, ALLOY_WAD},
view::portfolio_view::PortfolioPresenter,
};
Expand Down Expand Up @@ -149,11 +153,23 @@ impl Monolithic {

pub fn handle_submit_allocate(&mut self) -> anyhow::Result<Command<Message>> {
if let Some(client) = self.client.clone() {
if let Some(signer) = client.signer() {
if let (Some(signer), Some(_)) = (client.signer.as_ref(), client.dfmm_client.as_ref()) {
let submitter = signer.address();

let deposit_amount = self.create.amount.clone();
let deposit_amount = match deposit_amount {
let asset_token = self.model.get_current().map(|x| x.raw_asset_token).unwrap();
let asset_token = match asset_token {
Some(x) => x,
None => return Err(anyhow::anyhow!("No asset token")),
};

let quote_token = self.model.get_current().map(|x| x.raw_quote_token).unwrap();
let quote_token = match quote_token {
Some(x) => x,
None => return Err(anyhow::anyhow!("No quote token")),
};

let deposit_amount_dollars = self.create.amount.clone();
let deposit_amount_dollars = match deposit_amount_dollars {
Some(x) => x.parse::<f64>().unwrap(),
None => return Err(anyhow::anyhow!("No deposit amount")),
};
Expand Down Expand Up @@ -182,20 +198,50 @@ impl Monolithic {
};
let parameters = parameters.to_parameters(asset_price);

let (amount_x, amount_y, _total_liquidity) = get_deposits_given_price(
asset_price,
deposit_amount_dollars,
parameters.strike_price_wad,
parameters.sigma_percent_wad,
parameters.time_remaining_years_wad,
);

let payload_params = PoolInitParamsF64::LogNormal(LogNormalF64 {
sigma: parameters.sigma_percent_wad,
strike: parameters.strike_price_wad,
tau: parameters.time_remaining_years_wad,
swap_fee: 0.003,
});

let init_price_wad =
alloy_primitives::utils::parse_ether(&format!("{}", asset_price))?;
let init_price_wad = to_ethers_u256(init_price_wad);

let init_reserve_x_wad = to_ethers_u256(alloy_primitives::utils::parse_ether(
&format!("{}", amount_x),
)?);

let client = client.clone();
return Ok(Command::perform(
async move {
client
.create_position(
submitter,
deposit_amount,
asset_price,
parameters.strike_price_wad,
parameters.sigma_percent_wad,
parameters.time_remaining_years_wad,
let dfmm = client
.dfmm_client
.as_ref()
.unwrap_or_else(|| panic!("No DFMM client in ExcaliburMiddleware"));

let payload = dfmm
.get_init_payload(
to_ethers_address(asset_token),
to_ethers_address(quote_token),
init_reserve_x_wad,
init_price_wad,
payload_params,
)
.map_err(Arc::new)
.await
.await?;

// todo: handle mutable update to the pools array in the protocol client
// separately.
dfmm.initialize_pool(payload).map_err(Arc::new).await
},
Message::AllocateResult,
));
Expand Down Expand Up @@ -420,7 +466,7 @@ impl State for Monolithic {

fn subscription(&self) -> Subscription<Self::AppMessage> {
if let Some(client) = self.client.clone() {
let provider = client.client().unwrap().clone();
let provider = client.get_client();
let mut subscriptions: Vec<Subscription<Message>> = vec![];

// Fetches the most recent block and updates the model.
Expand Down Expand Up @@ -510,7 +556,7 @@ fn price_process_update_after_step(
}
}

let client = client.client().cloned().unwrap();
let client = client.get_client();

Command::perform(
async move {
Expand Down Expand Up @@ -549,3 +595,37 @@ fn price_process_update_after_step(
|_| Message::Empty,
)
}

/// L = Deposit $ / V(c)
/// x = X(L)
/// y = Y(x, L)
pub fn get_deposits_given_price(
price: f64,
amount_dollars: f64,
strike_price_wad: f64,
sigma_percent_wad: f64,
tau_years_wad: f64,
) -> (f64, f64, f64) {
let value_per =
compute_value_function(price, strike_price_wad, sigma_percent_wad, tau_years_wad);

let total_liquidity = amount_dollars / value_per;

let amount_x = compute_x_given_l_rust(
total_liquidity,
price,
strike_price_wad,
sigma_percent_wad,
tau_years_wad,
);

let amount_y = compute_y_given_x_rust(
amount_x,
total_liquidity,
strike_price_wad,
sigma_percent_wad,
tau_years_wad,
);

(amount_x, amount_y, total_liquidity)
}
79 changes: 20 additions & 59 deletions crates/app/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,7 @@ use iced::{
use iced_aw::graphics::icons::ICON_FONT_BYTES;
use sim::from_ethers_address;

use super::{
middleware::*,
model::{contacts, user::UserProfile},
*,
};
use super::{middleware::*, model::contacts, *};
use crate::{
app::AnvilSave,
components::{
Expand Down Expand Up @@ -53,30 +49,6 @@ pub struct Loader {
pub logo: PhiLogo,
}

/// This function attempts to load a user profile. If it fails, it creates a new
/// default profile. It then logs the loaded profile's name and file path.
#[tracing::instrument(level = "debug")]
pub fn load_profile() -> anyhow::Result<UserProfile> {
let profile = UserProfile::load(None);
let profile = match profile {
Ok(profile) => profile,
Err(e) => {
tracing::warn!("Failed to load profile: {:?}", e);
tracing::info!("Creating a new default profile.");

UserProfile::create_new(None)?
}
};

tracing::debug!(
"Loaded profile {:?} at path {:?}",
profile.name,
profile.file_path()
);

Ok(profile)
}

/// This function attempts to load user data into a model. If it fails, it
/// creates a new default model. It then logs the loaded model's user name and
/// file path.
Expand All @@ -103,26 +75,6 @@ pub fn load_user_data() -> anyhow::Result<Model> {
Ok(model)
}

/// This function loads a development client. It first logs the loading process,
/// then creates a signer with the chain id of the client. It then gets the
/// address of the signer and clones the client. It deploys the development
/// client and returns it.
#[tracing::instrument(skip(client), level = "trace")]
pub async fn load_dev_client(
client: Arc<ExcaliburMiddleware<Ws, LocalWallet>>,
) -> anyhow::Result<DevClient<NetworkClient<Ws, LocalWallet>>> {
tracing::debug!("Loading dev client");
let signer = client
.signer()
.unwrap()
.clone()
.with_chain_id(client.clone().anvil.as_ref().unwrap().chain_id());
let sender = signer.address();
let client = client.client().unwrap().clone();
let dev_client = DevClient::deploy(client, sender).await?;
Ok(dev_client)
}

/// Contracts that we start up the client with
pub const CONTRACT_NAMES: [&str; 5] = ["protocol", "strategy", "token_x", "token_y", "lex"];

Expand All @@ -134,18 +86,24 @@ pub async fn load_app(flags: super::Flags) -> LoadResult {
// Load the user's save or create a new one.
let mut model = load_user_data()?;

let mut exc_client = ExcaliburMiddleware::setup(true).await?;
// Create a new middleware client to make calls to the network.
let mut exc_client = ExcaliburMiddleware::new(None, None, None).await?;

// Start and connect to an anvil instance.
let anvil = start_anvil(None)?;
exc_client.connect_anvil(anvil).await?;

let chain_id = if let Some(anvil) = &exc_client.anvil {
anvil.chain_id()
} else {
31337
};

let client = exc_client.get_client();

// todo: try the connection to the sandbox next
// Connect the model to the desired network.
model
.connect_to_network(exc_client.client().cloned().unwrap())
.await?;
model.connect_to_network(client.clone()).await?;

// If profile has an anvil snapshot, load it.
let loaded_snapshot = if let Some(AnvilSave {
Expand All @@ -158,9 +116,8 @@ pub async fn load_app(flags: super::Flags) -> LoadResult {
&snapshot[..10.min(snapshot.len())],
);

let client = exc_client.client().unwrap().clone();

let success = client
.clone()
.provider()
.request::<[String; 1], bool>("anvil_loadState", [snapshot.to_string()])
.await
Expand All @@ -171,6 +128,7 @@ pub async fn load_app(flags: super::Flags) -> LoadResult {
if success {
tracing::info!("Syncing Anvil to block: {}", block_number);
client
.clone()
.provider()
.request::<[u64; 1], ()>("anvil_mine", [*block_number])
.await
Expand Down Expand Up @@ -206,14 +164,17 @@ pub async fn load_app(flags: super::Flags) -> LoadResult {

// If we are loading a fresh instance, deploy the contracts.
if !loaded_snapshot {
let signer = exc_client.signer().unwrap().clone().with_chain_id(chain_id);
let signer = exc_client.signer.clone().unwrap().with_chain_id(chain_id);
let sender = signer.address();
let client = exc_client.client().unwrap().clone();
let client = client.with_signer(signer);

exc_client.connect_signer(signer).await?;
let client = exc_client.get_client();

let dev_client = DevClient::deploy(client.into(), sender).await?;
exc_client.connect_dfmm(dev_client.protocol.clone()).await?;

let protocol = dev_client.protocol.protocol.address();
let strategy = dev_client.protocol.get_strategy().await?.address();
let strategy = dev_client.protocol.ln_strategy.address();
let token_x = dev_client.token_x.address();
let token_y = dev_client.token_y.address();
let solver = dev_client.solver.address();
Expand Down
Loading

0 comments on commit 7071751

Please sign in to comment.