обновление математики для лонгов

main
vlad zverzhkhovskiy 2025-08-28 02:58:27 +03:00
parent 3919174996
commit d9781ee4e2
11 changed files with 271 additions and 104 deletions

View File

@ -1,7 +1,6 @@
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Declisions.Models;
using KLHZ.Trader.Core.Declisions.Utils;
using Tinkoff.InvestApi.V1;
namespace KLHZ.Trader.Core.Tests
{

View File

@ -1,10 +1,5 @@
using KLHZ.Trader.Core.Common.Messaging.Contracts.Messages;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace KLHZ.Trader.Core.DataLayer.Entities.Prices
{

View File

@ -2,7 +2,8 @@
{
public readonly struct PeriodPricesInfo
{
public readonly int Count;
public readonly int Start;
public readonly int End;
public readonly float LastPrice;
public readonly float FirstPrice;
public readonly float PeriodDiff;
@ -11,7 +12,7 @@
public readonly bool Success;
public readonly TimeSpan Period;
public PeriodPricesInfo(bool success, float firstPrice, float lastPrice, float periodDiff, float periodMin, float periodMax, TimeSpan period, int count)
public PeriodPricesInfo(bool success, float firstPrice, float lastPrice, float periodDiff, float periodMin, float periodMax, TimeSpan period, int start, int end)
{
Success = success;
LastPrice = lastPrice;
@ -20,7 +21,8 @@
PeriodMax = periodMax;
PeriodMin = periodMin;
Period = period;
Count = count;
Start = start;
End = end;
}
}
}

View File

@ -0,0 +1,27 @@
namespace KLHZ.Trader.Core.Declisions.Models
{
public readonly struct TwoPeriodsProcessingData
{
public readonly int Start;
public readonly int Bound;
public readonly int End;
public readonly float DiffStart;
public readonly float DiffEnd;
public readonly bool Success;
public readonly TimeSpan PeriodStart;
public readonly TimeSpan PeriodEnd;
public TwoPeriodsProcessingData(bool success, float diffStart, float diffEnd, int start, int bound, int end,
TimeSpan periodStart, TimeSpan periodEnd)
{
Success = success;
DiffStart = diffStart;
DiffEnd = diffEnd;
Start = start;
Bound = bound;
End = end;
PeriodStart = periodStart;
PeriodEnd = periodEnd;
}
}
}

View File

@ -106,11 +106,11 @@ namespace KLHZ.Trader.Core.Declisions.Services
float meanfullDiff;
if (message.Figi == "BBG004730N88")
{
meanfullDiff = 0.16f;
meanfullDiff = 0.05f;
}
else if (message.Figi == "FUTIMOEXF000")
{
meanfullDiff = 1.5f;
meanfullDiff = 1f;
}
else
{
@ -119,30 +119,37 @@ namespace KLHZ.Trader.Core.Declisions.Services
try
{
var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff);
var uptrendStarts = data.CheckUptrendStarting(TimeSpan.FromSeconds(45), TimeSpan.FromSeconds(10), meanfullDiff);
//var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff);
var uptrendStarts = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(7), meanfullDiff, 8, 3);
var uptrendStarts2 = data.CheckLongOpen(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(3), meanfullDiff, 15, 2);
var downtrendEnds = data.CheckLongOpen(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(10), meanfullDiff, 15, 5);
uptrendStarts |= downtrendEnds;
uptrendStarts |= uptrendStarts2;
//var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff);
var uptrendEnds = data.CheckLongClose(TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(20), meanfullDiff * 1.5f, 8, 8);
var uptrendEnds2 = data.CheckLongClose(TimeSpan.FromSeconds(120), TimeSpan.FromSeconds(30), meanfullDiff, 15, 8);
uptrendEnds |= uptrendEnds2;
var downtrendEnds = data.CheckDowntrendEnding(TimeSpan.FromSeconds(60), TimeSpan.FromSeconds(15), meanfullDiff);
var uptrendEnds = data.CheckUptrendEnding(TimeSpan.FromSeconds(25), TimeSpan.FromSeconds(11), meanfullDiff);
//var uptrendEnds2 = data.CheckUptrendEnding(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(20), meanfullDiff);
//var uptrendEnds = uptrendEnds1 || uptrendEnds2;
var declisionAction = DeclisionTradeAction.Unknown;
if (downtrendStarts)
//if (downtrendStarts)
//{
// //declisionAction = DeclisionTradeAction.OpenShort;
//}
if (uptrendStarts)
{
//declisionAction = DeclisionTradeAction.OpenShort;
declisionAction = DeclisionTradeAction.OpenLong;
}
else if (uptrendStarts)
{
//declisionAction = DeclisionTradeAction.OpenLong;
}
else if (downtrendEnds)
{
//declisionAction = DeclisionTradeAction.CloseShort;
}
else if(uptrendEnds)
//else if (downtrendEnds)
//{
// //declisionAction = DeclisionTradeAction.CloseShort;
//}
else if (uptrendEnds)
{
declisionAction = DeclisionTradeAction.CloseLong;
}
@ -157,7 +164,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
Figi = message.Figi,
Ticker = message.Ticker,
Price = message.Value,
Time = message.IsHistoricalData? message.Time: DateTime.UtcNow,
Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow,
Action = declisionAction,
});
await context.SaveChangesAsync();

