• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moconnell / yolo / 23383623242

21 Mar 2026 04:17PM UTC coverage: 80.943% (+2.7%) from 78.226%
23383623242

Pull #108

github

web-flow
Merge cd8338f19 into 35fa78d57
Pull Request #108: FIX: reprice LMT orders n times, then replace with MKT; add new weight verification endpoint

324 of 382 branches covered (84.82%)

Branch coverage included in aggregate %.

474 of 582 new or added lines in 13 files covered. (81.44%)

74 existing lines in 2 files now uncovered.

2458 of 3055 relevant lines covered (80.46%)

22.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.59
/src/YoloFunk/Functions/EffectiveWeightsFunctionBase.cs
1
using System.Net;
2
using Microsoft.Azure.Functions.Worker.Http;
3
using Microsoft.Extensions.DependencyInjection;
4
using Microsoft.Extensions.Logging;
5
using YoloAbstractions;
6
using YoloAbstractions.Config;
7
using YoloAbstractions.Extensions;
8
using YoloAbstractions.Interfaces;
9
using YoloBroker.Interface;
10
using YoloFunk.Dto;
11
using YoloTrades;
12

13
namespace YoloFunk.Functions;
14

15
public abstract class EffectiveWeightsFunctionBase
16
{
17
    private readonly IServiceProvider _serviceProvider;
18
    private readonly ILogger _logger;
19

20
    protected EffectiveWeightsFunctionBase(IServiceProvider serviceProvider, ILogger logger)
5✔
21
    {
5✔
22
        _serviceProvider = serviceProvider;
5✔
23
        _logger = logger;
5✔
24
    }
5✔
25

26
    protected abstract string StrategyKey { get; }
27

28
    protected async Task<HttpResponseData> GetEffectiveWeightsAsync(HttpRequestData req, CancellationToken cancellationToken)
29
    {
5✔
30
        try
31
        {
5✔
32
            var broker = _serviceProvider.GetRequiredKeyedService<IYoloBroker>(StrategyKey);
5✔
33
            var accountContext = broker.GetAccountContext();
3✔
34
            var address = accountContext.Address;
3✔
35
            var vaultAddress = accountContext.VaultAddress;
3✔
36

37
            if (string.IsNullOrWhiteSpace(address))
3✔
38
            {
2✔
39
                var error = req.CreateResponse(HttpStatusCode.InternalServerError);
2✔
40
                await error.WriteAsJsonAsync(
2✔
41
                    new RebalanceErrorResponse(
2✔
42
                        StrategyKey,
2✔
43
                        "Invalid strategy configuration",
2✔
44
                        "Broker account context is missing address."),
2✔
45
                    cancellationToken);
2✔
46
                return error;
2✔
47
            }
48

49
            var weightsService = _serviceProvider.GetRequiredKeyedService<ICalcWeights>(StrategyKey);
1✔
50
            var yoloConfig = _serviceProvider.GetRequiredKeyedService<YoloConfig>(StrategyKey);
1✔
51
            var targetWeights = await weightsService.CalculateWeightsAsync(cancellationToken);
1✔
52

53
            var positions = await broker.GetPositionsAsync(cancellationToken);
1✔
54
            var baseAssetFilter = positions.Keys
1✔
55
                .Union(targetWeights.Keys.Select(x => x.GetBaseAndQuoteAssets().BaseAsset))
1✔
56
                .ToHashSet();
1✔
57

58
            var markets = await broker.GetMarketsAsync(
1✔
59
                baseAssetFilter,
1✔
60
                yoloConfig.BaseAsset,
1✔
61
                yoloConfig.AssetPermissions,
1✔
62
                cancellationToken);
1✔
63

64
            var nominal = yoloConfig.NominalCash ?? positions.GetTotalValue(markets, yoloConfig.BaseAsset);
1✔
65
            var verification = CalculateEffectiveWeights(targetWeights, positions, markets, yoloConfig, nominal);
1✔
66

67
            var response = req.CreateResponse(HttpStatusCode.OK);
1✔
68
            await response.WriteAsJsonAsync(
1✔
69
                new EffectiveWeightsResponse(
1✔
70
                    StrategyKey,
1✔
71
                    address,
1✔
72
                    vaultAddress,
1✔
73
                    DateTime.UtcNow,
1✔
74
                    nominal,
1✔
75
                    verification.WeightConstraint,
1✔
76
                    verification.Weights),
1✔
77
                cancellationToken);
1✔
78
            return response;
1✔
79
        }
80
        catch (Exception ex)
2✔
81
        {
2✔
82
            _logger.LogError(ex,
2✔
83
                "Failed to calculate effective weights for strategy {Strategy}",
2✔
84
                StrategyKey);
2✔
85

86
            var errorResponse = req.CreateResponse(HttpStatusCode.InternalServerError);
2✔
87
            await errorResponse.WriteAsJsonAsync(
2✔
88
                new RebalanceErrorResponse(
2✔
89
                    StrategyKey,
2✔
90
                    "Failed to calculate effective weights",
2✔
91
                    "An internal error occurred. Check logs for details."),
2✔
92
                cancellationToken);
2✔
93
            return errorResponse;
2✔
94
        }
95
    }
5✔
96

97
    private static (decimal WeightConstraint, IReadOnlyList<EffectiveWeightItem> Weights) CalculateEffectiveWeights(
98
        IReadOnlyDictionary<string, decimal> rawWeights,
99
        IReadOnlyDictionary<string, IReadOnlyList<Position>> positions,
100
        IReadOnlyDictionary<string, IReadOnlyList<MarketInfo>> markets,
101
        YoloConfig yoloConfig,
102
        decimal nominal)
103
    {
1✔
104
        var factors = rawWeights.ToDictionary(
1✔
105
            kvp => kvp.Key.GetBaseAndQuoteAssets().BaseAsset,
1✔
106
            kvp => (Weight: kvp.Value, IsInUniverse: true));
2✔
107

108
        var unconstrainedTargetLeverage = factors
1✔
109
            .Values
1✔
110
            .Sum(w => Math.Abs(w.Weight));
2✔
111

112
        var weightConstraint = unconstrainedTargetLeverage < yoloConfig.MaxLeverage
1✔
113
            ? 1
1✔
114
            : yoloConfig.MaxLeverage / unconstrainedTargetLeverage;
1✔
115

116
        var droppedTokens = positions.Keys.Except(factors.Keys.Union([yoloConfig.BaseAsset]));
1✔
117
        foreach (var token in droppedTokens)
3✔
NEW
118
        {
×
NEW
119
            factors[token] = (0m, false);
×
NEW
120
        }
×
121

122
        var effectiveItems = new List<EffectiveWeightItem>(factors.Count);
1✔
123

124
        foreach (var (token, (weight, isInUniverse)) in factors.OrderBy(x => x.Key, StringComparer.OrdinalIgnoreCase))
6✔
125
        {
1✔
126
            var tokenPositions = positions.TryGetValue(token, out var pos)
1✔
127
                ? pos.ToArray()
1✔
128
                : [];
1✔
129

130
            var constrainedTargetWeight = weightConstraint * weight;
1✔
131
            var marketList = markets.GetMarkets(token);
1✔
132
            var projectedPositions = marketList
1✔
133
                .ToDictionary(
1✔
134
                    market => market.Name,
1✔
135
                    market =>
1✔
136
                    {
1✔
137
                        var position = tokenPositions
1✔
138
                                           .FirstOrDefault(p =>
1✔
139
                                               p.AssetType == market.AssetType &&
1✔
140
                                               (p.AssetName == market.Name && market.AssetType == AssetType.Future ||
1✔
141
                                                p.BaseAsset == market.BaseAsset && market.AssetType == AssetType.Spot)) ??
1✔
142
                                       Position.Null;
1✔
143

1✔
144
                        return new ProjectedPosition(market, position.Amount, nominal);
1✔
145
                    });
2✔
146

147
            var hasTradableMarket = projectedPositions.Count != 0;
1✔
148
            var hasMultipleOpenPositions = projectedPositions.Count(kvp => kvp.Value.HasPosition) > 1;
2✔
149
            var currentWeight = hasTradableMarket && !hasMultipleOpenPositions
1✔
150
                ? projectedPositions.Values.Sum(projectedPosition => projectedPosition.ProjectedWeight)
1✔
151
                : null;
1✔
152

153
            var withinTradeBuffer = isInUniverse &&
1✔
154
                                    currentWeight.HasValue &&
1✔
155
                                    Math.Abs(currentWeight.Value - constrainedTargetWeight) <= yoloConfig.TradeBuffer;
1✔
156

157
            decimal? effectiveWeight = currentWeight.HasValue
1✔
158
                ? withinTradeBuffer
1✔
159
                    ? currentWeight.Value
1✔
160
                    : yoloConfig.RebalanceMode switch
1✔
161
                    {
1✔
NEW
162
                        RebalanceMode.Edge => CalculateEdgeTarget(currentWeight.Value, constrainedTargetWeight, yoloConfig.TradeBuffer),
×
163
                        _ => constrainedTargetWeight
1✔
164
                    }
1✔
165
                : null;
1✔
166

167
            decimal? deltaWeight = effectiveWeight.HasValue && currentWeight.HasValue
1✔
168
                ? effectiveWeight.Value - currentWeight.Value
1✔
169
                : null;
1✔
170

171
            effectiveItems.Add(new EffectiveWeightItem(
1✔
172
                token,
1✔
173
                weight,
1✔
174
                constrainedTargetWeight,
1✔
175
                currentWeight,
1✔
176
                effectiveWeight,
1✔
177
                deltaWeight,
1✔
178
                isInUniverse,
1✔
179
                withinTradeBuffer,
1✔
180
                hasTradableMarket));
1✔
181
        }
1✔
182

183
        return (weightConstraint, effectiveItems);
1✔
184
    }
1✔
185

186
    private static decimal CalculateEdgeTarget(decimal currentWeight, decimal idealWeight, decimal tradeBuffer) =>
NEW
187
        currentWeight > idealWeight
×
NEW
188
            ? idealWeight + tradeBuffer
×
NEW
189
            : idealWeight - tradeBuffer;
×
190

191
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc