Fix category mapping and TotalTrades drift, plan traits/tiering (Teil D)

- MarketCategoryMapper: classify from question text (the Gamma /markets
  endpoint delivers neither category nor event tags, so on-demand markets
  had no signal at all), match short tokens on word boundaries ("eth" no
  longer hits inside "whether", "pop" not inside "popular"), widen the
  keyword lists across all categories.
- UpdateMarketFields: never overwrite a tag-derived category with an
  uninformative "Other" from the on-demand path.
- PositionPnLEngine: sync Trader.TotalTrades to the actual replayed row
  count — the worker-side increment counters drift (INSERT IGNORE,
  deletions, historic imports) and produced Trades30d > TotalTrades.
- Tests: 14 new (mapper classification + word-boundary regression,
  TotalTrades sync + Trades30d invariant, category update guard via
  SQLite) — suite now 32 green + 1 skip.
- FIXPLAN Teil D for the larger rebuilds (AggregatedCount column,
  TraderTraits heuristics, IngestMode tiering for ultra-HF traders).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-07-11 14:09:12 +02:00
co-authored by Claude Fable 5
parent 940a99fec2
commit d2f3ec2bd0
9 changed files with 391 additions and 195 deletions
@@ -0,0 +1,55 @@
using Predictalytics.Domain.Enums;
using Predictalytics.Infrastructure.Helpers;
using Xunit;
namespace Predictalytics.Application.Tests.Services;
/// <summary>
/// Tests for the keyword-based market classification (added 2026-07-11).
/// The Gamma /markets endpoint carries neither a category field nor event tags,
/// so for on-demand fetched markets the QUESTION TEXT is often the only signal —
/// the mapper must classify from it. Short tokens must match on word boundaries
/// ("eth" must not hit inside "whether", "pop" not inside "popular").
/// </summary>
public class MarketCategoryMapperTests
{
[Theory]
// Question-text-only classification (the on-demand path has no tags):
[InlineData("", "", "Will the Lakers win the NBA Finals?", MarketCategory.Sports)]
[InlineData("", "", "Ethereum above $1,850 on July 10?", MarketCategory.Crypto)]
[InlineData("", "", "Will Trump win the 2028 presidential election?", MarketCategory.Politics)]
[InlineData("", "", "Fed rate cut in September?", MarketCategory.Economy)]
[InlineData("", "", "Will SpaceX launch Starship this quarter?", MarketCategory.Science)]
[InlineData("", "", "Russia-Ukraine ceasefire before August?", MarketCategory.GlobalNews)]
[InlineData("", "", "New Rihanna album before GTA VI?", MarketCategory.PopCulture)]
// Tag-based classification still works:
[InlineData("", "Sports, NBA", "", MarketCategory.Sports)]
[InlineData("", "Crypto", "", MarketCategory.Crypto)]
// No signal at all → Other:
[InlineData("", "", "Something entirely unclassifiable happens?", MarketCategory.Other)]
public void Map_ClassifiesFromAvailableSignals(string rawCategory, string tags, string question, MarketCategory expected)
{
var (category, _) = MarketCategoryMapper.Map(rawCategory, tags, question);
Assert.Equal(expected, category);
}
[Fact]
public void Map_ShortTokens_RequireWordBoundaries()
{
// "whether" contains the substring "eth" — must NOT classify as Crypto.
var (category, _) = MarketCategoryMapper.Map("", "", "Whether the government acts by Friday?");
Assert.NotEqual(MarketCategory.Crypto, category);
// "popular vote" contains the substring "pop" — must be Politics ("vote"),
// not PopCulture.
var (category2, _) = MarketCategoryMapper.Map("", "", "Popular vote winner in Michigan?");
Assert.Equal(MarketCategory.Politics, category2);
}
[Fact]
public void Map_Subcategory_PrefersFirstTagOverFallback()
{
var (_, subcategory) = MarketCategoryMapper.Map("", "NBA, Basketball", "Lakers to win?");
Assert.Equal("NBA", subcategory);
}
}
@@ -0,0 +1,93 @@
using System;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Predictalytics.Domain.Entities;
using Predictalytics.Domain.Enums;
using Predictalytics.Infrastructure.Data;
using Predictalytics.Infrastructure.Data.Repositories;
using Xunit;
namespace Predictalytics.Application.Tests.Services;
/// <summary>
/// Tests for the market upsert paths (added 2026-07-11).
/// Uses SQLite in-memory (same pattern as TradeRetentionWorkerTests).
/// </summary>
public class MarketRepositoryTests
{
/// <summary>
/// The on-demand /markets fetch carries no event tags and classifies markets
/// as "Other". Such an uninformative update must NOT overwrite a category that
/// was previously derived from the tag-bearing /events sync — while a
/// legitimate reclassification (Other → Sports) must still go through.
/// </summary>
[Fact]
public async Task AddOrUpdateAsync_DoesNotOverwriteGoodCategoryWithOther()
{
using var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlite(connection)
.Options;
using (var setup = new AppDbContext(options))
{
setup.Database.EnsureCreated();
var ev = new Event { Id = 1, Platform = PlatformType.Polymarket, Slug = "e", Title = "E" };
setup.Set<Event>().Add(ev);
setup.Markets.Add(new Market
{
Id = 10, EventId = 1, Platform = PlatformType.Polymarket,
ConditionId = "0xabc", PlatformMarketId = 1L, Question = "Lakers to win?",
Category = MarketCategory.Sports, Subcategory = "NBA"
});
setup.SaveChanges();
}
// Act 1: uninformative on-demand update (no tags → Other) must not downgrade.
using (var ctx = new AppDbContext(options))
{
var repo = new MarketRepository(ctx);
await repo.AddOrUpdateAsync(new Market
{
Platform = PlatformType.Polymarket, ConditionId = "0xabc",
PlatformMarketId = 1L, Question = "Lakers to win?",
Category = MarketCategory.Other, Subcategory = "Other"
});
}
using (var assertCtx = new AppDbContext(options))
{
var market = await assertCtx.Markets.SingleAsync(m => m.ConditionId == "0xabc");
Assert.Equal(MarketCategory.Sports, market.Category);
Assert.Equal("NBA", market.Subcategory);
}
// Act 2: a real classification must still overwrite an existing "Other".
using (var ctx = new AppDbContext(options))
{
var repo = new MarketRepository(ctx);
await repo.AddOrUpdateAsync(new Market
{
Platform = PlatformType.Polymarket, ConditionId = "0xdef",
PlatformMarketId = 2L, Question = "Unknown thing?",
Category = MarketCategory.Other, Subcategory = "Other",
Event = new Event { Platform = PlatformType.Polymarket, PlatformEventId = 2, Slug = "e2", Title = "E2" }
});
await repo.AddOrUpdateAsync(new Market
{
Platform = PlatformType.Polymarket, ConditionId = "0xdef",
PlatformMarketId = 2L, Question = "Unknown thing?",
Category = MarketCategory.Politics, Subcategory = "Elections"
});
}
using (var assertCtx = new AppDbContext(options))
{
var market = await assertCtx.Markets.SingleAsync(m => m.ConditionId == "0xdef");
Assert.Equal(MarketCategory.Politics, market.Category);
Assert.Equal("Elections", market.Subcategory);
}
}
}
@@ -778,4 +778,59 @@ public class PositionPnLEngineTests
Assert.Equal(30m, analytics.OverallPnL);
}
}
/// <summary>
/// Added 2026-07-11: Trader.TotalTrades was a drifting increment counter
/// (INSERT IGNORE over-counts, historic imports never counted, deletions never
/// subtracted), which produced impossible states like Trades30d > TotalTrades.
/// The engine recalculation is the single source of truth: it must sync
/// TotalTrades to the actual number of trade rows it just replayed.
/// </summary>
[Fact]
public async Task RecalculateTraderPositionsAsync_SyncsTotalTradesWithActualTradeCount()
{
// Arrange: counter is wildly wrong (both directions occur in production).
var dbName = Guid.NewGuid().ToString();
using (var db = CreateDbContext(dbName))
{
var trader = new Trader { Id = 1, PlatformUserId = "0x1", DisplayName = "Trader 1", TotalTrades = 999 };
var market = new Market { Id = 10, PlatformMarketId = 1L, Question = "Q?" };
market.Outcomes.Add(new MarketOutcome { Id = 100, MarketId = 10, Label = "Yes", TokenId = "t100", CurrentPrice = 0.50m });
db.Traders.Add(trader);
db.Markets.Add(market);
db.Trades.Add(new Trade
{
Id = 10, TraderId = 1, DbMarketId = 10, MarketOutcomeId = 100,
Side = TradeSide.Buy, Price = 0.40m, Size = 100m, Amount = 40m,
ExecutedAt = DateTime.UtcNow.AddDays(-2)
});
db.Trades.Add(new Trade
{
Id = 11, TraderId = 1, DbMarketId = 10, MarketOutcomeId = 100,
Side = TradeSide.Sell, Price = 0.50m, Size = 50m, Amount = 25m,
ExecutedAt = DateTime.UtcNow.AddDays(-1)
});
await db.SaveChangesAsync();
}
// Act
using (var db = CreateDbContext(dbName))
{
var pnlEngine = new PositionPnLEngine(db, NullLogger<PositionPnLEngine>.Instance);
await pnlEngine.RecalculateTraderPositionsAsync(1);
}
// Assert: counter equals the real row count, and the windowed count can
// never exceed it again.
using (var db = CreateDbContext(dbName))
{
var trader = await db.Traders.SingleAsync(t => t.Id == 1);
Assert.Equal(2, trader.TotalTrades);
var analytics = await db.TraderAnalytics.SingleAsync(a => a.TraderId == 1);
Assert.True(analytics.Trades30d <= trader.TotalTrades,
$"Trades30d ({analytics.Trades30d}) must never exceed TotalTrades ({trader.TotalTrades})");
}
}
}
@@ -192,8 +192,15 @@ public class MarketRepository : IMarketRepository
existing.PlatformMarketId = updated.PlatformMarketId;
existing.QuestionId = updated.QuestionId;
existing.Description = updated.Description;
existing.Category = updated.Category;
existing.Subcategory = updated.Subcategory;
// The /markets endpoint (on-demand path) carries no event tags, so its
// classification is often just "Other". Never let an uninformative update
// overwrite a category previously derived from the tag-bearing /events sync.
if (updated.Category != MarketCategory.Other || existing.Category == MarketCategory.Other)
{
existing.Category = updated.Category;
existing.Subcategory = updated.Subcategory;
}
existing.Volume = updated.Volume;
existing.Volume24h = updated.Volume24h;
existing.Liquidity = updated.Liquidity;
@@ -1,33 +1,77 @@
using System.Text.RegularExpressions;
using Predictalytics.Domain.Enums;
namespace Predictalytics.Infrastructure.Helpers;
public static class MarketCategoryMapper
{
public static (MarketCategory Category, string Subcategory) Map(string rawCategory, string tags)
/// <summary>
/// Classification rules, first match wins. Two matching modes per category:
/// - Substrings: long, unambiguous fragments matched anywhere ("politic", "bitcoin").
/// - Words: short/ambiguous tokens matched only on word boundaries, so "eth"
/// cannot hit inside "whether" and "pop" cannot hit inside "popular".
/// </summary>
private static readonly (MarketCategory Category, string[] Substrings, string[] Words)[] Rules =
{
var searchString = $"{rawCategory} {tags}".ToLowerInvariant();
(MarketCategory.Politics,
new[] { "politic", "election", "president", "senat", "congress", "parliament", "impeach",
"referendum", "governor", "minister", "chancellor", "nominee", "supreme court",
"white house", "ballot", "veto", "coalition", "legislation", "mayor" },
new[] { "trump", "biden", "harris", "vance", "scotus", "gop", "dnc", "rnc", "poll", "polls", "vote", "votes" }),
if (searchString.Contains("politic") || searchString.Contains("election") || searchString.Contains("trump") || searchString.Contains("biden"))
return (MarketCategory.Politics, GetSubcategory(rawCategory, tags, "Elections"));
if (searchString.Contains("crypto") || searchString.Contains("bitcoin") || searchString.Contains("eth") || searchString.Contains("solana"))
return (MarketCategory.Crypto, GetSubcategory(rawCategory, tags, "Crypto"));
if (searchString.Contains("sport") || searchString.Contains("nfl") || searchString.Contains("nba") || searchString.Contains("soccer") || searchString.Contains("tennis"))
return (MarketCategory.Sports, GetSubcategory(rawCategory, tags, "Sports"));
if (searchString.Contains("pop") || searchString.Contains("culture") || searchString.Contains("movie") || searchString.Contains("oscars") || searchString.Contains("music"))
return (MarketCategory.PopCulture, GetSubcategory(rawCategory, tags, "Pop Culture"));
if (searchString.Contains("science") || searchString.Contains("space") || searchString.Contains("weather") || searchString.Contains("climate"))
return (MarketCategory.Science, GetSubcategory(rawCategory, tags, "Science"));
if (searchString.Contains("news") || searchString.Contains("global") || searchString.Contains("world"))
return (MarketCategory.GlobalNews, GetSubcategory(rawCategory, tags, "Global News"));
(MarketCategory.Crypto,
new[] { "crypto", "bitcoin", "ethereum", "solana", "dogecoin", "blockchain", "stablecoin",
"binance", "coinbase", "airdrop", "halving", "memecoin", "altcoin", "defi", "satoshi" },
new[] { "btc", "eth", "sol", "xrp", "doge", "nft", "bnb", "ada", "usdt", "usdc" }),
if (searchString.Contains("economy") || searchString.Contains("finance") || searchString.Contains("business") || searchString.Contains("fed"))
return (MarketCategory.Economy, GetSubcategory(rawCategory, tags, "Economy"));
(MarketCategory.Sports,
new[] { "sport", "soccer", "tennis", "basketball", "baseball", "football", "hockey", "olympic",
"champions league", "premier league", "bundesliga", "la liga", "serie a", "world cup",
"super bowl", "grand slam", "wimbledon", "playoff", "esport", "cricket", "rugby",
"golf", "boxing", "marathon", "formula 1", "grand prix", "stanley cup", "world series",
"roland garros", "us open" },
new[] { "nfl", "nba", "mlb", "nhl", "ufc", "mma", "f1", "fifa", "uefa", "atp", "wta", "pga", "ncaa", "epl" }),
(MarketCategory.PopCulture,
new[] { "culture", "movie", "oscars", "music", "album", "billboard", "box office", "netflix",
"spotify", "grammy", "emmy", "celebrit", "taylor swift", "mrbeast", "youtube", "tiktok",
"video game", "rotten tomatoes", "eurovision" },
new[] { "pop", "gta", "oscar" }),
(MarketCategory.Science,
new[] { "science", "spacex", "nasa", "asteroid", "hurricane", "earthquake", "climate", "weather",
"temperature", "vaccine", "pandemic", "artificial intelligence", "openai", "chatgpt",
"quantum", "starship", "nobel", "space" },
new[] { "ai", "agi", "gpt", "llm" }),
(MarketCategory.GlobalNews,
new[] { "global", "world", "news", "ceasefire", "ukraine", "russia", "israel", "gaza", "iran",
"taiwan", "nato", "sanction", "treaty", "invasion", "north korea", "hostage",
"military", "missile", "nuclear" },
new[] { "war", "u.n." }),
(MarketCategory.Economy,
new[] { "econom", "finance", "business", "inflation", "recession", "interest rate", "rate cut",
"rate hike", "unemployment", "tariff", "treasury", "earnings", "market cap",
"stock price", "bankrupt", "acquisition", "merger" },
new[] { "fed", "fomc", "cpi", "gdp", "nasdaq", "dow", "ipo" }),
};
/// <summary>
/// Classifies a market. The question text is a first-class signal: the Gamma
/// /markets endpoint delivers neither a category field nor event tags, so for
/// on-demand fetched markets the question is often the ONLY signal available.
/// </summary>
public static (MarketCategory Category, string Subcategory) Map(string rawCategory, string tags, string question = "")
{
var searchString = $"{rawCategory} {tags} {question}".ToLowerInvariant();
var tokens = new HashSet<string>(Regex.Split(searchString, "[^a-z0-9.]+"));
foreach (var rule in Rules)
{
if (rule.Words.Any(tokens.Contains) || rule.Substrings.Any(searchString.Contains))
return (rule.Category, GetSubcategory(rawCategory, tags, rule.Category.ToString()));
}
return (MarketCategory.Other, GetSubcategory(rawCategory, tags, "Other"));
}
@@ -35,12 +79,12 @@ public static class MarketCategoryMapper
private static string GetSubcategory(string rawCategory, string tags, string fallback)
{
if (!string.IsNullOrWhiteSpace(rawCategory) && !rawCategory.Equals("OVERALL", StringComparison.OrdinalIgnoreCase))
return rawCategory;
return rawCategory.Trim();
var firstTag = tags.Split(',', StringSplitOptions.RemoveEmptyEntries).FirstOrDefault()?.Trim();
if (!string.IsNullOrWhiteSpace(firstTag))
return firstTag;
return fallback;
}
}
@@ -318,7 +318,7 @@ public class PolymarketProvider : IPlatformProvider
{
long.TryParse(raw.Id, out var marketNumericId);
var catMap = Predictalytics.Infrastructure.Helpers.MarketCategoryMapper.Map(raw.Category ?? "", parentTags);
var catMap = Predictalytics.Infrastructure.Helpers.MarketCategoryMapper.Map(raw.Category ?? "", parentTags, raw.Question ?? "");
var market = new Market
{
@@ -367,6 +367,11 @@ public class PositionPnLEngine : IPositionPnLEngine
// Sync back to Trader record for quick sorting / UI display
trader.TotalPnl = overallPnl;
trader.WinRate = winRateOverall;
// Row count is the single source of truth for TotalTrades — the worker-side
// increment counters drift (INSERT IGNORE, deletions, historic imports) and
// produced impossible states like Trades30d > TotalTrades. Compacted rows
// count as 1 until an AggregatedCount column exists (FIXPLAN Teil D).
trader.TotalTrades = trades.Count;
if (trades.Count > 0 || trader.LastTradesUpdatedAt != null)
{
trader.LastAnalyzedAt = DateTime.UtcNow;
+15 -168
View File
@@ -23,162 +23,7 @@ partial class MainForm
statusStrip1 = new StatusStrip();
label_apiRatelimit = new ToolStripStatusLabel();
label_buildVersion = new ToolStripStatusLabel();
tabControl1 = new TabControl();
tabPage_terminal = new TabPage();
rtb_terminal = new RichTextBox();
tabPage2 = new TabPage();
pg_settings = new PropertyGrid();
menuStrip1 = new MenuStrip();
filesToolStripMenuItem = new ToolStripMenuItem();
editToolStripMenuItem = new ToolStripMenuItem();
btn_logfolder = new ToolStripMenuItem();
btn_openbrowser = new ToolStripMenuItem();
developmentToolStripMenuItem = new ToolStripMenuItem();
btn_dbReset = new ToolStripMenuItem();
btn_syncmarkets = new ToolStripMenuItem();
btn_dbUpdate = new ToolStripMenuItem();
btn_recalcAll = new ToolStripMenuItem();
toolStrip1.SuspendLayout();
statusStrip1.SuspendLayout();
tabControl1.SuspendLayout();
tabPage_terminal.SuspendLayout();
tabPage2.SuspendLayout();
menuStrip1.SuspendLayout();
SuspendLayout();
//
// toolStrip1
//
toolStrip1.ImageScalingSize = new Size(24, 24);
toolStrip1.Items.AddRange(new ToolStripItem[] { btn_serverstart, btn_localWebserver });
toolStrip1.Location = new Point(0, 33);
toolStrip1.Name = "toolStrip1";
toolStrip1.Size = new Size(1864, 34);
toolStrip1.TabIndex = 0;
//
// btn_serverstart
//
btn_serverstart.ImageTransparentColor = Color.Magenta;
btn_serverstart.Name = "btn_serverstart";
btn_serverstart.Size = new Size(127, 29);
btn_serverstart.Text = "▶ Start Server";
//
// btn_localWebserver
//
btn_localWebserver.ImageTransparentColor = Color.Magenta;
btn_localWebserver.Name = "btn_localWebserver";
btn_localWebserver.Size = new Size(161, 29);
btn_localWebserver.Text = "▶ Start Webserver";
//
// statusStrip1
//
statusStrip1.ImageScalingSize = new Size(24, 24);
statusStrip1.Items.AddRange(new ToolStripItem[] { label_apiRatelimit, label_buildVersion });
statusStrip1.Location = new Point(0, 1000);
statusStrip1.Name = "statusStrip1";
statusStrip1.Size = new Size(1864, 32);
statusStrip1.TabIndex = 1;
//
// label_apiRatelimit
//
label_apiRatelimit.Name = "label_apiRatelimit";
label_apiRatelimit.Size = new Size(1782, 25);
label_apiRatelimit.Spring = true;
label_apiRatelimit.Text = "API: OK";
label_apiRatelimit.TextAlign = ContentAlignment.MiddleLeft;
//
// label_buildVersion
//
label_buildVersion.Name = "label_buildVersion";
label_buildVersion.Size = new Size(67, 25);
label_buildVersion.Text = "Build: -";
label_buildVersion.TextAlign = ContentAlignment.MiddleRight;
//
// tabControl1
//
tabControl1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
tabControl1.Controls.Add(tabPage_terminal);
tabControl1.Controls.Add(tabPage2);
tabControl1.Location = new Point(0, 61);
tabControl1.Name = "tabControl1";
tabControl1.SelectedIndex = 0;
tabControl1.Size = new Size(1864, 946);
tabControl1.TabIndex = 2;
//
// tabPage_terminal
//
tabPage_terminal.Controls.Add(rtb_terminal);
tabPage_terminal.Location = new Point(4, 34);
tabPage_terminal.Name = "tabPage_terminal";
tabPage_terminal.Padding = new Padding(3);
tabPage_terminal.Size = new Size(1856, 908);
tabPage_terminal.TabIndex = 0;
tabPage_terminal.Text = "Terminal";
tabPage_terminal.UseVisualStyleBackColor = true;
//
// rtb_terminal
//
rtb_terminal.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
rtb_terminal.Location = new Point(3, 6);
rtb_terminal.Name = "rtb_terminal";
rtb_terminal.Size = new Size(1847, 896);
rtb_terminal.TabIndex = 0;
rtb_terminal.Text = "";
//
// tabPage2
//
tabPage2.Controls.Add(pg_settings);
tabPage2.Location = new Point(4, 34);
tabPage2.Name = "tabPage2";
tabPage2.Padding = new Padding(3);
tabPage2.Size = new Size(1856, 908);
tabPage2.TabIndex = 1;
tabPage2.Text = "Settings";
tabPage2.UseVisualStyleBackColor = true;
//
// pg_settings
//
pg_settings.Location = new Point(3, 6);
pg_settings.Name = "pg_settings";
pg_settings.Size = new Size(1850, 896);
pg_settings.TabIndex = 0;
//
// menuStrip1
//
menuStrip1.ImageScalingSize = new Size(24, 24);
menuStrip1.Items.AddRange(new ToolStripItem[] { filesToolStripMenuItem, editToolStripMenuItem, developmentToolStripMenuItem });
menuStrip1.Location = new Point(0, 0);
menuStrip1.Name = "menuStrip1";
menuStrip1.Size = new Size(1864, 33);
menuStrip1.TabIndex = 3;
//
// filesToolStripMenuItem
//
filesToolStripMenuItem.Name = "filesToolStripMenuItem";
filesToolStripMenuItem.Size = new Size(62, 29);
filesToolStripMenuItem.Text = "Files";
//
// editToolStripMenuItem
//
editToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { btn_logfolder, btn_openbrowser });
editToolStripMenuItem.Name = "editToolStripMenuItem";
editToolStripMenuItem.Size = new Size(58, 29);
editToolStripMenuItem.Text = "Edit";
//
// btn_logfolder
//
btn_logfolder.Name = "btn_logfolder";
btn_logfolder.Size = new Size(261, 34);
btn_logfolder.Text = "Show Logfolder";
btn_logfolder.Click += btn_logfolder_Click;
//
// btn_openbrowser
toolStrip1 = new ToolStrip();
btn_serverstart = new ToolStripButton();
btn_localWebserver = new ToolStripButton();
statusStrip1 = new StatusStrip();
label_apiRatelimit = new ToolStripStatusLabel();
label_dbSize = new ToolStripStatusLabel();
label_buildVersion = new ToolStripStatusLabel();
tabControl1 = new TabControl();
tabPage_terminal = new TabPage();
rtb_terminal = new RichTextBox();
@@ -242,13 +87,6 @@ partial class MainForm
label_apiRatelimit.Text = "API: OK";
label_apiRatelimit.TextAlign = ContentAlignment.MiddleLeft;
//
// label_dbSize
//
label_dbSize.Name = "label_dbSize";
label_dbSize.Size = new Size(150, 25);
label_dbSize.Text = "DB Size: -";
label_dbSize.TextAlign = ContentAlignment.MiddleRight;
//
// label_buildVersion
//
label_buildVersion.Name = "label_buildVersion";
@@ -256,6 +94,13 @@ partial class MainForm
label_buildVersion.Text = "Build: -";
label_buildVersion.TextAlign = ContentAlignment.MiddleRight;
//
// label_dbSize
//
label_dbSize.Name = "label_dbSize";
label_dbSize.Size = new Size(150, 25);
label_dbSize.Text = "DB Size: -";
label_dbSize.TextAlign = ContentAlignment.MiddleRight;
//
// tabControl1
//
tabControl1.Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
@@ -351,27 +196,27 @@ partial class MainForm
// btn_dbReset
//
btn_dbReset.Name = "btn_dbReset";
btn_dbReset.Size = new Size(270, 34);
btn_dbReset.Size = new Size(286, 34);
btn_dbReset.Text = "reset TradesDB";
//
// btn_syncmarkets
//
btn_syncmarkets.Name = "btn_syncmarkets";
btn_syncmarkets.Size = new Size(270, 34);
btn_syncmarkets.Size = new Size(286, 34);
btn_syncmarkets.Text = "Sync Markets";
btn_syncmarkets.Click += syncMarketsaToolStripMenuItem_Click;
//
// btn_dbUpdate
//
btn_dbUpdate.Name = "btn_dbUpdate";
btn_dbUpdate.Size = new Size(270, 34);
btn_dbUpdate.Size = new Size(286, 34);
btn_dbUpdate.Text = "UpdateDB";
btn_dbUpdate.Click += btn_dbUpdate_Click;
//
//
// btn_recalcAll
//
//
btn_recalcAll.Name = "btn_recalcAll";
btn_recalcAll.Size = new Size(270, 34);
btn_recalcAll.Size = new Size(286, 34);
btn_recalcAll.Text = "Recalculate All Traders";
btn_recalcAll.Click += btn_recalcAll_Click;
//
@@ -385,6 +230,8 @@ partial class MainForm
Controls.Add(toolStrip1);
Controls.Add(menuStrip1);
MainMenuStrip = menuStrip1;
MaximumSize = new Size(1886, 1088);
MinimumSize = new Size(1886, 1088);
Name = "MainForm";
Text = "Predictalytics";
toolStrip1.ResumeLayout(false);