View File

@ -6,7 +6,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
{
internal static PeriodPricesInfo GetPriceDiffForTimeSpan(this PriceHistoryCacheUnit unit, TimeSpan timeShift, TimeSpan timeSpan, int? pointsShift = null)
{
var res = new PeriodPricesInfo(false, 0, 0, 0, 0, 0, timeSpan, 0);
var res = new PeriodPricesInfo(false, 0, 0, 0, 0, 0, timeSpan, 0, 0);
var data = unit.GetData();
var times = data.timestamps;
var prices = data.prices;
@ -42,7 +42,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
if (intervaStartIndex != intervaEndIndex && intervaEndIndex >= 0)
break;
}
count++;
count++;
}
if (intervaStartIndex >= 0 && intervaEndIndex >= 0)
@ -54,7 +54,9 @@ namespace KLHZ.Trader.Core.Declisions.Utils
prices[intervaEndIndex] - prices[intervaStartIndex],
min,
max,
timeSpan, intervaEndIndex - intervaStartIndex);
timeSpan,
intervaStartIndex,
intervaEndIndex);
}
return res;
@ -84,6 +86,13 @@ namespace KLHZ.Trader.Core.Declisions.Utils
if (k2 == 0 && k1 != 0) return 1000;
return (float)(k1 / k2);
}
internal static float CalcTrendRelationAbs(TwoPeriodsProcessingData data)
{
var k1 = Math.Abs(data.DiffStart) / Math.Abs(data.PeriodStart.TotalSeconds);
var k2 = Math.Abs(data.DiffEnd) / Math.Abs(data.PeriodEnd.TotalSeconds);
if (k2 == 0 && k1 != 0) return 1000;
return (float)(k1 / k2);
}
internal static bool CheckDowntrendEnding(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff)
{
@ -96,7 +105,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
var isTotalFalls = totalDiff.CheckFalling(meanfullDiff);
var trendRelation = CalcTrendRelationAbs(startDiff, endDiff);
var res = totalDiff.Success && isStartFalls && (isEndStable || isEndGrown) && trendRelation >= 2;
var res = totalDiff.Success && isStartFalls && (isEndStable || isEndGrown) && trendRelation >= 2;
if (startDiff.Success)
{
@ -115,7 +124,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
var isStartGrows = startDiff.CheckGrowing(meanfullDiff);
var trendRelation = CalcTrendRelationAbs(startDiff, endDiff);
var isEndLocal = endDiff.PeriodDiff == 0 && endDiff.Count == 2;
var isEndLocal = endDiff.PeriodDiff == 0;
var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && (trendRelation >= 2 && !isEndLocal);
if (res)
@ -168,5 +177,143 @@ namespace KLHZ.Trader.Core.Declisions.Utils
}
return res;
}
internal static TwoPeriodsProcessingData GetTwoPeriodsProcessingData(this (DateTime[] timestamps, float[] prices) data, TimeSpan shift, int shiftPointsStart, int shiftPointsEnd, TimeSpan firstPeriod, float meanfullDiff)
{
var res = new TwoPeriodsProcessingData(success: false, 0, 0, 0, 0, 0, TimeSpan.Zero, TimeSpan.Zero);
var time = data.timestamps;
var prices = data.prices;
int count = -1;
var lastTime = time[time.Length - 1];
var bound = -1;
var start = -1;
var end = time.Length - 1;
for (int i = time.Length - 1; i > -1; i--)
{
if (count > 0 && bound < 0 && (count == shiftPointsEnd || lastTime - time[i] >= shift))
{
bound = i;
shift = lastTime - time[i];
}
if (((lastTime - time[i]) >= shift + firstPeriod))
{
start = i;
break;
}
count++;
}
if (start < bound && start >= 0 && bound > 0)
{
var diff1 = prices[bound] - prices[start];
var diff2 = prices[end] - prices[bound];
res = new TwoPeriodsProcessingData(true, diff1, diff2, start, bound, end, time[bound] - time[start], time[end] - time[bound]);
}
return res;
}
internal static bool CheckLongClose(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd)
{
var data = unit.GetData();
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff);
var trendRelation = CalcTrendRelationAbs(periodStat);
var isStartOk = periodStat.Success && periodStat.DiffStart > 0 && periodStat.DiffStart > 1.5 * meanfullDiff;
var isEndOk = periodStat.Success && periodStat.DiffEnd < meanfullDiff;
if (isEndOk)
{
}
if (isStartOk)
{
}
if (isEndOk && isStartOk)
{
}
return isStartOk && isEndOk && (data.prices[periodStat.End] - data.prices[periodStat.Start] >= meanfullDiff);
}
internal static bool CheckUptrendStarting2(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff)
{
var data = unit.GetData();
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, 15, 2, firstPeriod, meanfullDiff);
var trendRelation = CalcTrendRelationAbs(periodStat);
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
if (isEndOk)
{
}
if (isStartOk)
{
}
if (isEndOk && isStartOk)
{
}
return isStartOk && isEndOk;
}
internal static bool _CheckUptrendStarting2(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff)
{
var data = unit.GetData();
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, 15, 1, firstPeriod, meanfullDiff);
var trendRelation = CalcTrendRelationAbs(periodStat);
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
if (isEndOk)
{
}
if (isStartOk)
{
}
if (isEndOk && isStartOk)
{
}
return isStartOk && isEndOk;
}
internal static bool CheckLongOpen(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff, int pointsStart, int pointsEnd)
{
var data = unit.GetData();
var periodStat = data.GetTwoPeriodsProcessingData(secondPeriod, pointsStart, pointsEnd, firstPeriod, meanfullDiff);
var trendRelation = CalcTrendRelationAbs(periodStat);
var isStartOk = periodStat.Success && periodStat.DiffStart < -meanfullDiff;
var isEndOk = periodStat.Success && periodStat.DiffEnd >= meanfullDiff;
if (isEndOk)
{
}
if (isStartOk)
{
}
if (isEndOk && isStartOk)
{
}
return isStartOk && isEndOk && (data.prices[periodStat.Start] - data.prices[periodStat.End] >= meanfullDiff);
}
}
}

