public OrderBook DeepClone(decimal fee = 0) { var result = (OrderBook)MemberwiseClone(); if (fee == 0) { Bids = new List <VolumePrice>(Bids); Asks = new List <VolumePrice>(Asks); } else { var bids = new List <VolumePrice>(); foreach (var bid in Bids) { var newVolumePrice = new VolumePrice(bid.Price - (bid.Price / 100 * fee), bid.Volume); bids.Add(newVolumePrice); } result.Bids = bids; var asks = new List <VolumePrice>(); foreach (var ask in Asks) { var newVolumePrice = new VolumePrice(ask.Price + (ask.Price / 100 * fee), ask.Volume); asks.Add(newVolumePrice); } result.Asks = asks; } return(result); }
private static WhichOrder[] GetWithMinVolumeInBaseAsset(VolumePrice left, VolumePrice middle, VolumePrice right) { var middleVolumeInBaseAsset = middle.Volume / left.Price; var interimBidPrice = left.Price * middle.Price; var rightVolumeInBaseAsset = right.Volume / interimBidPrice; var minVolume = Math.Min(Math.Min(left.Volume, middleVolumeInBaseAsset), rightVolumeInBaseAsset); var result = new List <WhichOrder>(); if (left.Volume == minVolume) { result.Add(WhichOrder.Left); } if (middleVolumeInBaseAsset == minVolume) { result.Add(WhichOrder.Middle); } if (rightVolumeInBaseAsset == minVolume) { result.Add(WhichOrder.Right); } return(result.ToArray()); }
private static VolumePrice SynthVolumePrice(VolumePrice left, VolumePrice right) { var newPrice = left.Price * right.Price; var rightVolumeInBaseAsset = right.Volume / left.Price; var minVolume = Math.Min(left.Volume, rightVolumeInBaseAsset); var result = new VolumePrice(newPrice, minVolume); return(result); }
public Arbitrage(AssetPair assetPair, SynthOrderBook bidSynth, VolumePrice bid, SynthOrderBook askSynth, VolumePrice ask) { Debug.Assert(bidSynth != null); Debug.Assert(askSynth != null); AssetPair = assetPair; BidSynth = bidSynth; AskSynth = askSynth; Bid = bid; Ask = ask; Spread = GetSpread(Bid.Price, Ask.Price); Volume = Ask.Volume < Bid.Volume ? Ask.Volume : Bid.Volume; PnL = GetPnL(Bid.Price, Ask.Price, Volume); ConversionPath = FormatConversionPath(BidSynth.ConversionPath, AskSynth.ConversionPath); StartedAt = DateTime.UtcNow; }
public static (decimal Volume, decimal PnL)? GetArbitrageVolumePnL(IEnumerable <VolumePrice> orderedBids, IEnumerable <VolumePrice> orderedAsks) { Debug.Assert(orderedBids != null); Debug.Assert(orderedAsks != null); var orderedBidsEnumerator = orderedBids.GetEnumerator(); var orderedAsksEnumerator = orderedAsks.GetEnumerator(); if (!orderedBidsEnumerator.MoveNext() || !orderedAsksEnumerator.MoveNext()) { return(null); } // Clone bids and asks decimal volume = 0; decimal pnl = 0; var bid = new VolumePrice(orderedBidsEnumerator.Current.Price, orderedBidsEnumerator.Current.Volume); var ask = new VolumePrice(orderedAsksEnumerator.Current.Price, orderedAsksEnumerator.Current.Volume); while (true) { if (bid.Price <= ask.Price) { break; } var tradeBidPrice = bid.Price; var tradeAskPrice = ask.Price; if (bid.Volume < ask.Volume) { volume += bid.Volume; pnl += bid.Volume * (tradeBidPrice - tradeAskPrice); ask.SubtractVolume(bid.Volume); if (!orderedBidsEnumerator.MoveNext()) { break; } bid = orderedBidsEnumerator.Current; } else if (bid.Volume > ask.Volume) { volume += ask.Volume; pnl += ask.Volume * (tradeBidPrice - tradeAskPrice); bid.SubtractVolume(ask.Volume); if (!orderedAsksEnumerator.MoveNext()) { break; } ask = orderedAsksEnumerator.Current; } else if (bid.Volume == ask.Volume) { volume += bid.Volume; pnl += bid.Volume * (tradeBidPrice - tradeAskPrice); if (!orderedBidsEnumerator.MoveNext()) { break; } bid = orderedBidsEnumerator.Current; if (!orderedAsksEnumerator.MoveNext()) { break; } ask = orderedAsksEnumerator.Current; } } orderedBidsEnumerator.Dispose(); orderedAsksEnumerator.Dispose(); return(volume == 0 ? ((decimal, decimal)?)null : (volume, Math.Round(pnl, 8))); }