13 Commits

Author SHA1 Message Date
renovate[bot] 57e723db65 Update module google.golang.org/grpc to v1.82.0
Deploy / Test, build and deploy (push) Successful in 2m26s
2026-07-01 00:06:49 +00:00
Valentin Popov f46ec2abde fix: restore backoff module requirement
Deploy / Test, build and deploy (push) Successful in 2m28s
2026-06-27 03:00:11 +04:00
Valentin Popov a055e0606f feat: seed active T-Capital fund whitelist
Deploy / Test, build and deploy (push) Failing after 52s
2026-06-27 01:55:03 +04:00
Valentin Popov 6c2f868d21 Merge pull request 'Update module github.com/cenkalti/backoff/v4 to v6' (!6) from renovate/github.com-cenkalti-backoff-v4-6.x into master
Deploy / Test, build and deploy (push) Failing after 48s
Reviewed-on: #6
2026-06-20 15:13:57 +04:00
Valentin Popov e8f25d06e7 Merge pull request 'Update actions/checkout action to v7' (!5) from renovate/actions-checkout-7.x into master
Deploy / Test, build and deploy (push) Successful in 57s
Reviewed-on: #5
2026-06-20 15:13:44 +04:00
renovate[bot] 4dfc2fe263 Update all digest updates to v0.43.0
Deploy / Test, build and deploy (push) Successful in 2m15s
2026-06-20 00:01:43 +00:00
renovate[bot] 6bd6585c1f Update module github.com/cenkalti/backoff/v4 to v6 2026-06-19 00:01:46 +00:00
renovate[bot] b7c13c536f Update actions/checkout action to v7 2026-06-19 00:01:07 +00:00
Valentin Popov 55e897909d Handle server time outages as soft pauses
Deploy / Test, build and deploy (push) Successful in 2m14s
2026-06-18 05:06:58 +04:00
renovate[bot] 7baa7395af Update module github.com/cenkalti/backoff/v4 to v6
Deploy / Test, build and deploy (push) Failing after 2m22s
2026-06-17 00:02:14 +00:00
Valentin Popov 4b37dc31a3 chore(deps): update go dependencies
Deploy / Test, build and deploy (push) Failing after 5m53s
2026-06-16 07:10:58 +00:00
Valentin Popov e693de933f Merge pull request 'Update module github.com/cenkalti/backoff/v4 to v5' (!3) from renovate/github.com-cenkalti-backoff-v4-5.x into master
Deploy / Test, build and deploy (push) Failing after 32s
Reviewed-on: #3
2026-06-16 02:49:58 +04:00
renovate[bot] 3b68d24677 Update module github.com/cenkalti/backoff/v4 to v5 2026-06-10 00:03:10 +00:00
9 changed files with 196 additions and 31 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Setup Go
uses: actions/setup-go@v6
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v6
uses: actions/checkout@v7
- name: Run Renovate
run: |
+10 -11
View File
@@ -5,16 +5,15 @@ go 1.26.2
require (
github.com/caarlos0/env/v11 v11.4.1
github.com/cenkalti/backoff/v4 v4.3.0
github.com/cenkalti/backoff/v5 v5.0.3
github.com/go-sql-driver/mysql v1.10.0
github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
github.com/golang-migrate/migrate/v4 v4.19.1
github.com/jmoiron/sqlx v1.4.0
github.com/russianinvestments/invest-api-go-sdk v1.40.1
github.com/shopspring/decimal v1.4.0
github.com/testcontainers/testcontainers-go v0.42.0
github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0
google.golang.org/grpc v1.81.1
github.com/testcontainers/testcontainers-go v0.43.0
github.com/testcontainers/testcontainers-go/modules/mariadb v0.43.0
google.golang.org/grpc v1.82.0
google.golang.org/protobuf v1.36.11
)
@@ -46,7 +45,7 @@ require (
github.com/magiconair/properties v1.8.10 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.2.0 // indirect
github.com/moby/moby/api v1.54.1 // indirect
github.com/moby/moby/api v1.54.2 // indirect
github.com/moby/moby/client v0.4.0 // indirect
github.com/moby/patternmatcher v0.6.1 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
@@ -57,7 +56,7 @@ require (
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
github.com/shirou/gopsutil/v4 v4.26.5 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
@@ -68,12 +67,12 @@ require (
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
golang.org/x/crypto v0.51.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/text v0.37.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
golang.org/x/text v0.38.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260615183401-62b3387ff324 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+23 -13
View File
@@ -15,7 +15,7 @@ github.com/caarlos0/env/v11 v11.4.1 h1:fYwH0sWEsBSMPG7t4e/PEfTFzrWrpjyygXyUnWiSw
github.com/caarlos0/env/v11 v11.4.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cenkalti/backoff/v6 v6.0.1/go.mod h1:5WCmPelT2zwAaNETjGJVKHDnZvjQdPsGeHHwm5lIPPI=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -93,6 +93,8 @@ github.com/moby/go-archive v0.2.0 h1:zg5QDUM2mi0JIM9fdQZWC7U8+2ZfixfTYoHL7rWUcP8
github.com/moby/go-archive v0.2.0/go.mod h1:mNeivT14o8xU+5q1YnNrkQVpK+dnNe/K6fHqnTg4qPU=
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
@@ -123,6 +125,8 @@ github.com/russianinvestments/invest-api-go-sdk v1.40.1 h1:EZ9mA5fTlyspH8urdAMFX
github.com/russianinvestments/invest-api-go-sdk v1.40.1/go.mod h1:rOu2P3GMTQEkQxRpQfp+wK5k71c3SUDHIke3Ijr8cOU=
github.com/shirou/gopsutil/v4 v4.26.3 h1:2ESdQt90yU3oXF/CdOlRCJxrP+Am1aBYubTMTfxJ1qc=
github.com/shirou/gopsutil/v4 v4.26.3/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/shirou/gopsutil/v4 v4.26.5 h1:RPcBXkpz7kOj9PqGFQOlBPZHsyaPvPVQc098y9RmCNM=
github.com/shirou/gopsutil/v4 v4.26.5/go.mod h1:LZ6ewCSkBqUpvSOf+LsTGnRinC6iaNUNMGBtDkJBaLQ=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
@@ -133,8 +137,12 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/testcontainers/testcontainers-go v0.42.0 h1:He3IhTzTZOygSXLJPMX7n44XtK+qhjat1nI9cneBbUY=
github.com/testcontainers/testcontainers-go v0.42.0/go.mod h1:vZjdY1YmUA1qEForxOIOazfsrdyORJAbhi0bp8plN30=
github.com/testcontainers/testcontainers-go v0.43.0 h1:oEQx5MW2DGd9z3AeEQfB2lPM0eLs7ztyaGRu75bFo5A=
github.com/testcontainers/testcontainers-go v0.43.0/go.mod h1:+VxkT2NQnKOZPKi6praMuMKYHYyOGXr0XSBSlSMCzFo=
github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0 h1:ZfWUJSIDnbNgoLAXMV1fc7lqcxBIX3zdnhwjaVUo7N0=
github.com/testcontainers/testcontainers-go/modules/mariadb v0.42.0/go.mod h1:0kV+yHee7zAgp0yccydxjNnHvlC1EOavTLCeg/lnRBY=
github.com/testcontainers/testcontainers-go/modules/mariadb v0.43.0 h1:cvZGnhieICwBODSeoPlqdpNrQpnFA8n0L5/4E591Az4=
github.com/testcontainers/testcontainers-go/modules/mariadb v0.43.0/go.mod h1:GIuI5uvfhjP44sScfPruPjundARJx1RwnzTBwGEI4qY=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
@@ -155,10 +163,10 @@ go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfC
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -166,19 +174,21 @@ golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/api v0.0.0-20260615183401-62b3387ff324 h1:g0RAkxK/smSu/iRwC/KIX1mwUoVJtk2OjbgaeS4DmUM=
google.golang.org/genproto/googleapis/api v0.0.0-20260615183401-62b3387ff324/go.mod h1:Z4WJ5pJOYWFWcHEQUelD5QaZDknIQkpIL/+fyJOT9+A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324 h1:9HZDLIdYBJXAnaFOr9WHrKVycfpY+75s9HGadC0305A=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260615183401-62b3387ff324/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU=
google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -0,0 +1,20 @@
UPDATE instruments
SET enabled=0,
quarantine=1,
quarantine_reason='rollback_tcapital_active_whitelist',
exclude_reason='rollback_tcapital_active_whitelist',
updated_at=UTC_TIMESTAMP(3)
WHERE instrument_uid IN (
'e8acd2fb-6de6-4ea4-9bfb-0daad9b2ed7b',
'd5cba263-cda7-440c-a21d-134fb5d334f6',
'de82be66-3b9b-4612-9572-61e3c6039013',
'2f243f46-34ce-4d50-a931-c6f8a67eb758',
'498ec3ff-ef27-4729-9703-a5aac48d5789',
'f509af83-6e71-462f-901f-bcb073f6773b',
'c5049184-ded4-49d0-8e14-bffefc40a223',
'4597c92e-128c-44de-abd2-a1d88d163b0c',
'5293ef3c-37bb-4d6f-8d43-802c57560881',
'd16d8124-ce0c-4869-9efb-98700332feab'
);
UPDATE schema_meta SET meta_value='0011' WHERE meta_key='schema_version';
@@ -0,0 +1,43 @@
INSERT INTO instruments (
instrument_uid, figi, ticker, class_code, name, lot, min_price_increment, currency,
enabled, fund_type, expected_commission_bps_per_side, free_order_limit_per_day,
quarantine, quarantine_reason, exclude_reason, updated_at
) VALUES
('e8acd2fb-6de6-4ea4-9bfb-0daad9b2ed7b', 'TCS60A1039N1', 'TBRU@', 'SPBRU', 'Российские облигации', 1, 0.01, 'RUB', 1, 'bonds', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('d5cba263-cda7-440c-a21d-134fb5d334f6', 'TCS10A107563', 'TDIV@', 'SPBRU', 'Дивидендные акции', 1, 0.01, 'RUB', 1, 'equity', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('de82be66-3b9b-4612-9572-61e3c6039013', 'TCS80A101X50', 'TGLD@', 'SPBRU', 'Золото', 1, 0.01, 'RUB', 1, 'commodity', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('2f243f46-34ce-4d50-a931-c6f8a67eb758', 'TCS20A107597', 'TLCB@', 'SPBRU', 'Локальные валютные облигации', 1, 0.01, 'RUB', 1, 'bonds', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('498ec3ff-ef27-4729-9703-a5aac48d5789', 'TCS70A106DL2', 'TMON@', 'SPBRU', 'Денежный рынок', 1, 0.01, 'RUB', 1, 'money_market', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('f509af83-6e71-462f-901f-bcb073f6773b', 'TCS60A101X76', 'TMOS@', 'SPBRU', 'Крупнейшие компании РФ', 1, 0.01, 'RUB', 1, 'equity', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('c5049184-ded4-49d0-8e14-bffefc40a223', 'TCS70A10A1L8', 'TOFZ@', 'SPBRU', 'Т-Капитал ОФЗ', 1, 0.01, 'RUB', 1, 'bonds', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('4597c92e-128c-44de-abd2-a1d88d163b0c', 'TCSM25708WX3', 'TPAY', 'TQBR', 'Пассивный доход', 1, 0.01, 'RUB', 1, 'bonds', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('5293ef3c-37bb-4d6f-8d43-802c57560881', 'TCS20A10B0G9', 'TRND@', 'SPBRU', 'Трендовые акции', 1, 0.01, 'RUB', 1, 'equity', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3)),
('d16d8124-ce0c-4869-9efb-98700332feab', 'TCS60A1011U5', 'TRUR@', 'SPBRU', 'Вечный портфель', 1, 0.01, 'RUB', 1, 'mixed', 0, 15, 0, NULL, NULL, UTC_TIMESTAMP(3))
ON DUPLICATE KEY UPDATE
figi=VALUES(figi),
ticker=VALUES(ticker),
class_code=VALUES(class_code),
name=VALUES(name),
lot=VALUES(lot),
min_price_increment=VALUES(min_price_increment),
currency=VALUES(currency),
enabled=VALUES(enabled),
fund_type=VALUES(fund_type),
expected_commission_bps_per_side=VALUES(expected_commission_bps_per_side),
free_order_limit_per_day=VALUES(free_order_limit_per_day),
quarantine=VALUES(quarantine),
quarantine_reason=VALUES(quarantine_reason),
exclude_reason=VALUES(exclude_reason),
updated_at=UTC_TIMESTAMP(3);
INSERT INTO risk_events (ts, severity, event_type, instrument_uid, message, raw_context_json)
VALUES (
UTC_TIMESTAMP(3),
'INFO',
'tcapital_whitelist_seeded',
NULL,
'Seeded verified T-Capital ETF whitelist for monitoring',
'{"tickers":["TBRU@","TDIV@","TGLD@","TLCB@","TMON@","TMOS@","TOFZ@","TPAY","TRND@","TRUR@"],"free_order_limit_per_day":15}'
);
UPDATE schema_meta SET meta_value='0012' WHERE meta_key='schema_version';
+6 -5
View File
@@ -1061,7 +1061,7 @@ func (s *Scheduler) recordInfrastructureFailure(ctx context.Context, err error)
now := s.nowUTC()
if s.infraFailedSince.IsZero() {
s.infraFailedSince = now
s.logWarn("infrastructure check failed; waiting for outage threshold", "err", err, "threshold", s.cfg.APIOutageHalt)
s.logWarn("infrastructure check failed; pausing scheduler step", "err", err, "threshold", s.cfg.APIOutageHalt)
if s.svc.Repo != nil {
if insertErr := s.svc.Repo.InsertRiskEvent(ctx, domain.RiskEvent{
TS: now,
@@ -1075,10 +1075,12 @@ func (s *Scheduler) recordInfrastructureFailure(ctx context.Context, err error)
}
return nil
}
if s.cfg.APIOutageHalt <= 0 || now.Sub(s.infraFailedSince) >= s.cfg.APIOutageHalt {
return err
elapsed := now.Sub(s.infraFailedSince)
if s.cfg.APIOutageHalt > 0 && elapsed >= s.cfg.APIOutageHalt {
s.logWarn("infrastructure check still failing after outage threshold; continuing soft pause", "err", err, "elapsed", elapsed, "threshold", s.cfg.APIOutageHalt)
return nil
}
s.logWarn("infrastructure check still failing", "err", err, "elapsed", now.Sub(s.infraFailedSince), "threshold", s.cfg.APIOutageHalt)
s.logWarn("infrastructure check still failing; pausing scheduler step", "err", err, "elapsed", elapsed, "threshold", s.cfg.APIOutageHalt)
return nil
}
@@ -1397,7 +1399,6 @@ func (s Scheduler) unknownBrokerState(ctx context.Context, portfolio domain.Port
func isHardHaltPreTradeReason(reason string) bool {
switch reason {
case "database_unavailable",
"server_time_unavailable",
"server_clock_drift_too_high",
"unknown_broker_order",
"unknown_broker_position",
+88
View File
@@ -115,6 +115,55 @@ func TestClockDriftHardLimitHaltsImmediately(t *testing.T) {
}
}
func TestServerTimeUnavailableSoftPausesAfterOutageThreshold(t *testing.T) {
ctx := context.Background()
gateway := tinvest.NewFakeGateway()
gateway.ServerTimeError = context.DeadlineExceeded
repo := testutil.NewMemoryRepository()
now := time.Date(2026, 6, 18, 9, 0, 0, 0, time.UTC)
s := &Scheduler{
clock: fixedClock{now: now},
cfg: Config{
Mode: domain.ModeSandbox,
MaxClockDrift: 2 * time.Second,
APIOutageHalt: 180 * time.Second,
},
svc: Services{
Repo: repo,
Gateway: gateway,
Risk: risk.NewManager(repo, risk.ManagerConfig{}),
Notifier: &countNotifier{},
},
}
if err := s.checkInfrastructure(ctx); err != nil {
t.Fatalf("first infrastructure check err=%v, want soft pause", err)
}
if repo.Halted {
t.Fatalf("system halted on first server time outage: reason=%q", repo.HaltReason)
}
if len(repo.RiskEvents) != 1 || repo.RiskEvents[0].EventType != "infrastructure_outage_started" {
t.Fatalf("risk events=%+v", repo.RiskEvents)
}
s.clock = fixedClock{now: now.Add(5 * time.Minute)}
if err := s.checkInfrastructure(ctx); err != nil {
t.Fatalf("post-threshold infrastructure check err=%v, want soft pause", err)
}
if repo.Halted {
t.Fatalf("system halted after server time outage threshold: reason=%q", repo.HaltReason)
}
gateway.ServerTimeError = nil
gateway.ServerTime = now.Add(5 * time.Minute)
if err := s.checkInfrastructure(ctx); err != nil {
t.Fatalf("recovered infrastructure check err=%v", err)
}
if !s.infraFailedSince.IsZero() {
t.Fatalf("infraFailedSince=%s, want zero after recovery", s.infraFailedSince)
}
}
func TestStepIsIdempotentAfterSignalPreparation(t *testing.T) {
ctx := context.Background()
repo := testutil.NewMemoryRepository()
@@ -602,6 +651,45 @@ func TestPreTradeClockDriftBreachHalts(t *testing.T) {
}
}
func TestPreTradeServerTimeUnavailableRejectsWithoutHalting(t *testing.T) {
ctx := context.Background()
repo := testutil.NewMemoryRepository()
now := time.Date(2026, 6, 8, 18, 20, 0, 0, time.UTC)
gateway := tinvest.NewFakeGateway()
gateway.ServerTimeError = context.DeadlineExceeded
notifier := &countNotifier{}
s := Scheduler{
cfg: Config{
Mode: domain.ModeSandbox,
Location: time.UTC,
MaxClockDrift: 2 * time.Second,
},
svc: Services{
Repo: repo,
Gateway: gateway,
Risk: risk.NewManager(repo, risk.ManagerConfig{}),
Notifier: notifier,
AccountIDHash: "hash",
},
}
result, err := s.preTradeCheck(ctx, now, "uid", domain.Portfolio{
Equity: decimal.NewFromInt(10000),
Cash: decimal.NewFromInt(10000),
}, 0, false, domain.TradingStatusNormal, now)
if err != nil {
t.Fatalf("err=%v, want reject without hard halt error", err)
}
if result.Allowed || result.Reason != "server_time_unavailable" {
t.Fatalf("result=%+v, want server_time_unavailable reject", result)
}
if repo.Halted || repo.HaltReason != "" {
t.Fatalf("halted=%v reason=%q, want no halt", repo.Halted, repo.HaltReason)
}
if notifier.alerts != 0 {
t.Fatalf("alerts=%d, want 0", notifier.alerts)
}
}
func TestPreTradeUsesPhaseDeadlineForMinTimeToClose(t *testing.T) {
ctx := context.Background()
repo := testutil.NewMemoryRepository()
+4
View File
@@ -43,6 +43,7 @@ type FakeGateway struct {
Portfolio domain.Portfolio
Operations []domain.Operation
ServerTime time.Time
ServerTimeError error
}
func NewFakeGateway() *FakeGateway {
@@ -239,6 +240,9 @@ func (f *FakeGateway) GetOperations(_ context.Context, _ string, from, to time.T
func (f *FakeGateway) GetServerTime(context.Context) (time.Time, error) {
f.mu.Lock()
defer f.mu.Unlock()
if f.ServerTimeError != nil {
return time.Time{}, f.ServerTimeError
}
if f.ServerTime.IsZero() {
return time.Now().UTC(), nil
}