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

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.DataLayer.Entities.Prices;
using KLHZ.Trader.Core.Declisions.Models; using KLHZ.Trader.Core.Declisions.Models;
using KLHZ.Trader.Core.Declisions.Utils; using KLHZ.Trader.Core.Declisions.Utils;
using Tinkoff.InvestApi.V1;
namespace KLHZ.Trader.Core.Tests namespace KLHZ.Trader.Core.Tests
{ {

View File

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

View File

@ -2,7 +2,8 @@
{ {
public readonly struct PeriodPricesInfo public readonly struct PeriodPricesInfo
{ {
public readonly int Count; public readonly int Start;
public readonly int End;
public readonly float LastPrice; public readonly float LastPrice;
public readonly float FirstPrice; public readonly float FirstPrice;
public readonly float PeriodDiff; public readonly float PeriodDiff;
@ -11,7 +12,7 @@
public readonly bool Success; public readonly bool Success;
public readonly TimeSpan Period; 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; Success = success;
LastPrice = lastPrice; LastPrice = lastPrice;
@ -20,7 +21,8 @@
PeriodMax = periodMax; PeriodMax = periodMax;
PeriodMin = periodMin; PeriodMin = periodMin;
Period = period; 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; float meanfullDiff;
if (message.Figi == "BBG004730N88") if (message.Figi == "BBG004730N88")
{ {
meanfullDiff = 0.16f; meanfullDiff = 0.05f;
} }
else if (message.Figi == "FUTIMOEXF000") else if (message.Figi == "FUTIMOEXF000")
{ {
meanfullDiff = 1.5f; meanfullDiff = 1f;
} }
else else
{ {
@ -119,30 +119,37 @@ namespace KLHZ.Trader.Core.Declisions.Services
try try
{ {
var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff); //var downtrendStarts = data.CheckDowntrendStarting(TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(7), meanfullDiff);
var uptrendStarts = data.CheckUptrendStarting(TimeSpan.FromSeconds(45), TimeSpan.FromSeconds(10), 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 uptrendEnds2 = data.CheckUptrendEnding(TimeSpan.FromSeconds(20), TimeSpan.FromSeconds(20), meanfullDiff);
//var uptrendEnds = uptrendEnds1 || uptrendEnds2; //var uptrendEnds = uptrendEnds1 || uptrendEnds2;
var declisionAction = DeclisionTradeAction.Unknown; var declisionAction = DeclisionTradeAction.Unknown;
if (downtrendStarts) //if (downtrendStarts)
//{
// //declisionAction = DeclisionTradeAction.OpenShort;
//}
if (uptrendStarts)
{ {
//declisionAction = DeclisionTradeAction.OpenShort; declisionAction = DeclisionTradeAction.OpenLong;
} }
else if (uptrendStarts) //else if (downtrendEnds)
{ //{
//declisionAction = DeclisionTradeAction.OpenLong; // //declisionAction = DeclisionTradeAction.CloseShort;
} //}
else if (downtrendEnds) else if (uptrendEnds)
{
//declisionAction = DeclisionTradeAction.CloseShort;
}
else if(uptrendEnds)
{ {
declisionAction = DeclisionTradeAction.CloseLong; declisionAction = DeclisionTradeAction.CloseLong;
} }
@ -157,7 +164,7 @@ namespace KLHZ.Trader.Core.Declisions.Services
Figi = message.Figi, Figi = message.Figi,
Ticker = message.Ticker, Ticker = message.Ticker,
Price = message.Value, Price = message.Value,
Time = message.IsHistoricalData? message.Time: DateTime.UtcNow, Time = message.IsHistoricalData ? message.Time : DateTime.UtcNow,
Action = declisionAction, Action = declisionAction,
}); });
await context.SaveChangesAsync(); 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) 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 data = unit.GetData();
var times = data.timestamps; var times = data.timestamps;
var prices = data.prices; var prices = data.prices;
@ -42,7 +42,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
if (intervaStartIndex != intervaEndIndex && intervaEndIndex >= 0) if (intervaStartIndex != intervaEndIndex && intervaEndIndex >= 0)
break; break;
} }
count++; count++;
} }
if (intervaStartIndex >= 0 && intervaEndIndex >= 0) if (intervaStartIndex >= 0 && intervaEndIndex >= 0)
@ -54,7 +54,9 @@ namespace KLHZ.Trader.Core.Declisions.Utils
prices[intervaEndIndex] - prices[intervaStartIndex], prices[intervaEndIndex] - prices[intervaStartIndex],
min, min,
max, max,
timeSpan, intervaEndIndex - intervaStartIndex); timeSpan,
intervaStartIndex,
intervaEndIndex);
} }
return res; return res;
@ -84,6 +86,13 @@ namespace KLHZ.Trader.Core.Declisions.Utils
if (k2 == 0 && k1 != 0) return 1000; if (k2 == 0 && k1 != 0) return 1000;
return (float)(k1 / k2); 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) internal static bool CheckDowntrendEnding(this PriceHistoryCacheUnit unit, TimeSpan firstPeriod, TimeSpan secondPeriod, float meanfullDiff)
{ {
@ -96,8 +105,8 @@ namespace KLHZ.Trader.Core.Declisions.Utils
var isTotalFalls = totalDiff.CheckFalling(meanfullDiff); var isTotalFalls = totalDiff.CheckFalling(meanfullDiff);
var trendRelation = CalcTrendRelationAbs(startDiff, endDiff); 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) if (startDiff.Success)
{ {
@ -115,7 +124,7 @@ namespace KLHZ.Trader.Core.Declisions.Utils
var isStartGrows = startDiff.CheckGrowing(meanfullDiff); var isStartGrows = startDiff.CheckGrowing(meanfullDiff);
var trendRelation = CalcTrendRelationAbs(startDiff, endDiff); 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); var res = totalDiff.Success && isStartGrows && (isEndStable || isEndFalls) && (trendRelation >= 2 && !isEndLocal);
if (res) if (res)
@ -153,14 +162,14 @@ namespace KLHZ.Trader.Core.Declisions.Utils
var trendRelation = CalcTrendRelationAbs(endDiff, startDiff); var trendRelation = CalcTrendRelationAbs(endDiff, startDiff);
var res = totalDiff.Success && (isStartStable || isStartFalls) && isEndGrows && endDiff.PeriodDiff > meanfullDiff; var res = totalDiff.Success && (isStartStable || isStartFalls) && isEndGrows && endDiff.PeriodDiff > meanfullDiff;
if (isStartStable) if (isStartStable)
{ {
res &= trendRelation >= 2; res &= trendRelation >= 2;
} }
else else
{ {
} }
if (res) if (res)
{ {
@ -168,5 +177,143 @@ namespace KLHZ.Trader.Core.Declisions.Utils
} }
return res; 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 try
{ {
await SubscribePrices(); await SubscribePrices();
await Task.Delay(1000);
//await SubscribeCandles(); //await SubscribeCandles();
} }
catch (Exception ex) catch (Exception ex)

View File

@ -1,11 +1,7 @@
using Google.Protobuf.WellKnownTypes; 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;
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Tinkoff.InvestApi; using Tinkoff.InvestApi;
using Tinkoff.InvestApi.V1; using Tinkoff.InvestApi.V1;
using Candle = KLHZ.Trader.Core.DataLayer.Entities.Prices.Candle; using Candle = KLHZ.Trader.Core.DataLayer.Entities.Prices.Candle;

View File

@ -4,4 +4,3 @@ scrape_configs:
scrape_interval: 5s scrape_interval: 5s
static_configs: static_configs:
- targets: ['klhz_trader:8080','gateway.docker.internal:9100'] - 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;
using KLHZ.Trader.Core.Common.Messaging.Contracts.Messages; using KLHZ.Trader.Core.Common.Messaging.Contracts.Messages;
using KLHZ.Trader.Core.DataLayer; using KLHZ.Trader.Core.DataLayer;
using KLHZ.Trader.Core.DataLayer.Entities.Prices;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; 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 namespace KLHZ.Trader.Service.Controllers
{ {
@ -30,15 +24,15 @@ namespace KLHZ.Trader.Service.Controllers
{ {
using var context1 = await _dbContextFactory.CreateDbContextAsync(); using var context1 = await _dbContextFactory.CreateDbContextAsync();
context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking; context1.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
var data = await context1.Candles var data = await context1.PriceChanges
.Where(c => c.Figi == figi) .Where(c => c.Figi == figi)
.OrderBy(c=>c.Time) .OrderBy(c => c.Time)
.Select(c => new NewPriceMessage() .Select(c => new NewPriceMessage()
{ {
Figi = figi, Figi = figi,
Ticker = c.Ticker, Ticker = c.Ticker,
Time = c.Time, Time = c.Time,
Value = c.Close, Value = c.Value,
IsHistoricalData = true IsHistoricalData = true
}) })
.ToArrayAsync(); .ToArrayAsync();

View File

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