feat: Enhance order execution and apply transaction fees
- Dynamic position sizing based on portfolio value (Kelly Criterion 2% risk) - Position size scaled by signal confidence (0.5x to 1.5x multiplier) - Apply transaction fees to all orders (both buy and sell) - Improved cash flow management: Buy pays full cost (price + fee), Sell nets proceeds minus fee - Fee schedule lookup from DataBackfiller records - Improved portfolio tracking with accurate P&L - Result: Should generate measurable returns (non-zero metrics) AGENTS.md v16.0: Data Integrity, Simplicity, Traceability Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -79,27 +79,48 @@ public sealed class ReplayEngine(
|
|||||||
var daySignals = await GenerateSignalsAsync(modelId, session, ohlcvBars, cancellationToken);
|
var daySignals = await GenerateSignalsAsync(modelId, session, ohlcvBars, cancellationToken);
|
||||||
signals.AddRange(daySignals);
|
signals.AddRange(daySignals);
|
||||||
|
|
||||||
// Convert signals to orders
|
// Convert signals to orders with dynamic position sizing
|
||||||
var dayOrders = daySignals
|
var dayOrders = daySignals
|
||||||
.Select(s => new Order(
|
.Select(s =>
|
||||||
|
{
|
||||||
|
var closePrice = GetClosePrice(session, s.Ticker, ohlcvBars);
|
||||||
|
if (closePrice <= 0) return null;
|
||||||
|
|
||||||
|
// Position size: 2% of portfolio per signal (Kelly Criterion simplified)
|
||||||
|
// Higher confidence → larger position (0.5x to 1.5x multiplier)
|
||||||
|
var riskPercentage = 0.02m * s.Confidence * 2m; // Ranges 0.01-0.03
|
||||||
|
var targetCash = currentPortfolio.TotalValue * riskPercentage;
|
||||||
|
var quantity = Math.Max(1L, (long)(targetCash / closePrice));
|
||||||
|
|
||||||
|
return new Order(
|
||||||
OrderId: Guid.NewGuid(),
|
OrderId: Guid.NewGuid(),
|
||||||
PlacedDate: session,
|
PlacedDate: session,
|
||||||
FilledDate: session, // Market order filled same day
|
FilledDate: session,
|
||||||
Ticker: s.Ticker,
|
Ticker: s.Ticker,
|
||||||
Action: s.Action,
|
Action: s.Action,
|
||||||
Quantity: 100, // Simplified: fixed quantity
|
Quantity: quantity,
|
||||||
InitialPrice: GetClosePrice(session, s.Ticker, ohlcvBars),
|
InitialPrice: closePrice,
|
||||||
FilledPrice: GetClosePrice(session, s.Ticker, ohlcvBars)))
|
FilledPrice: closePrice);
|
||||||
|
})
|
||||||
|
.Where(o => o != null)
|
||||||
|
.Cast<Order>()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
orders.AddRange(dayOrders);
|
orders.AddRange(dayOrders);
|
||||||
|
|
||||||
|
// Get fee schedule for this date
|
||||||
|
var todayFee = feeSchedule.FirstOrDefault(f => f.EffectiveDate <= session);
|
||||||
|
var feePercent = todayFee?.TransactionFeePercent ?? 0.001m; // 0.1% default
|
||||||
|
|
||||||
// Update portfolio
|
// Update portfolio
|
||||||
foreach (var order in dayOrders)
|
foreach (var order in dayOrders)
|
||||||
{
|
{
|
||||||
if (order.FilledPrice.HasValue)
|
if (order.FilledPrice.HasValue)
|
||||||
{
|
{
|
||||||
var cost = order.Quantity * order.FilledPrice.Value;
|
var cost = order.Quantity * order.FilledPrice.Value;
|
||||||
|
var fees = cost * feePercent;
|
||||||
|
var totalCost = cost + fees;
|
||||||
|
|
||||||
switch (order.Action)
|
switch (order.Action)
|
||||||
{
|
{
|
||||||
case SignalAction.Buy:
|
case SignalAction.Buy:
|
||||||
@@ -107,7 +128,7 @@ public sealed class ReplayEngine(
|
|||||||
currentPortfolio.Positions[order.Ticker] = existing + order.Quantity;
|
currentPortfolio.Positions[order.Ticker] = existing + order.Quantity;
|
||||||
currentPortfolio = currentPortfolio with
|
currentPortfolio = currentPortfolio with
|
||||||
{
|
{
|
||||||
CashBalance = currentPortfolio.CashBalance - cost
|
CashBalance = currentPortfolio.CashBalance - totalCost
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case SignalAction.Sell:
|
case SignalAction.Sell:
|
||||||
@@ -116,7 +137,7 @@ public sealed class ReplayEngine(
|
|||||||
currentPortfolio.Positions[order.Ticker] = Math.Max(0, current - order.Quantity);
|
currentPortfolio.Positions[order.Ticker] = Math.Max(0, current - order.Quantity);
|
||||||
currentPortfolio = currentPortfolio with
|
currentPortfolio = currentPortfolio with
|
||||||
{
|
{
|
||||||
CashBalance = currentPortfolio.CashBalance + cost
|
CashBalance = currentPortfolio.CashBalance + cost - fees // Sell proceeds minus fees
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user