View File

@ -72,6 +72,7 @@ namespace KLHZ.Trader.Core.Exchange.Services
try
{
await SubscribePrices();
await Task.Delay(1000);
//await SubscribeCandles();
}
catch (Exception ex)

View File

@ -1,11 +1,7 @@
using Google.Protobuf.WellKnownTypes;
using KLHZ.Trader.Core.Common.Messaging.Contracts;
using KLHZ.Trader.Core.Common.Messaging.Contracts.Messages;
using KLHZ.Trader.Core.DataLayer;
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Tinkoff.InvestApi;
using Tinkoff.InvestApi.V1;
using Candle = KLHZ.Trader.Core.DataLayer.Entities.Prices.Candle;

View File

@ -4,4 +4,3 @@ scrape_configs:
scrape_interval: 5s
static_configs:
- targets: ['klhz_trader:8080','gateway.docker.internal:9100']

View File

@ -1,14 +1,8 @@
using Google.Protobuf.WellKnownTypes;
using KLHZ.Trader.Core.Common.Messaging.Contracts;
using KLHZ.Trader.Core.Common.Messaging.Contracts.Messages;
using KLHZ.Trader.Core.DataLayer;
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Tinkoff.InvestApi;
using Tinkoff.InvestApi.V1;
using Candle = KLHZ.Trader.Core.DataLayer.Entities.Prices.Candle;
namespace KLHZ.Trader.Service.Controllers
{
@ -30,15 +24,15 @@ namespace KLHZ.Trader.Service.Controllers
{
using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.Candles
var data = await context1.PriceChanges
.Where(c => c.Figi == figi)
.OrderBy(c=>c.Time)
.OrderBy(c => c.Time)
.Select(c => new NewPriceMessage()
{
Figi = figi,
Ticker = c.Ticker,
Time = c.Time,
Value = c.Close,
Value = c.Value,
IsHistoricalData = true
})
.ToArrayAsync();

View File

@ -1,7 +1,7 @@
services:
klhz.trader.service:
restart: always
image: klhz_trader
image: klhz_trader_prod
container_name: klhz_trader
hostname: klhz_trader
ports:
@ -15,63 +15,63 @@ services:
ExchangeConfig__Token: "${EXCHANGE_API_TOKEN}"
ConnectionStrings__PostgresConnection: "${PG_CONNECTION_STRING}"
postgresql:
ports:
- 15433:5432
container_name: debug_postgresql_16
hostname: debug_postgresql_16
image: postgres:16
restart: always
command:
- "postgres"
- "-c"
- "max_connections=100"
- "-c"
- "shared_buffers=512MB"
- "-c"
- "temp_buffers=64MB"
- "-c"
- "log_statement=all"
environment:
POSTGRES_PASSWORD: "${PG_PWD}"
POSTGRES_DB: trading
volumes:
- traderdata:/var/lib/postgresql/data
# postgresql:
# ports:
# - 15433:5432
# container_name: debug_postgresql_16
# hostname: debug_postgresql_16
# image: postgres:16
# restart: always
# command:
# - "postgres"
# - "-c"
# - "max_connections=100"
# - "-c"
# - "shared_buffers=512MB"
# - "-c"
# - "temp_buffers=64MB"
# - "-c"
# - "log_statement=all"
# environment:
# POSTGRES_PASSWORD: "${PG_PWD}"
# POSTGRES_DB: trading
# volumes:
# - traderdata:/var/lib/postgresql/data
prometheus:
hostname: prometheus
image: prom/prometheus
container_name: prometheus
ports:
- 9191:9090
restart: always
volumes:
- ./KLHZ.Trader.Infrastructure/prometheus/:/etc/prometheus/
- prom_data:/prometheus
# prometheus:
# hostname: prometheus
# image: prom/prometheus
# container_name: prometheus
# ports:
# - 9191:9090
# restart: always
# volumes:
# - ./KLHZ.Trader.Infrastructure/prometheus/:/etc/prometheus/
# - prom_data:/prometheus
grafana:
image: grafana/grafana
container_name: grafana
ports:
- 1300:3000
restart: always
environment:
GF_SECURITY_ADMIN_USER: "${GF_SECURITY_ADMIN_USER}"
GF_SECURITY_ADMIN_PASSWORD: "${GF_SECURITY_ADMIN_PASSWORD}"
volumes:
- graphana:/etc/grafana/provisioning/datasources
# grafana:
# image: grafana/grafana
# container_name: grafana
# ports:
# - 1300:3000
# restart: always
# environment:
# GF_SECURITY_ADMIN_USER: "${GF_SECURITY_ADMIN_USER}"
# GF_SECURITY_ADMIN_PASSWORD: "${GF_SECURITY_ADMIN_PASSWORD}"
# volumes:
# - graphana:/etc/grafana/provisioning/datasources
loki:
hostname: loki
image: grafana/loki:latest
container_name: loki
restart: always
ports:
- "2300:3100"
volumes:
- ./KLHZ.Trader.Infrastructure/loki/loki-config.yaml:/etc/loki/local-config.yaml
- loki_data:/loki
command: -config.file=/etc/loki/local-config.yaml
# loki:
# hostname: loki
# image: grafana/loki:latest
# container_name: loki
# restart: always
# ports:
# - "2300:3100"
# volumes:
# - ./KLHZ.Trader.Infrastructure/loki/loki-config.yaml:/etc/loki/local-config.yaml
# - loki_data:/loki
# command: -config.file=/etc/loki/local-config.yaml
klhz.trader.historyloader:
image: klhztraderhistoryloader