Repository navigation
Expand file tree
/
Copy pathProgram.cs
More file actions
174 lines (157 loc) · 7.91 KB
/
Copy pathProgram.cs
File metadata and controls
174 lines (157 loc) · 7.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using MetaRPC.CSharpMT4;
using mt4_term_api;
using MetaRPC.CSharpMT4.Examples.LowLevel;
using MetaRPC.CSharpMT4.Examples.Services;
using MetaRPC.CSharpMT4.Examples.Sugar;
using MetaRPC.CSharpMT4.Examples.Orchestrators;
using MetaRPC.CSharpMT4.Examples.Presets;
using MetaRPC.CSharpMT4.Examples.UserCode;
using MetaRPC.CSharpMT4.Examples.Tools;
using static MetaRPC.CSharpMT4.ConsoleUi;
using MetaRPC.CSharpMT4.Helpers;
static class Program
{
static async Task<int> Main(string[] args)
{
// Ctrl+C → graceful cancellation
var appToken = Shutdown.HookCtrlC();
// Protobuf inspector is reflection-only — run it without connecting.
if (args.Contains("--inspect")) { ProtobufInspector.Run(); return 0; }
string? apiKeyCli = null;
for (int i = 0; i < args.Length; i++)
{
if (args[i] == "--api-key" && i + 1 < args.Length)
{
apiKeyCli = args[i + 1];
break;
}
else if (args[i].StartsWith("--api-key="))
{
apiKeyCli = args[i].Substring("--api-key=".Length);
break;
}
}
if (apiKeyCli == null && args.Length > 0 && !args[0].StartsWith("-"))
{
apiKeyCli = args[0];
}
// ---------------------------------------------------------------------
// CONFIG + LOGGER
// ---------------------------------------------------------------------
ConnectionHelper.ApiKeyOverride = apiKeyCli;
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.AddSimpleConsole(o => { o.SingleLine = true; o.TimestampFormat = "HH:mm:ss "; })
.SetMinimumLevel(LogLevel.Information);
});
var logger = loggerFactory.CreateLogger<MT4Service>();
// Load options (appsettings/env), resolve the API key and auto-provision a demo account if needed.
var mt4 = await ConnectionHelper.LoadOptionsAsync(logger, appToken);
var err = mt4.ValidateOrError();
if (err.Length > 0) { Console.Error.WriteLine(err); return 2; }
if (string.IsNullOrWhiteSpace(mt4.Grpc)) { Console.Error.WriteLine("Invalid Grpc endpoint"); return 2; }
Console.WriteLine($"CFG → user={mt4.User}, server={mt4.ServerName}, host={(mt4.Host ?? "(null)")}:{(mt4.Port?.ToString() ?? "(null)")}, grpc={mt4.Grpc}, symbol={mt4.Symbol}, apiKey={(mt4.ApiKey == "TRIAL" ? "TRIAL" : "***")}, timeout={mt4.TimeoutSeconds}s, retries={mt4.ConnectRetries}");
MT4Account account;
Box("CONNECT");
try
{
account = await ConnectionHelper.CreateAndConnectAccountAsync(mt4, logger, appToken);
}
catch (Exception ex)
{
Console.Error.WriteLine($"Connect failed: {ex.Message}");
return 3;
}
try
{
// ---------------------------------------------------------------------
// MAIN CALLS (PRIMARY DEMO SEQUENCE)
// ---------------------------------------------------------------------
// Keep this list compact and obvious — this is what reviewers will run.
var svc = new MT4Service(account);
var sym = mt4.Symbol ?? "EURUSD";
// ---------------------------------------------------------------------
// EXAMPLE MENU. Each entry calls a data-returning MT4Service method and
// prints the result here (the Service no longer prints). Grouped like the docs.
// Selection: --example <n|substring>, --all, or an interactive menu.
// Non-interactive stdin (CI / piped) runs the whole suite so nothing hangs.
// ---------------------------------------------------------------------
var examples = new List<(string Name, Func<Task> Run)>
{
// ===== 3-tier suites (run a whole level) =====
("== 1. LOW-LEVEL: info ==", () => LowLevelInfo.RunAsync(account, sym, appToken)),
("== 2. LOW-LEVEL: trading ==", () => LowLevelTrading.RunAsync(account, sym, false, appToken)),
("== 3. LOW-LEVEL: streaming ==", () => LowLevelStreaming.RunAsync(account, sym, appToken)),
("== 4. SERVICE suite (all) ==", () => ServiceExamples.RunAllAsync(svc, sym, appToken)),
("== 5. SUGAR scenario (assess->plan->execute) ==", () => SugarScenario.RunAsync(svc, sym, false, appToken)),
("== 6. ORCHESTRATOR: scalp (described) ==", () => new ScalpOrchestrator(svc) { Symbol = sym, Live = false }.ExecuteAsync(appToken)),
("== 7. ORCHESTRATOR: trailing stop (described) ==", () => new TrailingStopOrchestrator(svc) { Symbol = sym, Live = false }.ExecuteAsync(appToken)),
("== 8. ORCHESTRATOR: grid (described) ==", () => new GridOrchestrator(svc) { Symbol = sym, Live = false }.ExecuteAsync(appToken)),
("== 9. ORCHESTRATOR: breakout (described) ==", () => new BreakoutOrchestrator(svc) { Symbol = sym, Live = false }.ExecuteAsync(appToken)),
("== 10. PRESET: adaptive mode (described) ==", () => new AdaptiveModePreset(svc) { Symbol = sym, Live = false }.ExecuteAsync(appToken)),
("== 11. USER CODE sandbox ==", () => ProgramUserCode.RunAsync(svc, sym, appToken)),
("== 12. PROTOBUF inspector (interactive) ==", () => { ProtobufInspector.Run(); return Task.CompletedTask; }),
};
async Task RunOneAsync(string name, Func<Task> run)
{
Box(name);
try { await run(); }
catch (Exception ex) { logger.LogWarning(ex, "Example '{Name}' failed", name); }
}
async Task RunAllAsync()
{
foreach (var (name, run) in examples) await RunOneAsync(name, run);
}
string? exSel = null;
var runAll = false;
foreach (var a in args)
{
if (a == "--all") runAll = true;
else if (a.StartsWith("--example=")) exSel = a.Substring("--example=".Length);
}
if (!string.IsNullOrEmpty(exSel))
{
var hit = examples.FirstOrDefault(e => e.Name.IndexOf(exSel, StringComparison.OrdinalIgnoreCase) >= 0);
if (int.TryParse(exSel, out var idx) && idx >= 1 && idx <= examples.Count) hit = examples[idx - 1];
if (hit.Run == null) { Console.Error.WriteLine($"No example matches '{exSel}'."); return 2; }
await RunOneAsync(hit.Name, hit.Run);
}
else if (runAll || Console.IsInputRedirected)
{
await RunAllAsync();
}
else
{
while (!appToken.IsCancellationRequested)
{
Console.WriteLine();
Console.WriteLine("════════════════ MT4 EXAMPLES (Service) ════════════════");
for (int i = 0; i < examples.Count; i++)
Console.WriteLine($" {i + 1,2}. {examples[i].Name}");
Console.WriteLine(" a. Run ALL");
Console.WriteLine(" q. Quit");
Console.Write("Select: ");
var input = Console.ReadLine()?.Trim();
if (string.IsNullOrEmpty(input) || input.Equals("q", StringComparison.OrdinalIgnoreCase)) break;
if (input.Equals("a", StringComparison.OrdinalIgnoreCase)) { await RunAllAsync(); continue; }
if (int.TryParse(input, out var n) && n >= 1 && n <= examples.Count)
await RunOneAsync(examples[n - 1].Name, examples[n - 1].Run);
else
Console.WriteLine($"Unknown selection: '{input}'");
}
}
return 0;
}
finally
{
await ConnectionHelper.DisconnectAsync(account);
}
}
}