Add funding rate watcher (#2)

* Add FundingRate interfaces and worker

* Add build on PR

* Remove zip

* Specify the solution path

* Add build for worker too

* Set up StatisticService.cs for funding rate

* Add Fundingrate alerts

* Send alert when big funding rate change + add SlashCommands.cs for fundingrate

* Remove fixtures

* Refact names

* Renames
This commit is contained in:
Oda
2024-07-19 08:31:09 +07:00
committed by GitHub
parent 545c9d8e4a
commit 029ba5f40e
41 changed files with 914 additions and 304 deletions

View File

@@ -1,25 +1,26 @@
using Managing.Application.Abstractions.Repositories;
using System.Net.Http.Json;
using System.Numerics;
using Managing.Application.Abstractions.Repositories;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Accounts;
using Managing.Domain.Candles;
using Managing.Domain.Evm;
using Managing.Domain.Statistics;
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Abstractions;
using Managing.Infrastructure.Evm.Models.Gmx;
using Managing.Infrastructure.Evm.Referentials;
using Managing.Infrastructure.Evm.Services;
using Managing.Infrastructure.Evm.Services.Gmx;
using NBitcoin;
using Nethereum.Contracts;
using Nethereum.Contracts.Standards.ERC20.ContractDefinition;
using Nethereum.HdWallet;
using Nethereum.Hex.HexTypes;
using Nethereum.Signer;
using Nethereum.Web3;
using Nethereum.HdWallet;
using System.Numerics;
using System.Net.Http.Json;
using Managing.Infrastructure.Evm.Services;
using Managing.Domain.Candles;
using Managing.Infrastructure.Evm.Abstractions;
using Managing.Core;
using static Managing.Common.Enums;
using Managing.Infrastructure.Evm.Services.Gmx;
using Nethereum.Contracts.Standards.ERC20.ContractDefinition;
using Managing.Common;
using Managing.Domain.Trades;
using Managing.Domain.Accounts;
using Managing.Infrastructure.Evm.Models.Gmx;
using Managing.Infrastructure.Evm.Referentials;
namespace Managing.Infrastructure.Evm;
@@ -590,6 +591,15 @@ public class EvmManager : IEvmManager
return await GmxService.GetTrade(web3, reference, ticker);
}
public Task<List<FundingRate>> GetFundingRates()
{
// Call GMX v2
// Call hyperliquid
// Map the results
return Task.FromResult(new List<FundingRate>());
}
public async Task<decimal> QuantityInPosition(string chainName, string publicAddress, Ticker ticker)
{
var chain = ChainService.GetChain(chainName);

View File

@@ -1,8 +1,10 @@
using Managing.Application.Abstractions.Services;
using System.Net.Http.Json;
using Managing.Application.Abstractions.Services;
using Managing.Common;
using Managing.Core;
using Managing.Domain.Statistics;
using Managing.Domain.Trades;
using Managing.Infrastructure.Evm.Models;
using System.Net.Http.Json;
namespace Managing.Infrastructure.Evm.Services;
@@ -10,14 +12,32 @@ public class TradaoService : ITradaoService
{
private readonly HttpClient _httpClient;
private readonly HashSet<Enums.Ticker> _deltaNeutralTickers = new()
{
Enums.Ticker.BTC,
Enums.Ticker.ARB,
Enums.Ticker.ETH,
Enums.Ticker.AVAX,
Enums.Ticker.BNB,
Enums.Ticker.SOL,
Enums.Ticker.LINK,
Enums.Ticker.OP,
Enums.Ticker.UNI,
Enums.Ticker.AAVE,
Enums.Ticker.PEPE,
Enums.Ticker.WIF,
};
public TradaoService()
{
_httpClient = new HttpClient(); ;
_httpClient = new HttpClient();
;
}
public async Task<List<Trader>> GetBadTrader()
{
var bestTraders = await _httpClient.GetFromJsonAsync<TradaoList>($"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/asc/2592000/0/?current=1&limit=500&order=asc&window=2592000&chain=42161&exchange=gmx&openPosition=0");
var bestTraders = await _httpClient.GetFromJsonAsync<TradaoList>(
$"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/asc/2592000/0/?current=1&limit=500&order=asc&window=2592000&chain=42161&exchange=gmx&openPosition=0");
if (bestTraders == null || bestTraders.row.Count == 0)
{
@@ -30,7 +50,8 @@ public class TradaoService : ITradaoService
public async Task<List<Trader>> GetBestTrader()
{
var bestTraders = await _httpClient.GetFromJsonAsync<TradaoList>($"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/desc/2592000/0/?current=1&limit=500&order=desc&window=2592000&chain=42161&exchange=gmx&openPosition=0");
var bestTraders = await _httpClient.GetFromJsonAsync<TradaoList>(
$"https://api.tradao.xyz/v1/td/dashboard/42161/gmx/pnlTop/500/desc/2592000/0/?current=1&limit=500&order=desc&window=2592000&chain=42161&exchange=gmx&openPosition=0");
if (bestTraders == null || bestTraders.row.Count == 0)
{
@@ -42,7 +63,9 @@ public class TradaoService : ITradaoService
public async Task<List<Trade>> GetTrades(string address)
{
var response = await _httpClient.GetFromJsonAsync<TradaoUserDetails>($"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{address}");
var response =
await _httpClient.GetFromJsonAsync<TradaoUserDetails>(
$"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{address}");
var trades = new List<Trade>();
@@ -60,7 +83,7 @@ public class TradaoService : ITradaoService
Convert.ToDecimal(position.averagePrice),
Convert.ToDecimal(position.position) / Convert.ToDecimal(position.collateral),
address, position.liqPrice
);
);
trades.Add(trade);
}
@@ -68,12 +91,15 @@ public class TradaoService : ITradaoService
return trades;
}
private async Task<List<Trader>> GetTraderDetails(TradaoList traders)
{
var result = new List<Trader>();
foreach (var trader in traders.row)
{
var response = await _httpClient.GetFromJsonAsync<TradaoUserDetails>($"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{trader.user}");
var response =
await _httpClient.GetFromJsonAsync<TradaoUserDetails>(
$"https://api.tradao.xyz/v1/td/trader/42161/gmx/insights/{trader.user}");
if (response != null)
result.Add(Map(response, trader.user));
@@ -95,4 +121,94 @@ public class TradaoService : ITradaoService
Roi = Convert.ToDecimal(response.summary.roi)
};
}
}
public async Task<List<FundingRate>> GetFundingRates()
{
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
var response =
await _httpClient.GetFromJsonAsync<List<TradaoFundingRate>>("https://api.tradao.xyz/v1/td/fr/all");
return Map(response);
}
private List<FundingRate> Map(List<TradaoFundingRate>? response)
{
if (response == null)
{
return new List<FundingRate>();
}
var result = new List<FundingRate>();
foreach (var fundingRate in response)
{
var ticker = Map(fundingRate.Symbol);
if (ticker == null || fundingRate.ExchangeName != "gmxv2")
continue;
if (!_deltaNeutralTickers.Contains(ticker.Value))
continue;
var longRate = GetApy(fundingRate.LongFundingRate);
var shortRate = GetApy(fundingRate.ShortFundingRate);
result.Add(new FundingRate
{
Ticker = ticker.Value,
Rate = longRate,
Direction = Enums.TradeDirection.Long,
Date = DateTimeOffset.FromUnixTimeSeconds(fundingRate.Timestamp).DateTime
});
result.Add(new FundingRate
{
Ticker = ticker.Value,
Rate = shortRate,
Direction = Enums.TradeDirection.Short,
Date = DateTimeOffset.FromUnixTimeSeconds(fundingRate.Timestamp).DateTime
});
}
return result;
}
private Enums.Ticker? Map(string fundingRateSymbol)
{
try
{
return MiscExtensions.ParseEnum<Enums.Ticker>(fundingRateSymbol);
}
catch (Exception e)
{
return null;
}
}
public decimal GetApy(string fundingRate)
{
try
{
decimal rate = Convert.ToDecimal(fundingRate, System.Globalization.CultureInfo.InvariantCulture);
return rate * 100 * 24 * 365;
}
catch (FormatException)
{
// Handle the case where conversion fails due to an incorrect format
// Log the error, return 0, or handle as appropriate for your application
return 0;
}
}
public class TradaoFundingRate
{
public string Symbol { get; set; }
public string ChainId { get; set; }
public string ExchangeName { get; set; }
public string LongFundingRate { get; set; }
public string ShortFundingRate { get; set; }
public long Timestamp { get; set; }
}
}