using System;
using System.Collections.Generic;
using System.Linq;
using YLErp.DBModels;
using YLErp.DBModels.Enums;
using YLErp.Model;
namespace YLErp.Modules.SwapModule
{
///
/// SwapTradeAutoService 的可测试子类。
/// 覆盖所有外部依赖方法,用内存数据替代数据库和静态调用。
///
public class TestableSwapTradeAutoService : SwapTradeAutoService
{
private readonly Func _nextBusinessDay;
private readonly Func _nextBusinessDayBefore;
private readonly Func _bondCalc;
// 注入的数据
private readonly List _trades;
private readonly List _tradeExtends;
private readonly List _positions;
private readonly Dictionary _clients;
private readonly Dictionary _assets;
private readonly Dictionary _underlyings;
private readonly Func _etradingRuleFactory;
private readonly Func, int, string, SwapFloatRate> _floatRateFactory;
private readonly Func _newSwapTradeFactory;
private readonly Action _autoSwapUnwind;
private readonly List _flowEvents;
private readonly List _validTrades;
/// 捕获 PersistMerge 写入的所有 merge 记录
public List PersistedMerges { get; } = new List();
/// 捕获 CreateNewSwapTrade 创建的所有交易
public List CreatedTrades { get; } = new List();
/// 捕获 AutoSwapUnwind 调用
public List<(int tradeId, decimal qty, decimal fee)> UnwindCalls { get; } = new List<(int, decimal, decimal)>();
/// SaveChanges 调用次数
public int SaveChangesCount { get; private set; }
public TestableSwapTradeAutoService(
OptUserInfo optUser,
Func nextBusinessDay = null,
Func nextBusinessDayBefore = null,
Func bondCalc = null,
List trades = null,
List tradeExtends = null,
List positions = null,
Dictionary clients = null,
Dictionary assets = null,
Dictionary underlyings = null,
Func etradingRuleFactory = null,
Func, int, string, SwapFloatRate> floatRateFactory = null,
Func newSwapTradeFactory = null,
Action autoSwapUnwind = null,
List flowEvents = null,
List validTrades = null
) : base(optUser)
{
_nextBusinessDay = nextBusinessDay ?? (d => d.AddDays(1));
_nextBusinessDayBefore = nextBusinessDayBefore ?? (d => d.AddDays(-1));
_bondCalc = bondCalc ?? ((code, price, date) => null);
_trades = trades ?? new List();
_tradeExtends = tradeExtends ?? new List();
_positions = positions ?? new List();
_clients = clients ?? new Dictionary();
_assets = assets ?? new Dictionary();
_underlyings = underlyings ?? new Dictionary();
_etradingRuleFactory = etradingRuleFactory;
_floatRateFactory = floatRateFactory;
_newSwapTradeFactory = newSwapTradeFactory;
_autoSwapUnwind = autoSwapUnwind;
_flowEvents = flowEvents ?? new List();
_validTrades = validTrades ?? new List();
}
#region Override 可测试化方法
protected override DateTime GetNextBusinessDay(DateTime date) => _nextBusinessDay(date);
protected override DateTime GetNextBusinessDayBefore(DateTime date) => _nextBusinessDayBefore(date);
protected override CalBondResult CalculateBondYtm(string underlyingCode, decimal avgPrice, DateTime settleDate)
=> _bondCalc(underlyingCode, avgPrice, settleDate);
protected override void PersistMerge(swap_flow_merge merge) => PersistedMerges.Add(merge);
protected override void SetModelOpt(DBModelBaseV2 model) { }
protected override List FindActiveSwapTrades(DateTime valueDate) => _trades;
protected override List FindTradeExtends(IEnumerable tradeIds) => _tradeExtends;
protected override List FindActivePositions(IEnumerable tradeIds, int posiDirection)
=> _positions.Where(x => x.PosiDirection == posiDirection).ToList();
protected override List FindActivePositionsAll(IEnumerable tradeIds)
=> _positions;
protected override IQueryable QueryFloatRates(DateTime valueDate, DateTime matuirityDate)
=> new List().AsQueryable();
protected override Client FindClient(int clientId)
=> _clients.TryGetValue(clientId, out var c) ? c : null;
protected override AssetUnit FindAssetUnit(string assetAccountName)
=> _assets.TryGetValue(assetAccountName ?? "", out var a) ? a : null;
protected override underlying_manager FindUnderlying(string underlyingCode)
=> _underlyings.TryGetValue(underlyingCode ?? "", out var u) ? u : null;
protected override EtradingRule GetEtradingRule(BoundSideEnum boundSide, string clientNumber)
=> _etradingRuleFactory?.Invoke((int)boundSide, clientNumber);
protected override SwapFloatRate GetSwapFloatRate(IQueryable query, int clientId, string underlyingCode)
=> _floatRateFactory?.Invoke(query, clientId, underlyingCode);
protected override List FindFlowEventsForCashCheck(swap_flow_merge flowMerge)
=> _flowEvents;
protected override List FindValidTrades(IEnumerable tradeIds)
=> _validTrades;
protected override void SaveChanges() => SaveChangesCount++;
protected override trade CreateNewSwapTrade(swap_flow_merge flowMerge, Client client, AssetUnit asset, underlying_manager underlying, SwapFloatRate floatRate, string clearingAgency, bool cashNeedAfter = false)
{
if (_newSwapTradeFactory != null)
{
var t = _newSwapTradeFactory(flowMerge, client, asset, underlying, floatRate, clearingAgency, cashNeedAfter);
CreatedTrades.Add(t);
return t;
}
var trade = new trade { id = CreatedTrades.Count + 1, TradeNumber = $"TEST-{CreatedTrades.Count + 1}" };
CreatedTrades.Add(trade);
return trade;
}
protected override void AutoSwapUnwind(int tradeId, decimal tradingAmountAvg, decimal tradingAmountFeeAvg, decimal tradingAmountNetFeeAvg, decimal tradingAmountNetAvg, DateTime occurTime, decimal tradingQtyAbs, decimal tradingFeePending)
{
UnwindCalls.Add((tradeId, tradingQtyAbs, tradingFeePending));
_autoSwapUnwind?.Invoke(tradeId, tradingAmountAvg, tradingAmountFeeAvg, tradingAmountNetFeeAvg, tradingAmountNetAvg, occurTime, tradingQtyAbs, tradingFeePending);
}
#endregion
/// 公开 SummaryFlow 供测试调用
public List ExecuteSummaryFlow(
List swapFlows, DateTime valueDate, bool save = true,
Action callback = null)
=> SummaryFlow(swapFlows, valueDate, save, callback);
/// 公开 SummaryFlow 第二个重载
public List ExecuteSummaryFlowDeal(
List swapFlows1, DateTime tradeDate, List swapFlows)
=> SummaryFlow(swapFlows1, tradeDate, swapFlows);
/// 公开 MergeRestModeCompose 供测试调用
public void ExecuteMergeRestModeCompose(List mergeList, DateTime valueDate, Action? action = null)
=> MergeRestModeCompose(mergeList, valueDate, action);
/// 公开 MergeAvgModeCompose 供测试调用
public Dictionary> ExecuteMergeAvgModeCompose(List mergeList, DateTime valueDate, Action? action = null)
=> MergeAvgModeCompose(mergeList, valueDate, action);
}
}