Update open interest

This commit is contained in:
2025-08-14 23:53:45 +07:00
parent 580ce4d9c9
commit 2622da05e6
7 changed files with 44 additions and 34 deletions

View File

@@ -25,6 +25,7 @@ public interface ITradingRepository
Task<Position> GetPositionByIdentifierAsync(Guid identifier); Task<Position> GetPositionByIdentifierAsync(Guid identifier);
Task<IEnumerable<Position>> GetPositionsAsync(PositionInitiator positionInitiator); Task<IEnumerable<Position>> GetPositionsAsync(PositionInitiator positionInitiator);
Task<IEnumerable<Position>> GetPositionsByStatusAsync(PositionStatus positionStatus); Task<IEnumerable<Position>> GetPositionsByStatusAsync(PositionStatus positionStatus);
Task<IEnumerable<Position>> GetAllPositionsAsync();
Task UpdateScenarioAsync(Scenario scenario); Task UpdateScenarioAsync(Scenario scenario);
Task UpdateStrategyAsync(IndicatorBase indicatorBase); Task UpdateStrategyAsync(IndicatorBase indicatorBase);

View File

@@ -35,6 +35,7 @@ public interface ITradingService
Task UpdateScenarioAsync(Scenario scenario); Task UpdateScenarioAsync(Scenario scenario);
Task UpdateIndicatorAsync(IndicatorBase indicatorBase); Task UpdateIndicatorAsync(IndicatorBase indicatorBase);
Task<IEnumerable<Position>> GetBrokerPositions(Account account); Task<IEnumerable<Position>> GetBrokerPositions(Account account);
Task<IEnumerable<Position>> GetAllDatabasePositionsAsync();
Task<PrivyInitAddressResponse> InitPrivyWallet(string publicAddress); Task<PrivyInitAddressResponse> InitPrivyWallet(string publicAddress);
// Synth API integration methods // Synth API integration methods

View File

