90 lines
3.3 KiB
C#
90 lines
3.3 KiB
C#
|
|
namespace Simulator.Services
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 시뮬레이션 인메모리 상태.
|
||
|
|
/// 잔고, 포지션, 거래 내역을 관리합니다.
|
||
|
|
/// </summary>
|
||
|
|
public class SimState
|
||
|
|
{
|
||
|
|
private int _nextOrderId = 1000;
|
||
|
|
|
||
|
|
public double Balance { get; set; }
|
||
|
|
public SimPosition? LongPosition { get; set; }
|
||
|
|
public SimPosition? ShortPosition { get; set; }
|
||
|
|
|
||
|
|
public List<SimTrade> TradeHistory { get; } = new();
|
||
|
|
|
||
|
|
public SimState(double initialBalance)
|
||
|
|
{
|
||
|
|
Balance = initialBalance;
|
||
|
|
}
|
||
|
|
|
||
|
|
public string NextOrderId() => $"SIM{_nextOrderId++:D6}";
|
||
|
|
|
||
|
|
/// <summary>모든 포지션을 Bitget all-position 응답 형식 목록으로 반환합니다.</summary>
|
||
|
|
public List<object> GetAllPositions(string symbol, string productType, double currentPrice)
|
||
|
|
{
|
||
|
|
var result = new List<object>();
|
||
|
|
|
||
|
|
if (LongPosition != null)
|
||
|
|
result.Add(BuildPositionObject(LongPosition, symbol, productType, currentPrice));
|
||
|
|
|
||
|
|
if (ShortPosition != null)
|
||
|
|
result.Add(BuildPositionObject(ShortPosition, symbol, productType, currentPrice));
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
|
||
|
|
private static object BuildPositionObject(
|
||
|
|
SimPosition pos, string symbol, string productType, double currentPrice)
|
||
|
|
{
|
||
|
|
double unrealized = pos.Side == "long"
|
||
|
|
? (currentPrice - pos.AvgPrice) * pos.Size
|
||
|
|
: (pos.AvgPrice - currentPrice) * pos.Size;
|
||
|
|
|
||
|
|
return new
|
||
|
|
{
|
||
|
|
symbol = symbol,
|
||
|
|
productType = productType,
|
||
|
|
marginMode = "crossed",
|
||
|
|
holdSide = pos.Side,
|
||
|
|
openDelegateSize = pos.Size.ToString("F4"),
|
||
|
|
margin = "0",
|
||
|
|
available = pos.Size.ToString("F4"),
|
||
|
|
locked = "0",
|
||
|
|
total = pos.Size.ToString("F4"),
|
||
|
|
leverage = pos.Leverage.ToString(),
|
||
|
|
achievedProfits = "0",
|
||
|
|
openPriceAvg = pos.AvgPrice.ToString("F6"),
|
||
|
|
posMode = "hedge_mode",
|
||
|
|
unrealizedPL = unrealized.ToString("F4"),
|
||
|
|
liquidationPrice = "0",
|
||
|
|
keepMarginRate = "0.004",
|
||
|
|
markPrice = currentPrice.ToString("F6"),
|
||
|
|
marginRatio = "0",
|
||
|
|
cTime = pos.OpenTime.ToUnixTimeMilliseconds().ToString(),
|
||
|
|
uTime = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString(),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public class SimPosition
|
||
|
|
{
|
||
|
|
public string Side { get; set; } = ""; // "long" | "short"
|
||
|
|
public double Size { get; set; }
|
||
|
|
public double AvgPrice { get; set; }
|
||
|
|
public double Leverage { get; set; } = 10;
|
||
|
|
public DateTimeOffset OpenTime { get; set; } = DateTimeOffset.UtcNow;
|
||
|
|
}
|
||
|
|
|
||
|
|
public class SimTrade
|
||
|
|
{
|
||
|
|
public DateTimeOffset Time { get; set; }
|
||
|
|
public string Side { get; set; } = "";
|
||
|
|
public string Action { get; set; } = "";
|
||
|
|
public double Size { get; set; }
|
||
|
|
public double Price { get; set; }
|
||
|
|
public double Pnl { get; set; }
|
||
|
|
}
|
||
|
|
}
|