Fix risk
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
use hypersdk::{dec, hypercore::Side};
|
||||
use hypersdk::{
|
||||
dec,
|
||||
hypercore::{Cloid, Side},
|
||||
};
|
||||
use rust_decimal::Decimal;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
@@ -44,7 +47,17 @@ pub struct Signal {
|
||||
pub confidence: u8,
|
||||
pub entry_price: Decimal,
|
||||
pub take_profit: Decimal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct EngineOrder {
|
||||
pub risk_equity: Decimal,
|
||||
pub size: Decimal,
|
||||
pub stop_loss: Decimal,
|
||||
|
||||
pub entry: Cloid,
|
||||
pub take_profit: Cloid,
|
||||
pub stop_loss_id: Cloid,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
general::{EngineStatus, EventLog, Signal},
|
||||
general::{EngineOrder, EngineStatus, EventLog, Signal},
|
||||
strategy::StrategyManifest,
|
||||
};
|
||||
use hypersdk::Decimal;
|
||||
|
||||
pub type SignalStatus = Result<(Signal, Option<Decimal>), (Signal, Option<Decimal>)>;
|
||||
pub type SignalStatus = Result<(Signal, Option<EngineOrder>), (Signal, Option<EngineOrder>)>;
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum TerminalServerMessage {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
use hypersdk::{
|
||||
Decimal,
|
||||
hypercore::{
|
||||
self, BatchOrder, NonceHandler, OrderRequest, OrderResponseStatus, OrderTypePlacement,
|
||||
Side, TimeInForce,
|
||||
},
|
||||
use hypersdk::hypercore::{
|
||||
self, BatchOrder, NonceHandler, OrderRequest, OrderResponseStatus, OrderTypePlacement, Side,
|
||||
TimeInForce,
|
||||
};
|
||||
use pulse_sdk::prelude::*;
|
||||
|
||||
@@ -12,7 +9,7 @@ use crate::engine::Engine;
|
||||
impl Engine {
|
||||
pub async fn execute_signal(
|
||||
&self,
|
||||
size: &mut Option<Decimal>,
|
||||
size: &mut Option<EngineOrder>,
|
||||
signal: &Signal,
|
||||
) -> anyhow::Result<()> {
|
||||
let client = hypercore::mainnet();
|
||||
@@ -38,7 +35,7 @@ impl Engine {
|
||||
|
||||
let engine_order = self.risk_engine.create_order(signal).await?;
|
||||
|
||||
*size = Some(engine_order.size);
|
||||
*size = Some(engine_order);
|
||||
|
||||
let order = BatchOrder {
|
||||
orders: vec![
|
||||
@@ -55,7 +52,7 @@ impl Engine {
|
||||
},
|
||||
OrderRequest {
|
||||
asset: asset_id,
|
||||
is_buy: matches!(signal.side, Side::Bid),
|
||||
is_buy: !matches!(signal.side, Side::Bid),
|
||||
limit_px: signal.entry_price,
|
||||
sz: engine_order.size,
|
||||
reduce_only: true,
|
||||
@@ -68,19 +65,19 @@ impl Engine {
|
||||
},
|
||||
OrderRequest {
|
||||
asset: asset_id,
|
||||
is_buy: matches!(signal.side, Side::Bid),
|
||||
is_buy: !matches!(signal.side, Side::Bid),
|
||||
limit_px: signal.entry_price,
|
||||
sz: engine_order.size,
|
||||
reduce_only: true,
|
||||
order_type: OrderTypePlacement::Trigger {
|
||||
is_market: true,
|
||||
trigger_px: signal.stop_loss,
|
||||
trigger_px: engine_order.stop_loss,
|
||||
tpsl: hypercore::TpSl::Sl,
|
||||
},
|
||||
cloid: engine_order.stop_loss,
|
||||
cloid: engine_order.stop_loss_id,
|
||||
},
|
||||
],
|
||||
grouping: hypercore::OrderGrouping::Na,
|
||||
grouping: hypercore::OrderGrouping::NormalTpsl,
|
||||
builder: None,
|
||||
};
|
||||
|
||||
@@ -95,9 +92,10 @@ impl Engine {
|
||||
return Err(anyhow::anyhow!(
|
||||
"order rejected by HyperLiquid: {}",
|
||||
o.into_iter()
|
||||
.filter_map(|res| {
|
||||
.enumerate()
|
||||
.filter_map(|(i, res)| {
|
||||
if let OrderResponseStatus::Error(e) = res {
|
||||
Some(e)
|
||||
Some(format!("{i}={e}"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
||||
+20
-14
@@ -4,7 +4,7 @@ use futures::StreamExt;
|
||||
use hypersdk::{
|
||||
Decimal,
|
||||
hypercore::{
|
||||
self, Cloid,
|
||||
self, Cloid, Side,
|
||||
ws::{ConnectionHandle, ConnectionStream, Event},
|
||||
},
|
||||
};
|
||||
@@ -13,16 +13,6 @@ use tokio::sync::Mutex;
|
||||
|
||||
use crate::{engine::Engine, store::config::RiskConfig};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EngineOrder {
|
||||
pub risk_equity: Decimal,
|
||||
pub size: Decimal,
|
||||
|
||||
pub entry: Cloid,
|
||||
pub take_profit: Cloid,
|
||||
pub stop_loss: Cloid,
|
||||
}
|
||||
|
||||
pub struct RiskState {
|
||||
pub starting_equity: Decimal,
|
||||
pub pnl: Decimal,
|
||||
@@ -85,7 +75,7 @@ impl RiskEngine {
|
||||
let mut rm = Vec::new();
|
||||
|
||||
for (i, order_ids) in orders.iter().enumerate() {
|
||||
if order_ids.stop_loss == cloid {
|
||||
if order_ids.stop_loss_id == cloid {
|
||||
if order.status.is_filled() {
|
||||
rm.push(i);
|
||||
}
|
||||
@@ -154,12 +144,28 @@ impl RiskEngine {
|
||||
.risk_per_trade
|
||||
.get(state.starting_equity);
|
||||
|
||||
let size = self
|
||||
.get_risk_config()
|
||||
.await?
|
||||
.size_per_trade
|
||||
.get(state.starting_equity);
|
||||
|
||||
let sl_distance = risk / (size / signal.entry_price);
|
||||
|
||||
Ok(EngineOrder {
|
||||
entry: Cloid::random(),
|
||||
take_profit: Cloid::random(),
|
||||
stop_loss: Cloid::random(),
|
||||
stop_loss_id: Cloid::random(),
|
||||
|
||||
size: size.round_dp(3),
|
||||
risk_equity: risk,
|
||||
size: (risk / (signal.entry_price - signal.stop_loss).abs()).round_dp(3),
|
||||
|
||||
stop_loss: if matches!(signal.side, Side::Ask) {
|
||||
signal.entry_price + sl_distance
|
||||
} else {
|
||||
signal.entry_price - sl_distance
|
||||
}
|
||||
.round_dp(3),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
|
||||
pub struct RiskConfig {
|
||||
pub size_per_trade: Allocation,
|
||||
pub risk_per_trade: Allocation,
|
||||
pub max_open_positions: u32,
|
||||
pub max_daily_loss: Allocation,
|
||||
@@ -28,6 +29,7 @@ pub struct ConfigManager {
|
||||
impl Default for RiskConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
size_per_trade: Allocation::Percent(20.into()),
|
||||
risk_per_trade: Allocation::Percent(10.into()),
|
||||
max_daily_loss: Allocation::Percent(20.into()),
|
||||
max_open_positions: 1,
|
||||
|
||||
@@ -55,8 +55,8 @@ impl Formatted for EventLog {
|
||||
impl Formatted for SignalStatus {
|
||||
fn get_formatted(&self) -> Vec<String> {
|
||||
match self {
|
||||
Ok((signal, size)) | Err((signal, size)) => {
|
||||
vec![
|
||||
Ok((signal, order)) | Err((signal, order)) => {
|
||||
let mut base = vec![
|
||||
if matches!(self, Ok(_)) {
|
||||
format!("\x1b[34mOK\x1b[0m")
|
||||
} else {
|
||||
@@ -67,12 +67,16 @@ impl Formatted for SignalStatus {
|
||||
} else {
|
||||
format!("\x1b[31mSELL\x1b[0m")
|
||||
},
|
||||
size.map(|v| format_f64(v.as_f64())).unwrap_or_default(),
|
||||
format_symbol(&signal.symbol),
|
||||
format_usd(signal.entry_price.as_f64()),
|
||||
format_usd(signal.take_profit.as_f64()),
|
||||
format_usd_reverse(signal.stop_loss.as_f64()),
|
||||
]
|
||||
];
|
||||
|
||||
if let Some(order) = order {
|
||||
base.extend(vec![format_usd_reverse(order.stop_loss.as_f64())]);
|
||||
}
|
||||
|
||||
base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user