/// <summary> /// Быстрая (инкрементальная) синхронизация сделок и доп. информации, что с ними связана /// (заявки, стоп-заявки, позиции по деньгам и по бумагам) /// </summary> /// <param name="sps"></param> /// <param name="lAccountId">Локальный счет (в локальной базе)</param> /// <returns></returns> public async Task FastSyncAccountDataAsync(ISyncPipeServer sps, int lAccountId) { await Task.Run(async() => { _instrum_rid_lid = _replBL.GetReplications(ReplObjects.Instrum); _account_rid_lid = _replBL.GetReplications(ReplObjects.Account); _stoporder_rid_lid = _replBL.GetReplications(ReplObjects.StopOrder); _order_rid_lid = _replBL.GetReplications(ReplObjects.Order); _trade_rid_lid = _replBL.GetReplications(ReplObjects.Trade); // поиск rAccountId var found = _account_rid_lid.FirstOrDefault((p) => p.Value == lAccountId); int rAccountId = found.Key; if (rAccountId == 0) { return; } await FastSyncStopOrders(sps, lAccountId, rAccountId); await FastSyncOrders(sps, lAccountId, rAccountId); await FastSyncTrades(sps, lAccountId, rAccountId); await SyncHoldings(sps, lAccountId, rAccountId); await SyncCash(sps, lAccountId, rAccountId); _replBL.UpdateReplications(ReplObjects.Instrum, _instrum_rid_lid); _replBL.UpdateReplications(ReplObjects.Account, _account_rid_lid); _replBL.UpdateReplications(ReplObjects.StopOrder, _stoporder_rid_lid); _replBL.UpdateReplications(ReplObjects.Order, _order_rid_lid); _replBL.UpdateReplications(ReplObjects.Trade, _trade_rid_lid); }); }
/// <summary> /// Инкрементальная синхронизация сделок /// </summary> private async Task FastSyncTrades(ISyncPipeServer sps, int localAccountID, int remoteAccountID) { int maxRid = 0; // максимальный реплицированный номер if (_trade_rid_lid.Keys.Any()) { maxRid = _trade_rid_lid.Keys.Max(); } var newRemoteTrades = await sps.GetTrades(remoteAccountID, maxRid + 1); // новые записи if (newRemoteTrades == null) { return; } foreach (var rtrd in newRemoteTrades) { if (!_instrum_rid_lid.ContainsKey(rtrd.InsID)) { continue; // не смогли установить соответствие инструментов } if (!_order_rid_lid.ContainsKey(rtrd.OrderID)) { continue; // не смогли установить соответствие заявок } var ltrd = _accountDA.CreateTrade(localAccountID, _order_rid_lid[rtrd.OrderID], rtrd.Time, _instrum_rid_lid[rtrd.InsID], rtrd.BuySell, rtrd.LotCount, rtrd.Price, rtrd.Comm, rtrd.TradeNo); _trade_rid_lid.Add(rtrd.TradeID, ltrd.TradeID); // новое соответствие идентификаторов } }
public async Task SyncAccountDataAsync(ISyncPipeServer sps) { await Task.Run(async() => { _instrum_rid_lid = _replBL.GetReplications(ReplObjects.Instrum); _account_rid_lid = _replBL.GetReplications(ReplObjects.Account); _stoporder_rid_lid = _replBL.GetReplications(ReplObjects.StopOrder); _order_rid_lid = _replBL.GetReplications(ReplObjects.Order); _trade_rid_lid = _replBL.GetReplications(ReplObjects.Trade); await SyncInstrums(sps); await SyncAccounts(sps); foreach (var rAccID in _account_rid_lid.Keys) { int lAccID = _account_rid_lid[rAccID]; await SyncStopOrdersFull(sps, lAccID, rAccID); await SyncOrdersFull(sps, lAccID, rAccID); await SyncTradesFull(sps, lAccID, rAccID); await SyncHoldings(sps, lAccID, rAccID); await SyncCash(sps, lAccID, rAccID); } _replBL.UpdateReplications(ReplObjects.Instrum, _instrum_rid_lid); _replBL.UpdateReplications(ReplObjects.Account, _account_rid_lid); _replBL.UpdateReplications(ReplObjects.StopOrder, _stoporder_rid_lid); _replBL.UpdateReplications(ReplObjects.Order, _order_rid_lid); _replBL.UpdateReplications(ReplObjects.Trade, _trade_rid_lid); }); }
/// <summary> /// Синхронизация счетов /// </summary> private async Task SyncAccounts(ISyncPipeServer sps) { var remAccounts = await sps.GetAccountList(); if (remAccounts == null) { return; } var accounts = _accountDA.GetAccounts(); foreach (var racc in remAccounts) { if (_account_rid_lid.ContainsKey(racc.AccountID)) { int lid = _account_rid_lid[racc.AccountID]; var account = accounts.FirstOrDefault(r => r.AccountID == lid); if (account != null) // локальный найден { bool isUpdate = false; if (account.AccountType != racc.AccountType) { account.AccountType = racc.AccountType; isUpdate = true; } if (account.Code != racc.Code) { account.Code = racc.Code; isUpdate = true; } if (account.Name != racc.Name) { account.Name = racc.Name; isUpdate = true; } if (account.CommPerc != racc.CommPerc) { account.CommPerc = racc.CommPerc; isUpdate = true; } if (account.IsShortEnable != racc.IsShortEnable) { account.IsShortEnable = racc.IsShortEnable; isUpdate = true; } if (isUpdate) { _accountDA.UpdateAccount(account); } } else // соответствие есть, но локальный не найден, значит соответствие уже не действительно { var acc = _accountDA.CreateAccount(racc.Code, racc.Name, racc.CommPerc, racc.IsShortEnable, racc.AccountType); _account_rid_lid[racc.AccountID] = acc.AccountID; // прописываем новый ключ } } else // соответствие не найдено { var acc = _accountDA.CreateAccount(racc.Code, racc.Name, racc.CommPerc, racc.IsShortEnable, racc.AccountType); _account_rid_lid.Add(racc.AccountID, acc.AccountID); } } }
/// <summary> /// Синхронизация позиций по деньгам /// </summary> /// <param name="localAccountID">Локальный AccountID</param> /// <param name="remoteAccountID">Удаленный AccountID</param> private async Task SyncCash(ISyncPipeServer sps, int localAccountID, int remoteAccountID) { var remCash = await sps.GetCash(remoteAccountID); if (remCash == null) { return; } if (remCash == null) { return; } var cash = _accountDA.GetCash(localAccountID); if (cash == null) { _accountDA.CreateCash(localAccountID, remCash.Initial, remCash.Current, remCash.Sell, remCash.Buy, remCash.SellComm, remCash.BuyComm); } else { bool isUpdate = false; if (cash.Buy != remCash.Buy) { cash.Buy = remCash.Buy; isUpdate = true; } if (cash.BuyComm != remCash.BuyComm) { cash.BuyComm = remCash.BuyComm; isUpdate = true; } if (cash.Sell != remCash.Sell) { cash.Sell = remCash.Sell; isUpdate = true; } if (cash.SellComm != remCash.SellComm) { cash.SellComm = remCash.SellComm; isUpdate = true; } if (cash.Current != remCash.Current) { cash.Current = remCash.Current; isUpdate = true; } if (cash.Initial != remCash.Initial) { cash.Initial = remCash.Initial; isUpdate = true; } if (isUpdate) { _accountDA.UpdateCash(cash); } } }
/// <summary> /// Полная синхронизация сделок /// </summary> private async Task SyncTradesFull(ISyncPipeServer sps, int localAccountID, int remoteAccountID) { var remTrades = await sps.GetTrades(remoteAccountID, 0); if (remTrades == null) { return; } var trades = _accountDA.GetTrades(localAccountID); foreach (var rtrd in remTrades) { int insID = 0; // пытаемся сопоставить инструмент if (_instrum_rid_lid.ContainsKey(rtrd.InsID)) { insID = _instrum_rid_lid[rtrd.InsID]; } if (insID == 0) { continue; // если не смогли сопоставить инструмент, то ничего больше сделать не можем } int orderID = 0; // пытаемся сопоставить заявку if (_order_rid_lid.ContainsKey(rtrd.OrderID)) { orderID = _order_rid_lid[rtrd.OrderID]; } if (orderID == 0) { continue; // если не смогли сопоставить заявку, то ничего больше сделать не можем } if (_trade_rid_lid.ContainsKey(rtrd.TradeID)) { int lid = _trade_rid_lid[rtrd.TradeID]; var trade = trades.FirstOrDefault(r => r.TradeID == lid); if (trade == null) // локальный не найден { _trade_rid_lid.Remove(rtrd.TradeID); // удаляем старое соответствие var trd = _accountDA.CreateTrade(localAccountID, orderID, rtrd.Time, insID, rtrd.BuySell, rtrd.LotCount, rtrd.Price, rtrd.Comm, rtrd.TradeNo); _trade_rid_lid.Add(rtrd.TradeID, trd.TradeID); } } else // соответствие не найдено { var trd = _accountDA.CreateTrade(localAccountID, orderID, rtrd.Time, insID, rtrd.BuySell, rtrd.LotCount, rtrd.Price, rtrd.Comm, rtrd.TradeNo); _trade_rid_lid.Add(rtrd.TradeID, trd.TradeID); } } }
/// <summary> /// Синхронизация позиций по бумагам /// </summary> /// <param name="localAccountID">Локальный AccountID</param> /// <param name="remoteAccountID">Удаленный AccountID</param> private async Task SyncHoldings(ISyncPipeServer sps, int localAccountID, int remoteAccountID) { var remHoldings = await sps.GetHoldingList(remoteAccountID); if (remHoldings == null) { return; } var holdings = _accountDA.GetHoldings(localAccountID); foreach (var r_hold in remHoldings) { if (!_instrum_rid_lid.ContainsKey(r_hold.InsID)) { continue; } int l_insID = _instrum_rid_lid[r_hold.InsID]; var l_hold = holdings.FirstOrDefault(r => r.InsID == l_insID); if (l_hold == null) { _accountDA.CreateHolding(localAccountID, l_insID, r_hold.LotCount); } else if (l_hold.LotCount != r_hold.LotCount) { l_hold.LotCount = r_hold.LotCount; _accountDA.UpdateHolding(l_hold); } } foreach (var l_hold in holdings) { if (!_instrum_rid_lid.ContainsValue(l_hold.InsID)) { continue; } var r_insID = _instrum_rid_lid.FirstOrDefault(r => r.Value == l_hold.InsID).Key; if (remHoldings.FirstOrDefault(r => r.InsID == r_insID) == null) // в локальной базе есть запись с инструментом, а в удаленной базе нет такого инструмента, значит удаляем { _accountDA.DeleteHolding(l_hold.HoldingID); } } }
/// <summary> /// Инкрементальная синхронизация заявок /// </summary> /// <param name="sps">Провайдер синхронизации</param> /// <param name="localAccountID">Счет в локальной базе</param> /// <param name="remoteAccountID">Счет в базе Leech</param> /// <returns></returns> private async Task FastSyncOrders(ISyncPipeServer sps, int localAccountID, int remoteAccountID) { // активные заявки могут измениться // по всем активным заявкам составляем список соответствующих remoteID List <int> rids = new List <int>(); var activeOrders = _accountDA.GetOrders(localAccountID, true); foreach (var so in activeOrders) { if (!_order_rid_lid.Values.Contains(so.OrderID)) { continue; } int rid = _order_rid_lid.First(r => r.Value == so.OrderID).Key; rids.Add(rid); } var rOrders = await sps.GetOrders(rids.ToArray()); // список заявок, которые могут измениться if (rOrders == null) { return; } foreach (var rord in rOrders) { var lord = activeOrders.FirstOrDefault(r => r.OrderID == _order_rid_lid[rord.OrderID]); if (lord == null) { continue; } bool isUpdate = false; if (rord.BuySell != lord.BuySell) { lord.BuySell = rord.BuySell; isUpdate = true; } if (rord.LotCount != lord.LotCount) { lord.LotCount = rord.LotCount; isUpdate = true; } if (rord.Price != lord.Price) { lord.Price = rord.Price; isUpdate = true; } if (rord.Status != lord.Status) { lord.Status = rord.Status; isUpdate = true; } if (rord.OrderNo != lord.OrderNo) { lord.OrderNo = rord.OrderNo; isUpdate = true; } if (rord.Time != lord.Time) { lord.Time = rord.Time; isUpdate = true; } int?soID = null; if (rord.StopOrderID != null && _stoporder_rid_lid.ContainsKey(rord.StopOrderID.Value)) { soID = _stoporder_rid_lid[rord.StopOrderID.Value]; } if (lord.StopOrderID != soID) { lord.StopOrderID = soID; isUpdate = true; } if (isUpdate) { _accountDA.UpdateOrder(lord); } } int maxRid = 0; // максимальный реплицированный номер if (_order_rid_lid.Keys.Any()) { maxRid = _order_rid_lid.Keys.Max(); } var newRemoteOrders = await sps.GetOrders(remoteAccountID, maxRid + 1); // новые записи, которые появились на сервере и еще не загружались if (newRemoteOrders == null) { return; } foreach (var rord in newRemoteOrders) { if (!_instrum_rid_lid.ContainsKey(rord.InsID)) { continue; // не смогли установить соответствие инструментов } var ord = _accountDA.CreateOrder(localAccountID, rord.Time, _instrum_rid_lid[rord.InsID], rord.BuySell, rord.LotCount, rord.Price, rord.Status, rord.StopOrderID, rord.OrderNo); _order_rid_lid.Add(rord.OrderID, ord.OrderID); // новое соответствие идентификаторов } }
/// <summary> /// Полная синхронизация заявок /// </summary> private async Task SyncOrdersFull(ISyncPipeServer sps, int localAccountID, int remoteAccountID) { var remOrders = await sps.GetOrders(remoteAccountID, 0); if (remOrders == null) { return; } var orders = _accountDA.GetOrders(localAccountID, false); foreach (var rOrd in remOrders) { int insID = 0; // пытаемся сопоставить инструмент if (_instrum_rid_lid.ContainsKey(rOrd.InsID)) { insID = _instrum_rid_lid[rOrd.InsID]; } if (insID == 0) { continue; // если не смогли сопоставить инструмент, то ничего больше сделать не можем } // ссылка на стоп-заявку int?soID = null; if (rOrd.StopOrderID != null && _stoporder_rid_lid.ContainsKey(rOrd.StopOrderID.Value)) { soID = _stoporder_rid_lid[rOrd.StopOrderID.Value]; } if (_order_rid_lid.ContainsKey(rOrd.OrderID)) { int lid = _order_rid_lid[rOrd.OrderID]; var order = orders.FirstOrDefault(r => r.OrderID == lid); if (order != null) // локальный найден { // сравниваем все по полной, хотя в реальности объект не может так сильно меняться bool isUpdate = false; if (order.InsID != insID) { order.InsID = insID; isUpdate = true; } if (order.BuySell != rOrd.BuySell) { order.BuySell = rOrd.BuySell; isUpdate = true; } if (order.LotCount != rOrd.LotCount) { order.LotCount = rOrd.LotCount; isUpdate = true; } if (order.Status != rOrd.Status) { order.Status = rOrd.Status; isUpdate = true; } if (order.OrderNo != rOrd.OrderNo) { order.OrderNo = rOrd.OrderNo; isUpdate = true; } if (order.Time != rOrd.Time) { order.Time = rOrd.Time; isUpdate = true; } if (order.Price != rOrd.Price) { order.Price = rOrd.Price; isUpdate = true; } if (order.StopOrderID != soID) { order.StopOrderID = soID; isUpdate = true; } if (isUpdate) { _accountDA.UpdateOrder(order); } } else // соответствие есть, но локальный не найден, значит соответствие уже не действительно { _order_rid_lid.Remove(rOrd.OrderID); // удаляем старое соответствие var ord = _accountDA.CreateOrder(localAccountID, rOrd.Time, insID, rOrd.BuySell, rOrd.LotCount, rOrd.Price, rOrd.Status, soID, rOrd.OrderNo); _order_rid_lid.Add(rOrd.OrderID, ord.OrderID); } } else // соответствие не найдено { var ord = _accountDA.CreateOrder(localAccountID, rOrd.Time, insID, rOrd.BuySell, rOrd.LotCount, rOrd.Price, rOrd.Status, soID, rOrd.OrderNo); _order_rid_lid.Add(rOrd.OrderID, ord.OrderID); } } }
/// <summary> /// Полная синхронизация стоп-заявок /// </summary> private async Task SyncStopOrdersFull(ISyncPipeServer sps, int localAccountID, int remoteAccountID) { var remStopOrders = await sps.GetStopOrders(remoteAccountID, 0); if (remStopOrders == null) { return; } var stopOrders = _accountDA.GetStopOrders(localAccountID, false); foreach (var rso in remStopOrders) { int insID = 0; // пытаемся сопоставить инструмент if (_instrum_rid_lid.ContainsKey(rso.InsID)) { insID = _instrum_rid_lid[rso.InsID]; } if (insID == 0) { continue; // если не смогли сопоставить инструмент, то ничего больше сделать не можем } if (_stoporder_rid_lid.ContainsKey(rso.StopOrderID)) { int lid = _stoporder_rid_lid[rso.StopOrderID]; var stopOrder = stopOrders.FirstOrDefault(r => r.StopOrderID == lid); if (stopOrder != null) // локальный найден { // сравниваем все по полной, хотя в реальности объект не может так сильно меняться bool isUpdate = false; if (stopOrder.InsID != insID) { stopOrder.InsID = insID; isUpdate = true; } if (stopOrder.AlertPrice != rso.AlertPrice) { stopOrder.AlertPrice = rso.AlertPrice; isUpdate = true; } if (stopOrder.BuySell != rso.BuySell) { stopOrder.BuySell = rso.BuySell; isUpdate = true; } if (stopOrder.LotCount != rso.LotCount) { stopOrder.LotCount = rso.LotCount; isUpdate = true; } if (stopOrder.Status != rso.Status) { stopOrder.Status = rso.Status; isUpdate = true; } if (stopOrder.StopOrderNo != rso.StopOrderNo) { stopOrder.StopOrderNo = rso.StopOrderNo; isUpdate = true; } if (stopOrder.StopType != rso.StopType) { stopOrder.StopType = rso.StopType; isUpdate = true; } if (stopOrder.Time != rso.Time) { stopOrder.Time = rso.Time; isUpdate = true; } if (stopOrder.Price != rso.Price) { stopOrder.Price = rso.Price; isUpdate = true; } if (stopOrder.EndTime != rso.EndTime) { stopOrder.EndTime = rso.EndTime; isUpdate = true; } if (stopOrder.CompleteTime != rso.CompleteTime) { stopOrder.CompleteTime = rso.CompleteTime; isUpdate = true; } if (isUpdate) { _accountDA.UpdateStopOrder(stopOrder); } } else // соответствие есть, но локальный не найден, значит соответствие уже не действительно { _stoporder_rid_lid.Remove(rso.StopOrderID); // удаляем старое соответствие var so = _accountDA.CreateStopOrder(localAccountID, rso.Time, insID, rso.BuySell, rso.StopType, rso.EndTime, rso.AlertPrice, rso.Price, rso.LotCount, rso.Status, rso.CompleteTime, rso.StopOrderNo); _stoporder_rid_lid.Add(rso.StopOrderID, so.StopOrderID); } } else // соответствие не найдено { var so = _accountDA.CreateStopOrder(localAccountID, rso.Time, insID, rso.BuySell, rso.StopType, rso.EndTime, rso.AlertPrice, rso.Price, rso.LotCount, rso.Status, rso.CompleteTime, rso.StopOrderNo); _stoporder_rid_lid.Add(rso.StopOrderID, so.StopOrderID); } } }
/// <summary> /// Синхронизация фин инструментов /// </summary> private async Task SyncInstrums(ISyncPipeServer sps) { var remInstrums = await sps.GetInstrumList(); if (remInstrums == null) { return; } var instrums = _instrumDA.GetInstrums(); foreach (var rIns in remInstrums) { if (_instrum_rid_lid.ContainsKey(rIns.InsID)) { int lid = _instrum_rid_lid[rIns.InsID]; var ins = instrums.FirstOrDefault(r => r.InsID == lid); if (ins != null) // локальный найден { if (ins.Ticker != rIns.Ticker || ins.ShortName != rIns.ShortName || ins.Name != rIns.Name || ins.LotSize != rIns.LotSize || ins.Decimals != rIns.Decimals || ins.PriceStep != rIns.PriceStep) { _instrumDA.UpdateInstrum(ins.InsID, rIns.Ticker, rIns.ShortName, rIns.Name, rIns.LotSize, rIns.Decimals, rIns.PriceStep); } } else // соответствие есть, но локальный не найден, значит соответствие уже не действительно { int insID = _instrumDA.InsertInstrum( rIns.Ticker, rIns.ShortName, rIns.Name, rIns.LotSize, rIns.Decimals, rIns.PriceStep); _instrum_rid_lid[rIns.InsID] = insID; // прописываем новый ключ } } else // соответствие не найдено { int insID = _instrumDA.InsertInstrum( rIns.Ticker, rIns.ShortName, rIns.Name, rIns.LotSize, rIns.Decimals, rIns.PriceStep); _instrum_rid_lid.Add(rIns.InsID, insID); } } // отследим ситуацию, когда удаленный объект удален, а локальный остался // если локальный объект есть, а репликации для него нет, то значит этот объект не реплицировался и мы его не трогаем // если локальный объект есть и репликация для него есть, то мы смотрим remote-объект // если remote-объект есть, то здесь ничего не делаем (эту ситуацию мы обработали выше) // если remote-объекта нет, то локальный объект НЕ удаляем и не меняем, а просто удаляем для него репликацию // удалить локальный объект нельзя, т.к. на него могут быть ссылки, т.е. он остается, но больше не будет реплицироваться // если в удаленной базе объект снова возникнет под прежним id (что вряд-ли возможно), // то локально будет создан новый объект, а старый также останется foreach (var ins in instrums) { if (!_instrum_rid_lid.ContainsValue(ins.InsID)) { continue; // запись не реплицировалась и мы ее пропускаем } var repl = _instrum_rid_lid.FirstOrDefault(r => r.Value == ins.InsID); // обязательно будет не null var found = remInstrums.FirstOrDefault(r => r.InsID == repl.Key); if (found == null) // соответствующей remote-записи нет, значит локальную запись удаляем и убираем для нее репликацию { _instrum_rid_lid.Remove(repl.Key); } } }