c# – Duplicate properties moved into a base class. Is it wrong?

I have LiveTradeManager and PaperTradeManager classes that inherit from TradeManagerBase. The reason behind TradeManagerBase was to create properties like Pair, StrategyName, etc. which can be used freely in the derived classes.

I feel like that design is too overhead, because I’m making a properties wrapper for the same properties. Calling just Pair instead of TradeOptions.Pair is not really a big deal, correct? Because the way it is right now, it would have taken time if I wanted to add an additional property, because of the duplicated properties.

By the way, the classes ExchangeOptions and TradeOptions are appsettings.json options.

Maybe there is a better way to handle that? I was thinking about Factory method design pattern instead. Would you like to share your thoughts?

ConcurrentBag<Trade> Trades should probably be an external class, for ex. TradesMemoryStore or something? What do you think about this as well?

I kinda want to stick to the DRY and KISS principles.

public interface ITradeManager
{
    Task InitializeAsync();
    Task RunAsync();
}

public abstract class TradeManagerBase : ITradeManager
{
    protected static ConcurrentBag<Trade> Trades { get; private set; }
    protected IExchangeClient ExchangeClient { get; }
    protected ExchangeOptions ExchangeOptions { get; }
    protected TradeOptions TradeOptions { get; }

    protected TradeManagerBase(IExchangeClient exchangeClient, ExchangeOptions exchangeOptions, TradeOptions tradeOptions)
    {
        Trades = new ConcurrentBag<Trade>();

        ExchangeClient = exchangeClient;
        ExchangeOptions = exchangeOptions;
        TradeOptions = tradeOptions;
    }

    protected ITradingStrategy StrategyResolver { get; private set; }
    protected Exchange Exchange { get; private set; }
    protected string Pair { get; private set; }
    protected string StrategyName { get; private set; }
    protected Timeframe Timeframe { get; private set; }
    protected int StartupCandleCount { get; private set; }
    protected Wallet CurrentWallet { get; private set; }
    protected decimal TradableBalanceRatio { get; private set; }
    protected string BuyOrderType { get; private set; }
    protected string SellOrderType { get; private set; }
    
    public async Task InitializeAsync()
    {
        StrategyResolver = StrategyUtils.GetStrategyInstance(TradeOptions.StrategyName);
        if (StrategyResolver.IsNull())
        {
            throw new Exception($"The strategy "{TradeOptions.StrategyName}" could not be found.");
        }

        Exchange = ExchangeOptions.Exchange;
        Pair = TradeOptions.Pair;
        StrategyName = StrategyResolver.GetStrategyName();
        Timeframe = TradeOptions.Timeframe.ToTimeframe();
        StartupCandleCount = StrategyResolver.GetStartupCandleCount();
        TradableBalanceRatio = TradeOptions.TradableBalanceRatio;
        BuyOrderType = TradeOptions.BuyOrderType;
        SellOrderType = TradeOptions.SellOrderType;
    }

    public abstract Task RunAsync();
}

public class LiveTradeManager : TradeManagerBase
{
    private static readonly ILog _logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType.Name);

    public LiveTradeManager(
        IExchangeClient exchangeClient,
        ExchangeOptions exchangeOptions,
        TradeOptions tradeOptions)
        : base(exchangeClient, exchangeOptions, tradeOptions)
    {
    }
    
    public override async Task RunAsync()
    {
        ...
    }
}
```