@@ -75,14 +75,8 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
{ {
_logger.LogInformation("Refreshing platform summary data"); _logger.LogInformation("Refreshing platform summary data");
// Get all data in parallel for better performance var agents = await _agentService.GetAllAgentSummaries();
var agentsTask = _agentService.GetAllAgentSummaries(); var strategies = await _botService.GetBotsAsync();
var strategiesTask = _botService.GetBotsAsync();
await Task.WhenAll(agentsTask, strategiesTask);
var agents = await agentsTask;
var strategies = await strategiesTask;
// Calculate totals // Calculate totals
var totalAgents = agents.Count(); var totalAgents = agents.Count();
@@ -183,19 +177,20 @@ public class PlatformSummaryGrain : Grain, IPlatformSummaryGrain, IRemindable
try try
{ {
// Get all open positions from all accounts // Get all open positions from all accounts
var openPositions = await _tradingService.GetBrokerPositions(null); // Get positions directly from database instead of exchange
var allPositions = await _tradingService.GetAllDatabasePositionsAsync();
var openPositions = allPositions?.Where(p => !p.IsFinished());
if (openPositions?.Any() == true) if (openPositions?.Any() == true)
{ {
var positionCount = openPositions.Count(); var positionCount = openPositions.Count();
// Calculate open interest as the sum of position notional values // Calculate open interest as the sum of leveraged position notional values
// Open interest = sum of (position size * price) for all open positions // Open interest = sum of (position size * price * leverage) for all open positions
var openInterest = openPositions var openInterest = openPositions
.Where(p => p.Open?.Price > 0 && p.Open?.Quantity > 0) .Sum(p => (p.Open.Price * p.Open.Quantity) * p.Open.Leverage);
.Sum(p => p.Open.Price * p.Open.Quantity);
_logger.LogDebug("Calculated position metrics: {PositionCount} positions, {OpenInterest} open interest", _logger.LogDebug("Calculated position metrics: {PositionCount} positions, {OpenInterest} leveraged open interest",
positionCount, openInterest); positionCount, openInterest);
return (openInterest, positionCount); return (openInterest, positionCount);

View File

@@ -250,6 +250,11 @@ public class TradingService : ITradingService
return await _exchangeService.GetBrokerPositions(account); return await _exchangeService.GetBrokerPositions(account);
} }
public async Task<IEnumerable<Position>> GetAllDatabasePositionsAsync()
{
return await _tradingRepository.GetAllPositionsAsync();
}
private async Task ManageTrader(TraderFollowup a, List<Ticker> tickers) private async Task ManageTrader(TraderFollowup a, List<Ticker> tickers)
{ {
var shortAddress = a.Account.Address.Substring(0, 6); var shortAddress = a.Account.Address.Substring(0, 6);

View File

@@ -397,6 +397,21 @@ public class PostgreSqlTradingRepository : ITradingRepository
} }
} }
public async Task<IEnumerable<Position>> GetAllPositionsAsync()
{
var positions = await _context.Positions
.AsNoTracking()
.Include(p => p.User)
.Include(p => p.OpenTrade)
.Include(p => p.StopLossTrade)
.Include(p => p.TakeProfit1Trade)
.Include(p => p.TakeProfit2Trade)
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(positions);
}
#endregion #endregion
#region Signal Methods #region Signal Methods

View File

@@ -94,15 +94,15 @@ const BotList: React.FC<IBotList> = ({ list }) => {
</button> </button>
) )
} }
function getToggleBotStatusBadge(status: string, identifier: string) { function getToggleBotStatusBadge(status: BotStatus, identifier: string) {
const classes = const classes =
baseBadgeClass() + (status == 'Up' ? ' bg-error' : ' bg-success') baseBadgeClass() + (status == BotStatus.Running ? ' bg-error' : ' bg-success')
return ( return (
<button <button
className={classes} className={classes}
onClick={() => toggleBotStatus(status, identifier)} onClick={() => toggleBotStatus(status, identifier)}
> >
{status == 'Up' ? ( {status == BotStatus.Running ? (
<p className="text-accent-content flex"> <p className="text-accent-content flex">
<StopIcon width={15}></StopIcon> <StopIcon width={15}></StopIcon>
</p> </p>
@@ -165,11 +165,11 @@ const BotList: React.FC<IBotList> = ({ list }) => {
setShowTradesModal(true) setShowTradesModal(true)
} }
function toggleBotStatus(status: string, identifier: string) { function toggleBotStatus(status: BotStatus, identifier: string) {
const isUp = status == 'Up' const isUp = status == BotStatus.Running
const t = new Toast(isUp ? 'Stoping bot' : 'Restarting bot') const t = new Toast(isUp ? 'Stoping bot' : 'Restarting bot')
if (status == 'Up') { if (status == BotStatus.Running) {
client client
.bot_Stop(identifier) .bot_Stop(identifier)
.then(() => { .then(() => {
@@ -178,7 +178,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
.catch((err) => { .catch((err) => {
t.update('error', err) t.update('error', err)
}) })
} else if (status == 'Down' || status == 'None') { } else if (status == BotStatus.Stopped || status == BotStatus.Saved) {
client client
.bot_Restart(identifier) .bot_Restart(identifier)
.then(() => { .then(() => {
@@ -261,7 +261,7 @@ const BotList: React.FC<IBotList> = ({ list }) => {
{/* Action Badges - Only show for bot owners */} {/* Action Badges - Only show for bot owners */}
{isBotOwner(bot.agentName) && ( {isBotOwner(bot.agentName) && (
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{getToggleBotStatusBadge(bot.status, bot.identifier)} {getToggleBotStatusBadge(bot.status as BotStatus, bot.identifier)}
{getUpdateBotBadge(bot)} {getUpdateBotBadge(bot)}
{getManualPositionBadge(bot.identifier)} {getManualPositionBadge(bot.identifier)}
{getDeleteBadge(bot.identifier)} {getDeleteBadge(bot.identifier)}

View File

@@ -1,16 +1,11 @@
import React, {useEffect, useState} from 'react' import React, {useEffect, useState} from 'react'
import useApiUrlStore from '../../app/store/apiStore' import useApiUrlStore from '../../app/store/apiStore'
import { import {DataClient, type PlatformSummaryViewModel, type TopStrategiesViewModel} from '../../generated/ManagingApi'
DataClient,
type PlatformSummaryViewModel,
type StrategiesStatisticsViewModel,
type TopStrategiesViewModel
} from '../../generated/ManagingApi'
function PlatformSummary({ index }: { index: number }) { function PlatformSummary({ index }: { index: number }) {
const { apiUrl } = useApiUrlStore() const { apiUrl } = useApiUrlStore()
const [platformData, setPlatformData] = useState<PlatformSummaryViewModel | null>(null) const [platformData, setPlatformData] = useState<PlatformSummaryViewModel | null>(null)
const [strategiesStats, setStrategiesStats] = useState<StrategiesStatisticsViewModel | null>(null)
const [topStrategies, setTopStrategies] = useState<TopStrategiesViewModel | null>(null) const [topStrategies, setTopStrategies] = useState<TopStrategiesViewModel | null>(null)
const [isLoading, setIsLoading] = useState(true) const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
@@ -23,14 +18,12 @@ function PlatformSummary({ index }: { index: number }) {
const client = new DataClient({}, apiUrl) const client = new DataClient({}, apiUrl)
// Fetch all platform data in parallel // Fetch all platform data in parallel
const [platform, strategies, top] = await Promise.all([ const [platform, top] = await Promise.all([
client.data_GetPlatformSummary(), client.data_GetPlatformSummary(),
client.data_GetStrategiesStatistics(),
client.data_GetTopStrategies() client.data_GetTopStrategies()
]) ])
setPlatformData(platform) setPlatformData(platform)
setStrategiesStats(strategies)
setTopStrategies(top) setTopStrategies(top)
} catch (err) { } catch (err) {
setError('Failed to fetch platform data') setError('Failed to fetch platform data')
@@ -112,10 +105,10 @@ function PlatformSummary({ index }: { index: number }) {
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl font-bold text-white mb-2"> <h1 className="text-4xl font-bold text-white mb-2">
{formatNumber(strategiesStats?.totalStrategiesRunning || 0)} Strategies Deployed {formatNumber(platformData?.totalActiveStrategies || 0)} Strategies Deployed
</h1> </h1>
<div className="text-lg"> <div className="text-lg">
{strategiesStats && formatChangeIndicator(strategiesStats.changeInLast24Hours || 0)} {platformData && formatChangeIndicator(platformData.strategiesChange24h || 0)}
</div> </div>
</div> </div>